2015-01-22 2 views
3

У меня есть приложение Windows/C++ (с использованием JUCE), и я хочу сбросить трассировку стека в файл, когда приложение выйдет из строя. В моем коде инициализации, у меня есть:windows/C++: как я могу получить полезную трассировку стека из обработчика сигнала?

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

Есть ли способ получить трассировку стека для потока, где произошел сбой (или все потоки)?

+1

https://msdn.microsoft.com/en-us/library/windows/desktop/ms680634%28v=vs.85%29.aspx –

+0

@HansPassant, тот же результат я боюсь. – Taylor

+0

Не происходит в x64. Я не уверен, связано ли это с тем, что x86 использует исключения на основе стека, а не таблицы, или потому что WOW64 запутывает вещи. (У меня нет 32-битной машины, чтобы ее можно было попробовать.) –

ответ

7

Мое решение было, как это было предложено @HansPassant, использовать:

SetUnhandledExceptionFilter(TopLevelExceptionHandler); 

И в TopLevelExceptionHandler, вместо вызова CaptureStackBackTrace, я использую StackWalk64, который позволяет определить, какой стек ходить (в отличие от просто предполагая текущий стек).

Вот код:

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; 
} 
+0

Имея ту же проблему здесь. Интересно, почему вы передаете GetCurrentThread() в качестве третьего аргумента в StackWalk64()? Разве мы не должны передавать дескриптор потока, который мы рассматриваем, не так ли? Если да, то как получить дескриптор разбитой нити? –

 Смежные вопросы

  • Нет связанных вопросов^_^