Tracing all functions in my program

白昼怎懂夜的黑 提交于 2020-05-17 06:19:08

问题


Like the title says, I want to trace ALL functions calls in my application (from inside).

I tried using "_penter" but I get either a recursion limit reached error or an access violation when I try to prevent the recursion.

Is there any way to achieve this ?

Update

What I tried:

extern "C"
{
    void __declspec(naked) _cdecl _penter()
    {
        _asm {
            push    eax
            push    ecx
            push    edx
            mov     ecx, [esp + 0Ch]
            push    ecx
            mov     ecx, offset Context::Instance
            call    Context::addFrame
            pop     edx
            pop     ecx
            pop     eax
            ret
        }
}

class Context
{
 public:
    __forceinline void addFrame(const void* addr) throw() {}

    static thread_local Context Instance;
};

sadly this still gives a stack overflow due to recursion


回答1:


but I get either a recursion limit reached error

this can be if inside Context::addFrame implementation compiler also insert call _penter which recursive call Context::addFrame.

but how __forceinline you can ask ? nothing. c/c++ compiler to insert a copy of the function body into each place the function is called from code which is generated by this compiler. c/c++ compiler can not insert a copy of the function body into code, which he not compile itself. so when we call function marked as __forceinline from assembler code - function will be called in usual way but not expanded in place. so your __forceinline simply have no effect and sense

you need implement Context::addFrame (and all functions which it call) in separate c++ file (let be context.cpp) compiled without /Gh option.

you can set /Gh for all files in project, except context.cpp

if exist too many cpp files in project - you can set /Gh for project, but how then remove it for single file context.cpp ? exist one original way - you can copy <cmdline> for this file and that set custom build tool for it Command Line- CL.exe <cmdline> $(InputFileName) (not forget remove /Gh) and Outputs - $(IntDir)\$(InputName).obj. original by perfect work.

so in context.cpp you can have next code:

class Context
{
public:
    void __fastcall addFrame(const void* addr);

    int _n;

    static thread_local Context Instance;
};

thread_local Context Context::Instance;

void __fastcall Context::addFrame(const void* addr)
{
#pragma message(__FUNCDNAME__)

    DbgPrint("%p>%u\n", addr, _n++);
}

if Context::addFrame call some another internal function (explicit or implicit) - put it also in this file, which compile without /Gh

the _penter better implement in separate asm file, but not as inline asm (this not supported in x64 anyway)

so for x86 you can create code32.asm ( ml /c /Cp $(InputFileName) -> $(InputName).obj)

.686p

.MODEL flat

extern ?addFrame@Context@@QAIXPBX@Z:proc
extern ?Instance@Context@@2V12@A:byte

_TEXT segment 'CODE'

__penter proc
    push edx
    push ecx
    mov edx,[esp+8]
    lea ecx,?Instance@Context@@2V12@A
    call ?addFrame@Context@@QAIXPBX@Z
    pop ecx
    pop edx
    ret
__penter endp

_TEXT ends
end

note - you need save only rcx and rdx (if you use __fastcall , except context.cpp, functions)

for x64 - create code64.asm ( ml64 /c /Cp $(InputFileName) -> $(InputName).obj)

extern ?addFrame@Context@@QEAAXPEBX@Z:proc
extern ?Instance@Context@@2V12@A:byte

_TEXT segment 'CODE'

_penter proc
    mov [rsp+8],rcx
    mov [rsp+16],rdx
    mov [rsp+24],r8
    mov [rsp+32],r9
    mov rdx,[rsp]
    sub rsp,28h
    lea rcx,?Instance@Context@@2V12@A
    call ?addFrame@Context@@QEAAXPEBX@Z
    add rsp,28h
    mov r9,[rsp+32]
    mov r8,[rsp+24]
    mov rdx,[rsp+16]
    mov rcx,[rsp+8]
    ret
_penter endp

_TEXT ENDS
end



回答2:


Your approach is correct, /Gh and /GH compiler switches + _penter and _pexit functions is the way to go.

I think there’re errors in your implementation of these functions. That’s very low-level stuff, for 32 bit builds you have to use __declspec(naked), and for 64 bit builds you have to use assembler. Both are quite tricky to implement correctly.

Take a look at this repository for an example how to do it right: https://github.com/tyoma/micro-profiler Specifically, to this source file: https://github.com/tyoma/micro-profiler/blob/master/micro-profiler/collector/hooks.asm As you see, they decided to use assembler for both platforms, and from that they call some C++ function to record call information. Also note how in C++ collector implementation they use __forceinline to avoid recursion.



来源:https://stackoverflow.com/questions/48477998/tracing-all-functions-in-my-program

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!