程序崩溃时,输出调用堆栈以及符号名称

时间:2019-02-24 03:38:00

标签: c++

我想在遇到未处理的异常并且程序崩溃时输出调用堆栈。我想在程序还活着的情况下执行此操作,而无需任何事后分析。

我宁愿不使用任何第三方库,这是大多数类似问题的答案所建议的。我正在尝试在此处使用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之类的东西。

2 个答案:

答案 0 :(得分:2)

我解决了。我已经看到很多人和我有同样的问题。放在正确的上下文中!

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

答案 1 :(得分:0)

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

在实践中,C ++实现遵循As-if rule。他们可以并且经常使用optimizeinlining函数,甚至您没有使用inline进行注释的函数。发生这种情况时,为内联函数调用框架是没有意义的。

还要注意, some automatic variables实际上不在调用堆栈中。允许编译器(并且通常这样做)仅将某些变量放在processor registers中,而不会将它们溢出到调用堆栈中。了解有关register allocation的信息。您调用框架中的给定插槽可用于保留几个不相关的变量。

因此,显示调用栈是实现质量的问题,并且可能取决于外部因素(例如,编译C ++代码时所需的优化级别)。在Linux上,我建议使用Ian Taylor libbacktrace。我猜您可以在Windows上找到类似的库来进行调用堆栈检查。