在发布版本中获取调用堆栈时出现问题

发布于 2024-10-17 02:49:00 字数 1638 浏览 1 评论 0原文

我一直在努力获取 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小镇女孩 2024-10-24 02:49:00

我现在使用“兼容方式”来完成这项工作。我使用以下代码来初始化上下文:

#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);
}

我注意到我忘记在将 CONTEXT 结构发送到 RtlCaptureContext 之前清除它,所以我尝试这样做(因为我更喜欢使用 RtlCaptureContext 函数)。

CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);

现在 RtlCaptureContext 崩溃了,所以我又重新使用 GET_CURRENT_CONTEXT 宏。

I got this working using the "compatible way" now. I use the following code to initialize the context:

#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);

and then continue to fetch the stack using StackWalk64 as before.

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);
}

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).

CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);

Now RtlCaptureContext crashes, so I went back to using the GET_CURRENT_CONTEXT macro.

吻风 2024-10-24 02:49:00

这不是一个答案,而只是一个“我也是”报告,用于澄清特定版本:

我在 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 with dbghelp.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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文