当程序与符号名称一起崩溃时,输出呼叫堆栈

Output the call stack when the program crashes along with the symbol names

本文关键字:输出 呼叫 堆栈 崩溃 一起 程序 符号      更新时间:2023-10-16

我想在捕获未手动异常并且程序崩溃时输出呼叫堆栈。我想在程序还活着的情况下,没有任何验尸分析。

我宁愿不使用任何第三方库,这是对类似问题的大多数答案。我正在尝试在这里使用stackwalk。

我试图让它在Windows上工作。

这就是我所拥有的:

DWORD machine = IMAGE_FILE_MACHINE_I386;
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
CONTEXT context = {};
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
SymInitialize(process, NULL, TRUE);
SymSetOptions(SYMOPT_LOAD_LINES);
STACKFRAME frame = {};
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
while (StackWalk(machine, process, thread, &frame, &context, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
{
    char * functionName;
    char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255];
    PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
    symbol->SizeOfStruct = (sizeof IMAGEHLP_SYMBOL) + 255;
    symbol->MaxNameLength = 254;
    if (SymGetSymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
    {
        functionName = symbol->Name;
        std::string str(functionName);
        std::wstring stemp = std::wstring(str.begin(), str.end());
        LPCWSTR sw = stemp.c_str();
        MessageBox(NULL, sw, L"Error", MB_ICONERROR | MB_OK); //for testing purposes
        if (str.find("nvd3dum") != std::string::npos) {
            //I'd put a messagebox here telling the user to do something if I find a symbol name I recognize
       }
  }
}

我遇到的问题是,我没有在程序崩溃时输出呼叫堆栈,而是得到了我在此非常函数中使用的rtlcapturecontext之类的功能。

<</p>

我解决了它。我见过很多人和我有同样的问题。将其置于正确的上下文中!

CONTEXT context = {};
context.ContextFlags = exceptionInfo->ContextRecord->ContextFlags;
context.Eip = exceptionInfo->ContextRecord->Eip;
context.Ebp = exceptionInfo->ContextRecord->Ebp;
context.Esp = exceptionInfo->ContextRecord->Esp;

原则上,呼叫堆栈不需要在C 中存在。仔细阅读C 11标准N3337,那里没有提及呼叫堆栈。因此,从理论上讲,某些C 编译器可以足够聪明,可以避免任何呼叫堆栈(对于该编译器提供的某些特定程序)。许多C 编译器正在优化尾声(因此在调用函数和其呼叫框架之间共享相同的内存位置)。

实际上,C 实现遵循AS-IF规则。他们可以并且经常可以优化到内部函数的点,即使您没有使用inline注释的功能。发生这种情况时,谈到嵌入函数的呼叫框架是没有道理的。

还要注意一些自动变量实际上不在呼叫堆栈上。允许编译器(并且通常)仅将某些变量放入处理器寄存器中,而不会将它们溢出到呼叫堆栈中。阅读有关注册分配的信息。您的呼叫框架中的一个给定插槽可用于保持几个无关的变量。

因此,显示呼叫堆栈是一个实施质量问题,可能取决于 external 因素(例如,在编译C 代码时所需的优化级别)。在Linux上,我建议使用Ian Taylor Libbacktrace。我想您可以在Windows上找到一个类似的库进行调用堆栈检查。