跟踪我程序中的所有功能

时间:2018-01-27 16:40:18

标签: c++ windows visual-c++

就像标题所说,我想跟踪我的应用程序中的所有函数调用(从内部)。

我尝试使用" _penter"但是当我试图阻止递归时,我得到了一个递归限制达到错误或访问冲突。

有没有办法实现这个目标?

更新

我尝试了什么:

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;
};
遗憾的是,由于递归,这仍然会导致堆栈溢出

2 个答案:

答案 0 :(得分:2)

你的方法是正确的,/ Gh和/ GH编译器开关+ _penter和_pexit函数是要走的路。

我认为这些功能的实施存在错误。这是非常低级的东西,对于32位构建,你必须使用add,对于64位构建,你必须使用汇编程序。两者都非常难以正确实施。

请查看此存储库以获取如何正确执行此操作的示例: https://github.com/tyoma/micro-profiler具体来说,对于这个源文件:https://github.com/tyoma/micro-profiler/blob/master/micro-profiler/collector/hooks.asm如你所见,他们决定在两个平台上使用汇编程序,并从中调用一些C ++函数来记录调用信息。还要注意在C ++收集器实现中如何使用column来避免递归。

答案 1 :(得分:2)

  

但是我得到了一个递归限制错误

这可能是在Context::addFrame内部实现编译器还插入了递归调用_penter的调用Context::addFrame

但你可以问__forceinline怎么样?没有。 c / c ++ 编译器将函数体的副本插入到从编译器生成的代码中调用函数的每个位置。 c / c ++ 编译器无法将函数体的副本插入代码中,而不能自行编译。所以当我们从汇编代码中调用标记为__forceinline的函数时 - 函数将以通常的方式调用,但不会在适当的位置进行扩展。所以你的__forceinline根本没有效果和感觉

你需要在单独的 c ++ 文件中实现Context :: addFrame(以及它调用的所有函数)(让我们 context.cpp )编译没有 /Gh选项。

您可以为项目中的所有文件设置/Gh,但 context.cpp

除外

如果项目中存在太多 cpp 文件 - 您可以为项目设置/Gh,但是如何为单个文件 context.cpp 删除它?存在一种原始方式 - 您可以为此文件复制 <cmdline> 并为其设置自定义构建工具 命令行 - CL.exe <cmdline> $(InputFileName)(不要忘记删除/Gh)和输出 - $(IntDir)\$(InputName).obj。完美的工作原创。

所以在 context.cpp 中你可以拥有下一个代码:

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++);
}

如果Context::addFrame调用另一个内部函数(显式或隐式) - 也将它放在这个没有/Gh编译的文件中

_penter更好地在单独的asm文件中实现,但不能作为内联asm(在x64中不支持)

因此,对于x86,您可以创建 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

注意 - 您只需要保存 rcx rdx (如果使用__fastcall,则除了context.cpp,函数)

for x64 - 创建 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