在发布版本中获取调用堆栈时出现问题
我一直在努力获取 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);
}
我的问题是它们在未优化的构建中工作,但没有完全优化。发生的情况是,我得到一个损坏的条目,然后它们退出循环。在调试中,我获得了完整的调用堆栈,当我稍后查找符号时,一切都是正确的。
我不明白当调试器一直这样做时,在所有版本中都很难做到这一点。我可以明确地说,代码生成中没有省略帧指针。我首先构建调试,然后仅将优化从无更改为完全优化,然后重建以重现调用堆栈失败。
任何有关解决方案的提示将不胜感激。
/乔纳斯
I have been struggling to get a call stack in a Windows executable. I have tried several different ways to obtain the call stack. The following are some examples. Note that I modified them slightly and removed error handling to make them easy to understand so they may not compile as is. I think you get the point.
The simple way:
const int max_entries = 10;
void *entries[max_entries];
return CaptureStackBackTrace(0, max_entries, entries, 0);
The low level way:
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];
}
The compatible way:
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);
}
My problem is that they work in an unoptimized build, but not with full optimization on. What happens is that I get one broken entry and then they exits their loops. In debug I get the full call stack and when I later look up the symbols, it is all correct.
I don't understand how it can be hard to make this work in all builds when the debugger does it all the time. I can specifically say that the frame pointers are not omitted in the code generation. I build for debug first and then only change the optimization from none to full optimization and rebuild to reproduce the call stack failure.
Any hints to a solution will be greatly appreciated.
/Jonas
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我现在使用“兼容方式”来完成这项工作。我使用以下代码来初始化上下文:
然后像以前一样继续使用 StackWalk64 获取堆栈。
我注意到我忘记在将 CONTEXT 结构发送到 RtlCaptureContext 之前清除它,所以我尝试这样做(因为我更喜欢使用 RtlCaptureContext 函数)。
现在 RtlCaptureContext 崩溃了,所以我又重新使用 GET_CURRENT_CONTEXT 宏。
I got this working using the "compatible way" now. I use the following code to initialize the context:
and then continue to fetch the stack using StackWalk64 as before.
I noticed that I forgot to clear the CONTEXT structure before sending it to RtlCaptureContext so I tried to do it like this (because I would prefer to use the RtlCaptureContext function).
Now RtlCaptureContext crashes, so I went back to using the GET_CURRENT_CONTEXT macro.
这不是一个答案,而只是一个“我也是”报告,用于澄清特定版本:
我在
RtlCaptureContext
内看到零星崩溃,但仅限于 32 位调试版本,而不是 32 位发布版本,并且不适用于 Debug 或 Release 64 位版本。我正在使用 VS2008 SP1 和 2011 年 4 月 25 日下载的 Windows 调试工具中的dbghelp.dll
文件版本 6.12.2.633,并且在调用之前将 dbghelp.dll 复制到与我的 EXE 相同的目录中。这是在同一台 Windows XP 64 位 SP2 计算机上使用完全相同版本的 VS2008 SP1 编译器进行编译(同时编译 32 位和 64 位本机应用程序,混合中根本没有 .NET 托管代码)。
上面的关键是它的零星性质。我还没有确定它崩溃的条件。
This is not an answer, but just a "me too" report to clarify specific versions:
I'm seeing sporadic crashes inside
RtlCaptureContext
but only on 32-bit Debug builds and not on 32-bit Release builds, and not on either Debug or Release 64-bit builds. I'm using VS2008 SP1 withdbghelp.dll
fileVersion 6.12.2.633 from Debugging Tools for Windows downloaded April 25, 2011, and that dbghelp.dll copied into the same directory as my EXE prior to invocation.This is with compiling using the exact same release of VS2008 SP1 compiler on the same Windows XP 64-bit SP2 machine (compiling both 32-bit and 64-bit native apps, no .NET managed code at all is in the mix).
The key above is it's sporadic nature. I have not determined the conditions by which it crashes.