好的,关于SO的第二个问题在一天之内。看起来像Windows编程让我开心......:S
我正在尝试在Win32可执行文件上获取函数调用堆栈。
今天早上,我也问了一个问题:
现在,我很确定StackWalk64
函数是关键。
我已经阅读了一些关于如何使用它的文章,以及MS文档。
它实际上在我的测试程序中显示帧,所以它有点工作......
问题是我无法从堆栈信息中检索符号名称。
我正在使用SymGetSymFromAddr64
函数,UnDecorateSymbolName
。但我只收到垃圾字符。
这是我的代码。希望它不要乱,因为我不熟悉Windows编程:
void printStack( void )
{
BOOL result;
HANDLE process;
HANDLE thread;
CONTEXT context;
STACKFRAME64 stack;
ULONG frame;
IMAGEHLP_SYMBOL64 symbol;
DWORD64 displacement;
char name[ 256 ];
RtlCaptureContext( &context );
memset( &stack, 0, sizeof( STACKFRAME64 ) );
process = GetCurrentProcess();
thread = GetCurrentThread();
displacement = 0;
stack.AddrPC.Offset = context.Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context.Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context.Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
for( frame = 0; ; frame++ )
{
result = StackWalk64
(
IMAGE_FILE_MACHINE_I386,
process,
thread,
&stack,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
);
symbol.SizeOfStruct = sizeof( IMAGEHLP_SYMBOL64 );
symbol.MaxNameLength = 255;
SymGetSymFromAddr64( process, ( ULONG64 )stack.AddrPC.Offset, &displacement, &symbol );
UnDecorateSymbolName( symbol.Name, ( PSTR )name, 256, UNDNAME_COMPLETE );
printf
(
"Frame %lu:\n"
" Symbol name: %s\n"
" PC address: 0x%08LX\n"
" Stack address: 0x%08LX\n"
" Frame address: 0x%08LX\n"
"\n",
frame,
symbol.Name,
( ULONG64 )stack.AddrPC.Offset,
( ULONG64 )stack.AddrStack.Offset,
( ULONG64 )stack.AddrFrame.Offset
);
if( !result )
{
break;
}
}
}
实际输出是:
Frame 0:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠
PC address: 0x00BA2763
Stack address: 0x00000000
Frame address: 0x0031F7E8
Frame 1:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠☺
PC address: 0x00BB4FFF
Stack address: 0x00000000
Frame address: 0x0031F940
Frame 2:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠☻
PC address: 0x00BB4E2F
Stack address: 0x00000000
Frame address: 0x0031F990
Frame 3:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♥
PC address: 0x75BE3677
Stack address: 0x00000000
Frame address: 0x0031F998
Frame 4:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♦
PC address: 0x770F9D72
Stack address: 0x00000000
Frame address: 0x0031F9A4
Frame 5:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♣
PC address: 0x770F9D45
Stack address: 0x00000000
Frame address: 0x0031F9E4
Frame 6:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♠
PC address: 0x770F9D45
Stack address: 0x00000000
Frame address: 0x0031F9E4
似乎奇怪的是,堆栈地址总是为0 ...任何帮助赞赏:)
感谢大家!
修改
我正在寻找一个简单的C解决方案,没有第三方库......
答案 0 :(得分:6)
您已将symbol.MaxNameLength
设置为255,但您在堆栈上使用IMAGEHLP_SYMBOL64 symbol;
分配了“符号”。该类型定义为:
typedef struct _IMAGEHLP_SYMBOL64 {
DWORD SizeOfStruct;
DWORD64 Address;
DWORD Size;
DWORD Flags;
DWORD MaxNameLength;
TCHAR Name[1];
} IMAGEHLP_SYMBOL64;
请注意,Name字段默认只有一个字符。如果要存储更大的名称,则需要执行以下操作:
const int MaxNameLen = 255;
IMAGEHLP_SYMBOL64* pSymbol =
malloc(sizeof(IMAGEHLP_SYMBOL64)+MaxNameLen*sizeof(TCHAR));
pSymbol->MaxNameLength = MaxNameLen;
否则,SymGetSymFromAddr64()
可能会覆盖内存。以下是结构的the help page(强调添加):
MaxNameLength :最大长度 Name成员可以使用的字符串 包含字符,不包括 空终止字符。 因为符号名称可能不同 长度,这个数据结构是 由来电者分配 。这个成员 使用,所以图书馆知道多少 内存可供使用 符号名称。
答案 1 :(得分:5)
查看Stackwalker project on codeplex - 它是开源的。很好地工作。
答案 2 :(得分:2)
我使用了你的代码并且它起初也没有用,直到我在文档中注意到你首先需要调用SymInitialize,比如SymInitialize(process,NULL,TRUE)。你可以在RtlCaptureContext之前调用它。
答案 3 :(得分:0)
首先要解决两个问题:
1)如AShelly所指出的,名称需要预先分配。你不需要malloc来做它:
#define MY_MAX_SYM_LEN 255
printStack()
{
struct sym_pack_tag {
IMAGEHLP_SYMBOL64 sym;
char name[MY_MAX_SYM_LEN];
} sym_pack;
IMAGEHLP_SYMBOL64 *symbol = &sym_pack.sym;
...
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64 );
symbol->MaxNameLength = MY_MAX_SYM_LEN;
if (!SymGetSymFromAddr64( process, stack.AddrPC.Offset, &displacement, symbol )) ...
2)使用RtlCaptureContext()来获取32位构建中的上下文是不行的。如果您有64位计算机,则将IMAGE_FILE_MACHINE_I386更改为适当的64位类型。如果您有32位版本,则使用内联汇编来正确设置EBP,ESP和EIP。这是一种方法:
__declspec(naked) void WINAPI CaptureContext_X86ControlOnly(CONTEXT *context) {
__asm {
push ebp
mov ebp, esp
mov ecx, context //ecx = [ebp + 8]
pop ebp //restore old frame
pop eax //pop return address
pop ecx //pop context as WINAPI needs. Note: ecx will stay the same
mov [ecx]CONTEXT.ContextFlags, CONTEXT_CONTROL
mov [ecx]CONTEXT.Ebp, ebp
mov [ecx]CONTEXT.Eip, eax
mov [ecx]CONTEXT.Esp, esp
jmp eax
}
} //I'm writing from my memory - so step through the code above to double check.
小点 - SymGetSymFromAddr64没问题,但建议改用SymFromAddr。
祝所有Windows上的跟踪堆栈好运。
答案 4 :(得分:0)
查看基本相同问题的答案:
https://stackoverflow.com/a/28276227/10592
请注意,您需要确保您的用户拥有.pdb文件,并且他们的进程可以找到它 - 请参阅该答案以获取更多详细信息。