From 03ba95e7e1e8f8c49e8afc831472c41b07a4a9bf Mon Sep 17 00:00:00 2001 From: dpjudas Date: Sat, 26 Apr 2025 13:32:01 +0200 Subject: [PATCH] Improve call stack by using the CONTEXT record from the SEH exception --- src/common/platform/win32/i_crash.cpp | 32 ++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/common/platform/win32/i_crash.cpp b/src/common/platform/win32/i_crash.cpp index 50d2eee32..d074874dc 100644 --- a/src/common/platform/win32/i_crash.cpp +++ b/src/common/platform/win32/i_crash.cpp @@ -85,7 +85,7 @@ private: static void on_se_unhandled_exception(unsigned int exception_code, PEXCEPTION_POINTERS exception_pointers); static LONG WINAPI on_win32_unhandled_exception(PEXCEPTION_POINTERS exception_pointers); - static int CaptureStackTrace(int max_frames, void** out_frames); + static int CaptureStackTrace(PCONTEXT context, int max_frames, void** out_frames); typedef BOOL(WINAPI* MiniDumpWriteDumpPointer)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, CONST PMINIDUMP_EXCEPTION_INFORMATION, CONST PMINIDUMP_USER_STREAM_INFORMATION, CONST PMINIDUMP_CALLBACK_INFORMATION); HMODULE module_dbghlp; @@ -291,7 +291,7 @@ void CrashReporter::on_sigabort(int) invoke(); } -int CrashReporter::CaptureStackTrace(int max_frames, void** out_frames) +int CrashReporter::CaptureStackTrace(PCONTEXT initcontext, int max_frames, void** out_frames) { memset(out_frames, 0, sizeof(void*) * max_frames); @@ -299,7 +299,10 @@ int CrashReporter::CaptureStackTrace(int max_frames, void** out_frames) // RtlCaptureStackBackTrace doesn't support RtlAddFunctionTable.. CONTEXT context; - RtlCaptureContext(&context); + if (initcontext) + memcpy(&context, initcontext, sizeof(CONTEXT)); + else + RtlCaptureContext(&context); UNWIND_HISTORY_TABLE history; memset(&history, 0, sizeof(UNWIND_HISTORY_TABLE)); @@ -307,8 +310,14 @@ int CrashReporter::CaptureStackTrace(int max_frames, void** out_frames) ULONG64 establisherframe = 0; PVOID handlerdata = nullptr; - int frame; - for (frame = 0; frame < max_frames; frame++) + int frame = 0; + + // If the context came from an exception then we want its location included too + // Otherwise first frame is the call to this function which we never want to see + if (initcontext && frame < max_frames) + out_frames[frame++] = (void*)context.Rip; + + while (frame < max_frames) { ULONG64 imagebase; PRUNTIME_FUNCTION rtfunc = RtlLookupFunctionEntry(context.Rip, &imagebase, &history); @@ -330,14 +339,11 @@ int CrashReporter::CaptureStackTrace(int max_frames, void** out_frames) break; out_frames[frame] = (void*)context.Rip; + frame++; } return frame; - -#elif defined(WIN32) - // JIT isn't supported here, so just do nothing. - return 0;//return RtlCaptureStackBackTrace(0, min(max_frames, 32), out_frames, nullptr); #else - return backtrace(out_frames, max_frames); + return 0; #endif } @@ -352,7 +358,7 @@ void CrashReporter::on_se_unhandled_exception(unsigned int exception_code, PEXCE dump_params.thread_id = GetCurrentThreadId(); dump_params.exception_pointers = exception_pointers; dump_params.exception_code = exception_code; - dump_params.stack_frames.frame_count = CaptureStackTrace(StackFrameList::max_frames, dump_params.stack_frames.frames); + dump_params.stack_frames.frame_count = CaptureStackTrace(exception_pointers->ContextRecord, StackFrameList::max_frames, dump_params.stack_frames.frames); // Ensure we only get a dump of the first thread crashing - let other threads block here. std::unique_lock mutex_lock(mutex); @@ -373,7 +379,7 @@ LONG CrashReporter::on_win32_unhandled_exception(PEXCEPTION_POINTERS exception_p dump_params.thread_id = GetCurrentThreadId(); dump_params.exception_pointers = exception_pointers; dump_params.exception_code = 0; - dump_params.stack_frames.frame_count = CaptureStackTrace(StackFrameList::max_frames, dump_params.stack_frames.frames); + dump_params.stack_frames.frame_count = CaptureStackTrace(exception_pointers->ContextRecord, StackFrameList::max_frames, dump_params.stack_frames.frames); // Ensure we only get a dump of the first thread crashing - let other threads block here. std::unique_lock mutex_lock(mutex); @@ -1004,7 +1010,7 @@ void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FStr FString lastExternalCode; const StackFrameList& callstack = debugger.GetStackFrameList(); - for (int i = 1; i < callstack.frame_count; i++) + for (int i = 0; i < callstack.frame_count; i++) { FString text = debugger.GetCalledFromText(callstack.frames[i]); if (!text.IsEmpty())