SDL: sdlprocdump: store context and exception record in minidump

From be43dec79848e5dc16ec0767ee691eb62b0819b4 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Fri, 5 Jul 2024 21:06:05 +0200
Subject: [PATCH] sdlprocdump: store context and exception record in minidump

---
 test/win32/sdlprocdump.c | 82 +++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 35 deletions(-)

diff --git a/test/win32/sdlprocdump.c b/test/win32/sdlprocdump.c
index c23a4625e3a9e..7034297bee43c 100644
--- a/test/win32/sdlprocdump.c
+++ b/test/win32/sdlprocdump.c
@@ -181,10 +181,11 @@ static const char *get_simple_basename(const char *path) {
     return path;
 }
 
-static void write_minidump(const char *child_file_path, const LPPROCESS_INFORMATION process_information, DWORD dwThreadId) {
+static void write_minidump(const char *child_file_path, const LPPROCESS_INFORMATION process_information, DWORD dwThreadId, PEXCEPTION_RECORD exception_record, PCONTEXT context) {
     BOOL success;
     char dump_file_path[MAX_PATH];
     char child_file_name[64];
+    EXCEPTION_POINTERS exception_pointers;
     HANDLE hFile = INVALID_HANDLE_VALUE;
     HMODULE dbghelp_module = NULL;
     MINIDUMP_EXCEPTION_INFORMATION minidump_exception_information;
@@ -221,8 +222,11 @@ static void write_minidump(const char *child_file_path, const LPPROCESS_INFORMAT
         printf_windows_message("Failed to open file for minidump");
         goto post_dump;
     }
+    memset(&exception_pointers, 0, sizeof(exception_pointers));
+    exception_pointers.ContextRecord = context;
+    exception_pointers.ExceptionRecord = exception_record;
     minidump_exception_information.ClientPointers = FALSE;
-    minidump_exception_information.ExceptionPointers = FALSE;
+    minidump_exception_information.ExceptionPointers = &exception_pointers;
     minidump_exception_information.ThreadId = dwThreadId;
     success = dyn_dbghelp.pMiniDumpWriteDump(
         process_information->hProcess,      /* HANDLE                            hProcess */
@@ -244,11 +248,13 @@ static void write_minidump(const char *child_file_path, const LPPROCESS_INFORMAT
     }
 }
 
-static void print_stacktrace(const LPPROCESS_INFORMATION process_information, LPVOID address) {
+static void print_stacktrace(const LPPROCESS_INFORMATION process_information, PCONTEXT context, LPVOID address) {
     STACKFRAME64 stack_frame;
-    CONTEXT context;
-    HANDLE thread_handle = NULL;
 
+    if (!context) {
+        printf_message("Cannot create a stacktrace without a context");
+        return;
+    }
     if (!dyn_dbghelp.pStackWalk64) {
         printf_message("Cannot find StackWalk64 in dbghelp.dll: no stacktrace");
         return;
@@ -274,19 +280,6 @@ static void print_stacktrace(const LPPROCESS_INFORMATION process_information, LP
         return;
     }
 
-    thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, process_information->dwThreadId);
-    if (!thread_handle) {
-        printf_windows_message("OpenThread failed: no stacktrace");
-        goto cleanup;
-    }
-
-    memset(&context, 0, sizeof(context));
-    context.ContextFlags = CONTEXT_ALL;
-    if (!GetThreadContext(thread_handle, &context)) {
-        printf_windows_message("GetThreadContext failed: no stacktrace");
-        goto cleanup;
-    }
-
     if (!dyn_dbghelp.pSymRefreshModuleList || !dyn_dbghelp.pSymRefreshModuleList(process_information->hProcess)) {
         printf_windows_message("SymRefreshModuleList failed: maybe no stacktrace");
     }
@@ -299,24 +292,24 @@ static void print_stacktrace(const LPPROCESS_INFORMATION process_information, LP
 
 #if defined(SDLPROCDUMP_CPU_X86)
     DWORD machine_type = IMAGE_FILE_MACHINE_I386;
-    stack_frame.AddrFrame.Offset = context.Ebp;
-    stack_frame.AddrStack.Offset = context.Esp;
-    stack_frame.AddrPC.Offset = context.Eip;
+    stack_frame.AddrFrame.Offset = context->Ebp;
+    stack_frame.AddrStack.Offset = context->Esp;
+    stack_frame.AddrPC.Offset = context->Eip;
 #elif defined(SDLPROCDUMP_CPU_X64)
     DWORD machine_type = IMAGE_FILE_MACHINE_AMD64;
-    stack_frame.AddrFrame.Offset = context.Rbp;
-    stack_frame.AddrStack.Offset = context.Rsp;
-    stack_frame.AddrPC.Offset = context.Rip;
+    stack_frame.AddrFrame.Offset = context->Rbp;
+    stack_frame.AddrStack.Offset = context->Rsp;
+    stack_frame.AddrPC.Offset = context->Rip;
 #elif defined(SDLPROCDUMP_CPU_ARM32)
     DWORD machine_type = IMAGE_FILE_MACHINE_ARM;
-    stack_frame.AddrFrame.Offset = context.Lr;
-    stack_frame.AddrStack.Offset = context.Sp;
-    stack_frame.AddrPC.Offset = context.Pc;
+    stack_frame.AddrFrame.Offset = context->Lr;
+    stack_frame.AddrStack.Offset = context->Sp;
+    stack_frame.AddrPC.Offset = context->Pc;
 #elif defined(SDLPROCDUMP_CPU_ARM64)
     DWORD machine_type = IMAGE_FILE_MACHINE_ARM64;
-    stack_frame.AddrFrame.Offset = context.Fp;
-    stack_frame.AddrStack.Offset = context.Sp;
-    stack_frame.AddrPC.Offset = context.Pc;
+    stack_frame.AddrFrame.Offset = context->Fp;
+    stack_frame.AddrStack.Offset = context->Sp;
+    stack_frame.AddrPC.Offset = context->Pc;
 #endif
     while (dyn_dbghelp.pStackWalk64(machine_type,                          /* DWORD                            MachineType */
                                     process_information->hProcess,         /* HANDLE                           hProcess */
@@ -374,12 +367,26 @@ static void print_stacktrace(const LPPROCESS_INFORMATION process_information, LP
         }
         printf_message("%s!%s+0x%x %s %s", image_file_name, symbol_name, dwDisplacement, file_name, line_number);
     }
+}
 
-cleanup:
-    if (thread_handle) {
+static PCONTEXT FillInThreadContext(LPPROCESS_INFORMATION process_information, PCONTEXT context_buffer) {
+    HANDLE thread_handle = NULL;
+
+    thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, process_information->dwThreadId);
+    if (!thread_handle) {
+        printf_windows_message("OpenThread failed: no stacktrace");
+        return NULL;
+    }
+
+    memset(context_buffer, 0, sizeof(*context_buffer));
+    context_buffer->ContextFlags = CONTEXT_ALL;
+    if (!GetThreadContext(thread_handle, context_buffer)) {
+        printf_windows_message("GetThreadContext failed: no stacktrace");
         CloseHandle(thread_handle);
+        return NULL;
     }
-    return;
+    CloseHandle(thread_handle);
+    return context_buffer;
 }
 
 int main(int argc, char *argv[]) {
@@ -454,6 +461,9 @@ int main(int argc, char *argv[]) {
             switch (event.dwDebugEventCode) {
             case EXCEPTION_DEBUG_EVENT:
                 if (IsFatalExceptionCode(event.u.Exception.ExceptionRecord.ExceptionCode) || (event.u.Exception.ExceptionRecord.ExceptionFlags & EXCEPTION_NONCONTINUABLE)) {
+                    CONTEXT context_buffer;
+                    PCONTEXT context;
+
                     printf_message("EXCEPTION_DEBUG_EVENT");
                     printf_message("       ExceptionCode: 0x%08lx (%s)",
                         event.u.Exception.ExceptionRecord.ExceptionCode,
@@ -463,10 +473,12 @@ int main(int argc, char *argv[]) {
                     printf_message("    ExceptionAddress: 0x%08lx",
                         event.u.Exception.ExceptionRecord.ExceptionAddress);
                     printf_message("    (Non-continuable exception debug event)");
-                    write_minidump(argv[1], &process_information, event.dwThreadId);
+
+                    context = FillInThreadContext(&process_information, &context_buffer);
+                    write_minidump(argv[1], &process_information, event.dwThreadId, &event.u.Exception.ExceptionRecord, context);
                     printf_message("");
 #ifdef SDLPROCDUMP_PRINTSTACK
-                    print_stacktrace(&process_information, event.u.Exception.ExceptionRecord.ExceptionAddress);
+                    print_stacktrace(&process_information, event.u.Exception.ExceptionRecord.ExceptionAddress, context);
 #else
                     printf_message("No support for printing stacktrack for current architecture");
 #endif