就像标题所说,我想跟踪我的应用程序中的所有函数调用(从内部)。
我尝试使用" _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;
};
遗憾的是,由于递归,这仍然会导致堆栈溢出
答案 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