Windows/C++:如何从信号处理程序中获取有用的堆栈跟踪

windows/c++: how can I get a useful stack trace from a signal handler?

本文关键字:获取 有用 跟踪 堆栈 程序 信号处理 C++ Windows      更新时间:2023-10-16

我有一个Windows/C++应用程序(使用JUCE),我想在应用程序崩溃时将堆栈跟踪转储到文件中。在我的初始化代码中,我得到了:

signal(SIGABRT, abortHandler);
signal(SIGSEGV, abortHandler);
signal(SIGILL, abortHandler);
signal(SIGFPE, abortHandler);

然后我的处理程序看起来像:

void abortHandler(int signum)
{
    juce::File log("stacktrace.txt");
    log.appendText(juce::SystemStats::getStackBacktrace());
    exit(signum);
}

但是,生成的堆栈跟踪不是发生崩溃的线程:

0: AudulusDebug32: juce::SystemStats::getStackBacktrace + 0x7f
1: AudulusDebug32: abortHandler + 0x61
2: AudulusDebug32: _XcptFilter + 0x1e3
3: AudulusDebug32: __tmainCRTStartup + 0x15f
4: AudulusDebug32: WinMainCRTStartup + 0xd
5: BaseThreadInitThunk + 0xe
6: RtlInitializeExceptionChain + 0x84
7: RtlInitializeExceptionChain + 0x5a

在内部,getStackBacktrace执行以下操作:

    HANDLE process = GetCurrentProcess();
    SymInitialize (process, nullptr, TRUE);
    void* stack[128];
    int frames = (int) CaptureStackBackTrace (0, numElementsInArray (stack), stack, nullptr);

有没有办法获取发生崩溃的线程(或所有线程)的堆栈跟踪?

正如@HansPassant所建议的那样,我的解决方案是使用:

SetUnhandledExceptionFilter(TopLevelExceptionHandler);

TopLevelExceptionHandler中,我使用 StackWalk64 而不是调用 CaptureStackBackTrace ,它允许您指定要遍历的堆栈(而不是仅仅假设当前堆栈)。

这是代码:

LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    std::ofstream f;
    f.open("stacktrace.txt", std::ios::out | std::ios::trunc);
    HANDLE process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    // StackWalk64() may modify context record passed to it, so we will
    // use a copy.
    CONTEXT context_record = *pExceptionInfo->ContextRecord;
    // Initialize stack walking.
    STACKFRAME64 stack_frame;
    memset(&stack_frame, 0, sizeof(stack_frame));
    #if defined(_WIN64)
    int machine_type = IMAGE_FILE_MACHINE_AMD64;
    stack_frame.AddrPC.Offset = context_record.Rip;
    stack_frame.AddrFrame.Offset = context_record.Rbp;
    stack_frame.AddrStack.Offset = context_record.Rsp;
    #else
    int machine_type = IMAGE_FILE_MACHINE_I386;
    stack_frame.AddrPC.Offset = context_record.Eip;
    stack_frame.AddrFrame.Offset = context_record.Ebp;
    stack_frame.AddrStack.Offset = context_record.Esp;
    #endif
    stack_frame.AddrPC.Mode = AddrModeFlat;
    stack_frame.AddrFrame.Mode = AddrModeFlat;
    stack_frame.AddrStack.Mode = AddrModeFlat;
    juce::HeapBlock<SYMBOL_INFO> symbol;
    symbol.calloc(sizeof(SYMBOL_INFO) + 256, 1);
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    while (StackWalk64(machine_type,
        GetCurrentProcess(),
        GetCurrentThread(),
        &stack_frame,
        &context_record,
        NULL,
        &SymFunctionTableAccess64,
        &SymGetModuleBase64,
        NULL)) {
        DWORD64 displacement = 0;
        if (SymFromAddr(process, (DWORD64)stack_frame.AddrPC.Offset, &displacement, symbol))
        {
            IMAGEHLP_MODULE64 moduleInfo;
            juce::zerostruct(moduleInfo);
            moduleInfo.SizeOfStruct = sizeof(moduleInfo);
            if (::SymGetModuleInfo64(process, symbol->ModBase, &moduleInfo))
                f << moduleInfo.ModuleName << ": ";
            f << symbol->Name << " + 0x" << String::toHexString((juce::int64)displacement) << std::endl;
        }
    }
    return EXCEPTION_CONTINUE_SEARCH;
}