[英]Tracing all functions in my program
正如标题所说,我想跟踪我的应用程序中的所有函数调用(从内部)。
我尝试使用“_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;
};
遗憾的是,由于递归,这仍然会导致堆栈溢出
您的方法是正确的, /Gh 和 /GH 编译器开关 + _penter 和 _pexit 函数是要走的路。
我认为您在实现这些功能时存在错误。 这是非常低级的东西,对于 32 位构建,您必须使用__declspec(naked)
,对于 64 位构建,您必须使用汇编程序。 两者都很难正确实施。
看看这个存储库的一个例子,如何正确地做到这一点: https : //github.com/tyoma/micro-profiler具体来说,这个源文件: https : //github.com/tyoma/micro-profiler/blob /master/micro-profiler/collector/hooks.asm如您所见,他们决定在两个平台上都使用汇编程序,并从中调用一些 C++ 函数来记录调用信息。 还要注意在 C++ 收集器实现中他们如何使用__forceinline
来避免递归。
但我得到一个递归限制达到错误
这可以是如果在Context::addFrame
实现编译器中也插入 call _penter
,它递归调用Context::addFrame
。
但是__forceinline
你怎么能问? 没什么。 c/c++编译器将函数体的副本插入到从该编译器生成的代码调用函数的每个位置。 c/c++编译器不能将函数体的副本插入到代码中,而他自己不编译。 因此,当我们从汇编代码中调用标记为__forceinline
- 函数将以通常的方式调用,但不会就地展开。 所以你的__forceinline
根本没有效果和意义
您需要在没有/Gh
选项的情况下在单独的C++文件(假设是context.cpp )中实现 Context::addFrame (以及它调用的所有函数)。
您可以为项目中的所有文件设置/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 函数)
对于 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
这是我使用的
Configuration Properties > C/C++ > Command Line
将编译器选项添加到Additional Options
框
为 _penter 钩子添加标志 /Gh
为 _pexit 钩子添加标志 /GH
我用于跟踪/记录的代码
#include <intrin.h>
extern "C" void __declspec(naked) __cdecl _penter(void) {
__asm {
push ebp; // standard prolog
mov ebp, esp;
sub esp, __LOCAL_SIZE
pushad; // save registers
}
// _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
PBYTE addr;
addr = (PBYTE)_ReturnAddress() - 5;
SYMBOL_INFO* mysymbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
mysymbol->MaxNameLen = 255;
mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
myprintf("Entered Function: %s [0x%X]\n", mysymbol->Name, addr);
_asm {
popad; // restore regs
mov esp, ebp; // standard epilog
pop ebp;
ret;
}
}
extern "C" void __declspec(naked) __cdecl _pexit(void) {
__asm {
push ebp; // standard prolog
mov ebp, esp;
sub esp, __LOCAL_SIZE
pushad; // save registers
}
// _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
PBYTE addr;
addr = (PBYTE)_ReturnAddress() - 5;
SYMBOL_INFO* mysymbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
mysymbol->MaxNameLen = 255;
mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
myprintf("Exit Function: %s [0x%X]\n", mysymbol->Name, addr);
_asm {
popad; // restore regs
mov esp, ebp; // standard epilog
pop ebp;
ret;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.