我一直在努力在Windows可执行文件中获取调用堆栈。我已经尝试了几种不同的方法来获取调用堆栈。以下是一些例子。请注意,我稍微修改了它们并删除了错误处理以使它们易于理解,因此它们可能无法按原样编译。我想你明白了。
简单的方法:
const int max_entries = 10;
void *entries[max_entries];
return CaptureStackBackTrace(0, max_entries, entries, 0);
低级方式:
const int max_entries = 10;
void *entries[max_entries];
void **frame = 0;
__asm { mov frame, ebp }
unsigned int i = 0;
while(frame && i < max_entries) {
entries[i++] = frame[1];
frame = (void **)frame[0];
}
兼容方式:
void *entries[max_entries];
CONTEXT context;
RtlCaptureContext(&context);
STACKFRAME64 stack_frame;
ZeroMemory(&stack_frame, sizeof(STACKFRAME64));
stack_frame.AddrPC.Offset = context.Eip;
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = context.Ebp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = context.Esp;
stack_frame.AddrStack.Mode = AddrModeFlat;
unsigned int num_frames = 0;
while (true) {
if (!StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(),
GetCurrentThread(), &stack_frame, &context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL))
break;
if (stack_frame.AddrPC.Offset == 0)
break;
entries[num_frames++] = reinterpret_cast<void *>(stack_frame.AddrPC.Offset);
}
我的问题是他们在未经优化的构建中工作,但没有完全优化。会发生什么事情,我得到一个破碎的条目,然后他们退出他们的循环。在调试中,我得到完整的调用堆栈,当我稍后查找符号时,它都是正确的。
我不明白当调试器一直这样做时,如何在所有版本中使这项工作变得困难。我可以具体说在代码生成中不会省略帧指针。我首先构建调试,然后只将优化从none更改为完全优化,并重建以重现调用堆栈失败。
非常感谢任何有关解决方案的提示。
/纳斯
答案 0 :(得分:3)
我现在正在使用“兼容方式”。我使用以下代码初始化上下文:
#define GET_CURRENT_CONTEXT(c, contextFlags) \
do { \
memset(&c, 0, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
__asm call x \
__asm x: pop eax \
__asm mov c.Eip, eax \
__asm mov c.Ebp, ebp \
__asm mov c.Esp, esp \
} while(0);
CONTEXT context;
GET_CURRENT_CONTEXT(context, CONTEXT_FULL);
然后继续像以前一样使用StackWalk64获取堆栈。
void *entries[max_entries];
STACKFRAME64 stack_frame;
ZeroMemory(&stack_frame, sizeof(STACKFRAME64));
stack_frame.AddrPC.Offset = context.Eip;
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = context.Ebp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = context.Esp;
stack_frame.AddrStack.Mode = AddrModeFlat;
unsigned int num_frames = 0;
while (true) {
if (!StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(),
GetCurrentThread(), &stack_frame, &context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL))
break;
if (stack_frame.AddrPC.Offset == 0)
break;
entries[num_frames++] = reinterpret_cast<void *>(stack_frame.AddrPC.Offset);
}
我注意到在将其发送到RtlCaptureContext之前忘记清除CONTEXT结构,所以我尝试这样做(因为我更喜欢使用RtlCaptureContext函数)。
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
现在RtlCaptureContext崩溃了,所以我回到了使用GET_CURRENT_CONTEXT宏。
答案 1 :(得分:0)
这不是一个答案,只是一个“我也是”报告来澄清具体版本:
我看到RtlCaptureContext
内部偶然发生崩溃,但只发生在32位Debug版本而不是32位Release版本上,而不是在Debug或Release 64位版本上。我在2011年4月25日下载的Debugging Tools for Windows中使用VS2008 SP1和dbghelp.dll
fileVersion 6.12.2.633,并且在调用之前将dbghelp.dll复制到与我的EXE相同的目录中。
这是在同一台Windows XP 64位SP2计算机上使用完全相同的VS2008 SP1编译器进行编译(编译32位和64位本机应用程序,根本没有.NET托管代码) )。
上面的关键是零星的性质。我还没有确定它崩溃的条件。