Windows / C ++: как я могу получить полезную трассировку стека от обработчика сигнала?

У меня есть приложение для 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);

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

3

Решение

Мое решение было, как предложил @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;
}
7

Другие решения


По вопросам рекламы [email protected]