Atlas - sdlprocdump.c
Home / ext / SDL / test / win32 Lines: 9 | Size: 30549 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1#ifndef WIN32_LEAN_AND_MEAN 2#define WIN32_LEAN_AND_MEAN 3#endif 4 5#include <windows.h> 6#include <dbghelp.h> 7 8#ifndef STATUS_HEAP_CORRUPTION 9#define STATUS_HEAP_CORRUPTION ((DWORD)0xC0000374L) 10#endif 11#ifndef EXCEPTION_UNWINDING 12#define EXCEPTION_UNWINDING 0x2 13#endif 14#ifndef EXCEPTION_EXIT_UNWIND 15#define EXCEPTION_EXIT_UNWIND 0x4 16#endif 17#ifndef EXCEPTION_STACK_INVALID 18#define EXCEPTION_STACK_INVALID 0x8 19#endif 20#ifndef EXCEPTION_NESTED_CALL 21#define EXCEPTION_NESTED_CALL 0x10 22#endif 23#ifndef EXCEPTION_TARGET_UNWIND 24#define EXCEPTION_TARGET_UNWIND 0x20 25#endif 26#ifndef EXCEPTION_COLLIDED_UNWIND 27#define EXCEPTION_COLLIDED_UNWIND 0x40 28#endif 29 30#include <inttypes.h> 31#include <stdarg.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35 36#define DUMP_FOLDER "minidumps" 37#define APPNAME "SDLPROCDUMP" 38 39#define PRODCUMP_MIN(A,B) (((A) < (B)) ? (A) : (B)) 40 41#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86) 42#define SDLPROCDUMP_CPU_X86 1 43#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) 44#define SDLPROCDUMP_CPU_X64 1 45#elif defined(__aarch64__) || defined(_M_ARM64) 46#define SDLPROCDUMP_CPU_ARM64 1 47#elif defined(__arm__) || defined(_M_ARM) 48#define SDLPROCDUMP_CPU_ARM32 1 49#endif 50 51#if defined(SDLPROCDUMP_CPU_X86) || defined(SDLPROCDUMP_CPU_X64) || defined(SDLPROCDUMP_CPU_ARM32) || defined(SDLPROCDUMP_CPU_ARM64) 52#define SDLPROCDUMP_PRINTSTACK 53#else 54#pragma message("Unsupported architecture: don't know how to StackWalk") 55#endif 56 57#ifndef EXCEPTION_SOFTWARE_ORIGINATE 58#define EXCEPTION_SOFTWARE_ORIGINATE 0x80 59#endif 60 61static void printf_message(const char *format, ...) { 62 va_list ap; 63 fprintf(stderr, "[" APPNAME "] "); 64 va_start(ap, format); 65 vfprintf(stderr, format, ap); 66 va_end(ap); 67 fprintf(stderr, "\n"); 68} 69 70static void printf_windows_message(const char *format, ...) { 71 va_list ap; 72 char win_msg[512]; 73 size_t win_msg_len; 74 75 FormatMessageA( 76 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 77 NULL, 78 GetLastError(), 79 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 80 win_msg, sizeof(win_msg)/sizeof(*win_msg), 81 NULL); 82 win_msg_len = strlen(win_msg); 83 while (win_msg[win_msg_len-1] == '\r' || win_msg[win_msg_len-1] == '\n' || win_msg[win_msg_len-1] == ' ') { 84 win_msg[win_msg_len-1] = '\0'; 85 win_msg_len--; 86 } 87 fprintf(stderr, "[" APPNAME "] "); 88 va_start(ap, format); 89 vfprintf(stderr, format, ap); 90 va_end(ap); 91 fprintf(stderr, " (%s)\n", win_msg); 92} 93 94struct { 95 HMODULE module; 96 BOOL (WINAPI *pSymInitialize)(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess); 97 BOOL (WINAPI *pSymCleanup)(HANDLE hProcess); 98 BOOL (WINAPI *pMiniDumpWriteDump)( 99 HANDLE hProcess, 100 DWORD ProcessId, 101 HANDLE hFile, 102 MINIDUMP_TYPE DumpType, 103 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 104 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 105 PMINIDUMP_CALLBACK_INFORMATION CallbackParam); 106 BOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol); 107 BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line); 108 BOOL (WINAPI *pStackWalk64)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, 109 PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, 110 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, 111 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); 112 PVOID (WINAPI *pSymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); 113 DWORD64 (WINAPI *pSymGetModuleBase64)(HANDLE hProcess, DWORD64 qwAddr); 114 BOOL (WINAPI *pSymGetModuleInfo64)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULE64 ModuleInfo); 115 BOOL (WINAPI *pSymRefreshModuleList)(HANDLE hProcess); 116} dyn_dbghelp; 117 118static void load_dbghelp(void) { 119 if (dyn_dbghelp.module) { 120 return; 121 } 122 dyn_dbghelp.module = LoadLibraryA("dbghelp.dll"); 123 if (!dyn_dbghelp.module) { 124 printf_message("Failed to load dbghelp.dll"); 125 goto failed; 126 } 127 dyn_dbghelp.pSymInitialize = (void *)GetProcAddress(dyn_dbghelp.module, "SymInitialize"); 128 dyn_dbghelp.pSymCleanup = (void *)GetProcAddress(dyn_dbghelp.module, "SymCleanup"); 129 dyn_dbghelp.pMiniDumpWriteDump = (void *)GetProcAddress(dyn_dbghelp.module, "MiniDumpWriteDump"); 130 dyn_dbghelp.pSymFromAddr = (void *)GetProcAddress(dyn_dbghelp.module, "SymFromAddr"); 131 dyn_dbghelp.pStackWalk64 = (void *)GetProcAddress(dyn_dbghelp.module, "StackWalk64"); 132 dyn_dbghelp.pSymGetLineFromAddr64 = (void *)GetProcAddress(dyn_dbghelp.module, "SymGetLineFromAddr64"); 133 dyn_dbghelp.pSymFunctionTableAccess64 = (void *)GetProcAddress(dyn_dbghelp.module, "SymFunctionTableAccess64"); 134 dyn_dbghelp.pSymGetModuleBase64 = (void *)GetProcAddress(dyn_dbghelp.module, "SymGetModuleBase64"); 135 dyn_dbghelp.pSymGetModuleInfo64 = (void *)GetProcAddress(dyn_dbghelp.module, "SymGetModuleInfo64"); 136 dyn_dbghelp.pSymRefreshModuleList = (void *)GetProcAddress(dyn_dbghelp.module, "SymRefreshModuleList"); 137 return; 138failed: 139 if (dyn_dbghelp.module) { 140 FreeLibrary(dyn_dbghelp.module); 141 dyn_dbghelp.module = NULL; 142 } 143} 144 145static void unload_dbghelp(void) { 146 if (!dyn_dbghelp.module) { 147 return; 148 } 149 FreeLibrary(dyn_dbghelp.module); 150 memset(&dyn_dbghelp, 0, sizeof(dyn_dbghelp)); 151} 152 153#define FOREACH_EXCEPTION_CODES(X) \ 154 X(EXCEPTION_ACCESS_VIOLATION) \ 155 X(EXCEPTION_DATATYPE_MISALIGNMENT) \ 156 X(EXCEPTION_BREAKPOINT) \ 157 X(EXCEPTION_SINGLE_STEP) \ 158 X(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) \ 159 X(EXCEPTION_FLT_DENORMAL_OPERAND) \ 160 X(EXCEPTION_FLT_DIVIDE_BY_ZERO) \ 161 X(EXCEPTION_FLT_INEXACT_RESULT) \ 162 X(EXCEPTION_FLT_INVALID_OPERATION) \ 163 X(EXCEPTION_FLT_OVERFLOW) \ 164 X(EXCEPTION_FLT_STACK_CHECK) \ 165 X(EXCEPTION_FLT_UNDERFLOW) \ 166 X(EXCEPTION_INT_DIVIDE_BY_ZERO) \ 167 X(EXCEPTION_INT_OVERFLOW) \ 168 X(EXCEPTION_PRIV_INSTRUCTION) \ 169 X(EXCEPTION_IN_PAGE_ERROR) \ 170 X(EXCEPTION_ILLEGAL_INSTRUCTION) \ 171 X(EXCEPTION_NONCONTINUABLE_EXCEPTION) \ 172 X(EXCEPTION_STACK_OVERFLOW) \ 173 X(EXCEPTION_INVALID_DISPOSITION) \ 174 X(EXCEPTION_GUARD_PAGE) \ 175 X(EXCEPTION_INVALID_HANDLE) \ 176 X(STATUS_HEAP_CORRUPTION) 177 178#define FOREACH_EXCEPTION_FLAGS(X) \ 179 X(EXCEPTION_NONCONTINUABLE) \ 180 X(EXCEPTION_UNWINDING) \ 181 X(EXCEPTION_EXIT_UNWIND) \ 182 X(EXCEPTION_STACK_INVALID) \ 183 X(EXCEPTION_NESTED_CALL) \ 184 X(EXCEPTION_TARGET_UNWIND) \ 185 X(EXCEPTION_COLLIDED_UNWIND) \ 186 X(EXCEPTION_SOFTWARE_ORIGINATE) 187 188static const char *exceptionCode_to_string(DWORD dwCode) { 189#define SWITCH_CODE_STR(V) case V: return #V; 190 switch (dwCode) { 191 case 0xe06d7363: return "MS Visual C++ Exception"; 192 FOREACH_EXCEPTION_CODES(SWITCH_CODE_STR) 193 default: { 194 return "unknown"; 195 } 196 } 197#undef SWITCH_CODE_STR 198} 199 200static const char *exceptionFlags_to_string(DWORD dwFlags, char *buffer, size_t buffer_length) { 201 buffer[0] = '\0'; 202 203#define APPEND_OR_STR(CODE) \ 204 if (dwFlags & (CODE)) { \ 205 if (buffer[0]) { \ 206 strcat_s(buffer, buffer_length, "|"); \ 207 } \ 208 strcat_s(buffer, buffer_length, #CODE); \ 209 } 210 211 FOREACH_EXCEPTION_FLAGS(APPEND_OR_STR) 212#undef APPEND_OR_STR 213 return buffer; 214} 215 216static BOOL IsCXXException(DWORD dwCode) { 217 /* https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 */ 218 return dwCode == 0xe06d7363; /* FOURCC(0xe0, 'm', 's', 'c') */ 219} 220 221static BOOL IsFatalExceptionCode(DWORD dwCode) { 222 switch (dwCode) { 223 case EXCEPTION_ACCESS_VIOLATION: 224 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 225 case EXCEPTION_IN_PAGE_ERROR: 226 case EXCEPTION_ILLEGAL_INSTRUCTION: 227 case EXCEPTION_INT_DIVIDE_BY_ZERO: 228 case EXCEPTION_STACK_OVERFLOW: 229 case STATUS_HEAP_CORRUPTION: 230 case STATUS_STACK_BUFFER_OVERRUN: 231 case EXCEPTION_GUARD_PAGE: 232 case EXCEPTION_INVALID_HANDLE: 233 return TRUE; 234 default: 235 return FALSE; 236 } 237} 238 239static const char *get_simple_basename(const char *path) { 240 const char *pos = strrchr(path, '\\'); 241 if (pos) { 242 return pos + 1; 243 } 244 pos = strrchr(path, '/'); 245 if (pos) { 246 return pos + 1; 247 } 248 return path; 249} 250 251static void write_minidump(const char *child_file_path, const LPPROCESS_INFORMATION process_information, DWORD dwThreadId, PEXCEPTION_RECORD exception_record, PCONTEXT context) { 252 BOOL success; 253 char dump_file_path[MAX_PATH]; 254 char child_file_name[64]; 255 EXCEPTION_POINTERS exception_pointers; 256 HANDLE hFile = INVALID_HANDLE_VALUE; 257 MINIDUMP_EXCEPTION_INFORMATION minidump_exception_information; 258 SYSTEMTIME system_time; 259 260 if (!dyn_dbghelp.pMiniDumpWriteDump) { 261 printf_message("Cannot find pMiniDumpWriteDump in dbghelp.dll: no minidump"); 262 return; 263 } 264 265 success = CreateDirectoryA(DUMP_FOLDER, NULL); 266 if (!success && GetLastError() != ERROR_ALREADY_EXISTS) { 267 printf_windows_message("Failed to create minidump directory"); 268 goto post_dump; 269 } 270 _splitpath_s(child_file_path, NULL, 0, NULL, 0, child_file_name, sizeof(child_file_name), NULL, 0); 271 GetLocalTime(&system_time); 272 273 snprintf(dump_file_path, sizeof(dump_file_path), "minidumps/%s_%04d-%02d-%02d_%02d-%02d-%02d.dmp", 274 child_file_name, 275 system_time.wYear, system_time.wMonth, system_time.wDay, 276 system_time.wHour, system_time.wMinute, system_time.wSecond); 277 printf_message(""); 278 printf_message("Writing minidump to \"%s\"", dump_file_path); 279 hFile = CreateFileA( 280 dump_file_path, 281 GENERIC_WRITE, 282 FILE_SHARE_WRITE, 283 NULL, 284 CREATE_ALWAYS, 285 FILE_ATTRIBUTE_NORMAL, 286 NULL); 287 if (hFile == INVALID_HANDLE_VALUE) { 288 printf_windows_message("Failed to open file for minidump"); 289 goto post_dump; 290 } 291 memset(&exception_pointers, 0, sizeof(exception_pointers)); 292 exception_pointers.ContextRecord = context; 293 exception_pointers.ExceptionRecord = exception_record; 294 minidump_exception_information.ClientPointers = FALSE; 295 minidump_exception_information.ExceptionPointers = &exception_pointers; 296 minidump_exception_information.ThreadId = dwThreadId; 297 success = dyn_dbghelp.pMiniDumpWriteDump( 298 process_information->hProcess, /* HANDLE hProcess */ 299 process_information->dwProcessId, /* DWORD ProcessId */ 300 hFile, /* HANDLE hFile */ 301 MiniDumpWithFullMemory, /* MINIDUMP_TYPE DumpType */ 302 &minidump_exception_information, /* PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam */ 303 NULL, /* PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam */ 304 NULL); /* PMINIDUMP_CALLBACK_INFORMATION CallbackParam */ 305 if (!success) { 306 printf_windows_message("Failed to write minidump"); 307 } 308post_dump: 309 if (hFile != INVALID_HANDLE_VALUE) { 310 CloseHandle(hFile); 311 } 312} 313 314static void print_stacktrace(const LPPROCESS_INFORMATION process_information, LPVOID address, PCONTEXT context) { 315 STACKFRAME64 stack_frame; 316 DWORD machine_type; 317 318 if (!context) { 319 printf_message("Cannot create a stacktrace without a context"); 320 return; 321 } 322 if (!dyn_dbghelp.pStackWalk64) { 323 printf_message("Cannot find StackWalk64 in dbghelp.dll: no stacktrace"); 324 return; 325 } 326 if (!dyn_dbghelp.pSymFunctionTableAccess64) { 327 printf_message("Cannot find SymFunctionTableAccess64 in dbghelp.dll: no stacktrace"); 328 return; 329 } 330 if (!dyn_dbghelp.pSymGetModuleBase64) { 331 printf_message("Cannot find SymGetModuleBase64 in dbghelp.dll: no stacktrace"); 332 return; 333 } 334 if (!dyn_dbghelp.pSymFromAddr) { 335 printf_message("Cannot find pSymFromAddr in dbghelp.dll: no stacktrace"); 336 return; 337 } 338 if (!dyn_dbghelp.pSymGetLineFromAddr64) { 339 printf_message("Cannot find SymGetLineFromAddr64 in dbghelp.dll: no stacktrace"); 340 return; 341 } 342 if (!dyn_dbghelp.pSymGetModuleInfo64) { 343 printf_message("Cannot find SymGetModuleInfo64 in dbghelp.dll: no stacktrace"); 344 return; 345 } 346 347 if (!dyn_dbghelp.pSymRefreshModuleList || !dyn_dbghelp.pSymRefreshModuleList(process_information->hProcess)) { 348 printf_windows_message("SymRefreshModuleList failed: maybe no stacktrace"); 349 } 350 351 memset(&stack_frame, 0, sizeof(stack_frame)); 352 353 stack_frame.AddrPC.Mode = AddrModeFlat; 354 stack_frame.AddrFrame.Mode = AddrModeFlat; 355 stack_frame.AddrStack.Mode = AddrModeFlat; 356 357#if defined(SDLPROCDUMP_CPU_X86) 358 machine_type = IMAGE_FILE_MACHINE_I386; 359 stack_frame.AddrFrame.Offset = context->Ebp; 360 stack_frame.AddrStack.Offset = context->Esp; 361 stack_frame.AddrPC.Offset = context->Eip; 362#elif defined(SDLPROCDUMP_CPU_X64) 363 machine_type = IMAGE_FILE_MACHINE_AMD64; 364 stack_frame.AddrFrame.Offset = context->Rbp; 365 stack_frame.AddrStack.Offset = context->Rsp; 366 stack_frame.AddrPC.Offset = context->Rip; 367#elif defined(SDLPROCDUMP_CPU_ARM32) 368 machine_type = IMAGE_FILE_MACHINE_ARM; 369 stack_frame.AddrFrame.Offset = context->Lr; 370 stack_frame.AddrStack.Offset = context->Sp; 371 stack_frame.AddrPC.Offset = context->Pc; 372#elif defined(SDLPROCDUMP_CPU_ARM64) 373 machine_type = IMAGE_FILE_MACHINE_ARM64; 374 stack_frame.AddrFrame.Offset = context->Fp; 375 stack_frame.AddrStack.Offset = context->Sp; 376 stack_frame.AddrPC.Offset = context->Pc; 377#endif 378 while (dyn_dbghelp.pStackWalk64(machine_type, /* DWORD MachineType */ 379 process_information->hProcess, /* HANDLE hProcess */ 380 process_information->hThread, /* HANDLE hThread */ 381 &stack_frame, /* LPSTACKFRAME64 StackFrame */ 382 context, /* PVOID ContextRecord */ 383 NULL, /* PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine */ 384 dyn_dbghelp.pSymFunctionTableAccess64, /* PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine */ 385 dyn_dbghelp.pSymGetModuleBase64, /* PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine */ 386 NULL)) { /* PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress */ 387 IMAGEHLP_MODULE64 module_info; 388 union { 389 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; 390 SYMBOL_INFO symbol_info; 391 } symbol; 392 DWORD64 dwDisplacement; 393 DWORD lineColumn = 0; 394 IMAGEHLP_LINE64 line; 395 const char *image_file_name; 396 const char *symbol_name; 397 const char *file_name; 398 char line_number[16]; 399 400 if (stack_frame.AddrPC.Offset == stack_frame.AddrReturn.Offset) { 401 printf_message("PC == Return Address => Possible endless callstack"); 402 break; 403 } 404 405 memset(&module_info, 0, sizeof(module_info)); 406 module_info.SizeOfStruct = sizeof(module_info); 407 if (!dyn_dbghelp.pSymGetModuleInfo64(process_information->hProcess, stack_frame.AddrPC.Offset, &module_info)) { 408 image_file_name = "?"; 409 } else { 410 image_file_name = get_simple_basename(module_info.ImageName); 411 } 412 413 memset(&symbol, 0, sizeof(symbol)); 414 symbol.symbol_info.SizeOfStruct = sizeof(symbol.symbol_info); 415 symbol.symbol_info.MaxNameLen = MAX_SYM_NAME; 416 if (!dyn_dbghelp.pSymFromAddr(process_information->hProcess, (DWORD64)(uintptr_t)stack_frame.AddrPC.Offset, &dwDisplacement, &symbol.symbol_info)) { 417 symbol_name = "???"; 418 dwDisplacement = 0; 419 } else { 420 symbol_name = symbol.symbol_info.Name; 421 } 422 423 line.SizeOfStruct = sizeof(line); 424 if (!dyn_dbghelp.pSymGetLineFromAddr64(process_information->hProcess, (DWORD64)(uintptr_t)stack_frame.AddrPC.Offset, &lineColumn, &line)) { 425 file_name = ""; 426 line_number[0] = '\0'; 427 } else { 428 file_name = line.FileName; 429 snprintf(line_number, sizeof(line_number), "Line %u", (unsigned int)line.LineNumber); 430 } 431 printf_message("%s!%s+0x%x %s %s", image_file_name, symbol_name, dwDisplacement, file_name, line_number); 432 } 433} 434 435static PCONTEXT FillInThreadContext(LPPROCESS_INFORMATION process_information, PCONTEXT context_buffer) { 436 HANDLE thread_handle = NULL; 437 438 thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, process_information->dwThreadId); 439 if (!thread_handle) { 440 printf_windows_message("OpenThread failed: no stacktrace"); 441 return NULL; 442 } 443 444 memset(context_buffer, 0, sizeof(*context_buffer)); 445 context_buffer->ContextFlags = CONTEXT_ALL; 446 if (!GetThreadContext(thread_handle, context_buffer)) { 447 printf_windows_message("GetThreadContext failed: no stacktrace"); 448 CloseHandle(thread_handle); 449 return NULL; 450 } 451 CloseHandle(thread_handle); 452 return context_buffer; 453} 454 455static void GetMSCExceptionName(HANDLE hProcess, ULONG_PTR *parameters, DWORD count_parameters, char *buffer, size_t buffer_size) { 456 457#define FIXUP_DWORD_POINTER(ADDR) ((sizeof(void *) == 8) ? (parameters[3] + (ADDR)) : (ADDR)) 458#define CHECKED_ReadProcessMemory(PROCESS, ADDRESS, BUFFER, COUNT, WHAT) \ 459 do { \ 460 SIZE_T actual_count; \ 461 BOOL res = ReadProcessMemory((PROCESS), (ADDRESS), (BUFFER), (COUNT), &actual_count); \ 462 if (!res) { \ 463 printf_windows_message(WHAT ": ReadProcessMemory failed"); \ 464 strncpy_s(buffer, buffer_size, "<error>", buffer_size); \ 465 return; \ 466 } \ 467 if ((COUNT) != (actual_count)) { \ 468 printf_message(WHAT ": ReadProcessMemory did not read enough data actual=%lu expected=%lu", \ 469 (unsigned long) (actual_count), (unsigned long) (COUNT)); \ 470 strncpy_s(buffer, buffer_size, "<error>", buffer_size); \ 471 return; \ 472 } \ 473 } while (0) 474 475 DWORD depth0; 476 char *ptr_depth0; 477 DWORD depth1; 478 char *ptr_depth1; 479 DWORD depth2; 480 char *ptr_depth2; 481 482 CHECKED_ReadProcessMemory(hProcess, (void *)(parameters[2] + 3 * sizeof(DWORD)), &depth0, sizeof(depth0), "depth 0"); 483 ptr_depth0 = (char *)FIXUP_DWORD_POINTER(depth0); 484 CHECKED_ReadProcessMemory(hProcess, ptr_depth0 + 1 * sizeof(DWORD), &depth1, sizeof(depth1), "depth 1"); 485 ptr_depth1 = (char *)FIXUP_DWORD_POINTER(depth1); 486 CHECKED_ReadProcessMemory(hProcess, ptr_depth1 + 1 * sizeof(DWORD), &depth2, sizeof(depth2), "depth 2"); 487 ptr_depth2 = (char *)FIXUP_DWORD_POINTER(depth2); 488 CHECKED_ReadProcessMemory(hProcess, ptr_depth2 + 2 * sizeof(void*), buffer, buffer_size, "data"); 489 buffer[buffer_size - 1] = '\0'; 490 491#undef FIXUP_DWORD_POINTER 492#undef CHECKED_ReadProcessMemory 493} 494 495static void log_usage(const char *argv0) { 496 fprintf(stderr, "Usage: %s [--help] [--debug-stream] [--] PROGRAM [ARG1 [ARG2 [ARG3 ... ]]]\n", argv0); 497} 498 499int main(int argc, char *argv[]) { 500 int i; 501 int cmd_start; 502 size_t command_line_len = 0; 503 char *command_line; 504 STARTUPINFOA startup_info; 505 PROCESS_INFORMATION process_information; 506 BOOL success; 507 BOOL debugger_present; 508 DWORD exit_code; 509 DWORD creation_flags; 510 BOOL log_debug_stream = FALSE; 511 512 cmd_start = -1; 513 for (i = 1; i < argc; i++) { 514 if (strcmp(argv[i], "--") == 0) { 515 cmd_start = i + 1; 516 break; 517 } else if (strcmp(argv[i], "--debug-stream") == 0) { 518 log_debug_stream = TRUE; 519 continue; 520 } else if (strcmp(argv[i], "--help") == 0) { 521 log_usage(argv[0]); 522 return 0; 523 } else { 524 cmd_start = i; 525 break; 526 } 527 } 528 if (cmd_start < 0 || cmd_start >= argc) { 529 log_usage(argv[0]); 530 return 1; 531 } 532 533 for (i = cmd_start; i < argc; i++) { 534 command_line_len += strlen(argv[i]) + 1; 535 } 536 command_line = malloc(command_line_len + 1); 537 if (!command_line) { 538 printf_message("Failed to allocate memory for command line"); 539 return 1; 540 } 541 command_line[0] = '\0'; 542 for (i = cmd_start; i < argc; i++) { 543 strcat_s(command_line, command_line_len, argv[i]); 544 if (i != argc - 1) { 545 strcat_s(command_line, command_line_len, " "); 546 } 547 } 548 549 memset(&startup_info, 0, sizeof(startup_info)); 550 startup_info.cb = sizeof(startup_info); 551 552 debugger_present = IsDebuggerPresent(); 553 creation_flags = NORMAL_PRIORITY_CLASS; 554 if (!debugger_present) { 555 creation_flags |= DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; 556 } 557 success = CreateProcessA( 558 argv[cmd_start], /* LPCSTR lpApplicationName, */ 559 command_line, /* LPSTR lpCommandLine, */ 560 NULL, /* LPSECURITY_ATTRIBUTES lpProcessAttributes, */ 561 NULL, /* LPSECURITY_ATTRIBUTES lpThreadAttributes, */ 562 TRUE, /* BOOL bInheritHandles, */ 563 creation_flags, /* DWORD dwCreationFlags, */ 564 NULL, /* LPVOID lpEnvironment, */ 565 NULL, /* LPCSTR lpCurrentDirectory, */ 566 &startup_info, /* LPSTARTUPINFOA lpStartupInfo, */ 567 &process_information); /* LPPROCESS_INFORMATION lpProcessInformation */ 568 569 if (!success) { 570 printf_windows_message("Failed to start application \"%s\"", argv[cmd_start]); 571 return 1; 572 } 573 574 if (debugger_present) { 575 WaitForSingleObject(process_information.hProcess, INFINITE); 576 } else { 577 int process_alive = 1; 578 DEBUG_EVENT event; 579 while (process_alive) { 580 DWORD continue_status = DBG_CONTINUE; 581 success = WaitForDebugEvent(&event, INFINITE); 582 if (!success) { 583 printf_windows_message("Failed to get a debug event"); 584 return 1; 585 } 586 switch (event.dwDebugEventCode) { 587 case OUTPUT_DEBUG_STRING_EVENT: 588 { 589 if (log_debug_stream) { 590 SIZE_T bytes_read = 0; 591 union { 592 char char_buffer[512]; 593 WCHAR wchar_buffer[256]; 594 } buffer; 595 if (ReadProcessMemory(process_information.hProcess, event.u.DebugString.lpDebugStringData, buffer.char_buffer, PRODCUMP_MIN(sizeof(buffer), event.u.DebugString.nDebugStringLength), &bytes_read) && bytes_read) { 596 if (event.u.DebugString.fUnicode) { 597 size_t len = bytes_read / 2; 598 buffer.wchar_buffer[255] = '\0'; 599 while (len > 0 && (buffer.wchar_buffer[len - 1] == '\0' || buffer.wchar_buffer[len - 1] == '\n' || buffer.wchar_buffer[len - 1] == '\r')) { 600 buffer.wchar_buffer[len - 1] = '\0'; 601 len -= 1; 602 } 603 if (len > 0) { 604 printf("[" APPNAME "] (debug) %S\n", buffer.wchar_buffer); 605 } 606 } else { 607 size_t len = bytes_read; 608 buffer.char_buffer[511] = '\0'; 609 while (len > 0 && (buffer.char_buffer[len - 1] == '\0' || buffer.char_buffer[len - 1] == '\n' || buffer.char_buffer[len - 1] == '\r')) { 610 buffer.char_buffer[len - 1] = '\0'; 611 len -= 1; 612 } 613 if (len > 0) { 614 printf("[" APPNAME "] (debug) %s\n", buffer.char_buffer); 615 } 616 } 617 } 618 } 619 break; 620 } 621 case EXCEPTION_DEBUG_EVENT: 622 { 623 const BOOL cxx_exception = IsCXXException(event.u.Exception.ExceptionRecord.ExceptionCode); 624 const BOOL is_fatal = !cxx_exception && (IsFatalExceptionCode(event.u.Exception.ExceptionRecord.ExceptionCode) || (event.u.Exception.ExceptionRecord.ExceptionFlags & EXCEPTION_NONCONTINUABLE)); 625 if (cxx_exception || is_fatal) { 626 char flag_buffer[256]; 627 printf_message("EXCEPTION_DEBUG_EVENT"); 628 printf_message(" ExceptionCode: 0x%08lx (%s)", 629 event.u.Exception.ExceptionRecord.ExceptionCode, 630 exceptionCode_to_string(event.u.Exception.ExceptionRecord.ExceptionCode)); 631 printf_message(" ExceptionFlags: 0x%08lx (%s)", 632 event.u.Exception.ExceptionRecord.ExceptionFlags, 633 exceptionFlags_to_string(event.u.Exception.ExceptionRecord.ExceptionFlags, flag_buffer, sizeof(flag_buffer))); 634 635 printf_message(" FirstChance: %ld", event.u.Exception.dwFirstChance); 636 printf_message(" ExceptionAddress: 0x%08lx", 637 event.u.Exception.ExceptionRecord.ExceptionAddress); 638 } 639 if (cxx_exception) { 640 char exception_name[256]; 641 GetMSCExceptionName(process_information.hProcess, event.u.Exception.ExceptionRecord.ExceptionInformation, event.u.Exception.ExceptionRecord.NumberParameters, 642 exception_name, sizeof(exception_name)); 643 printf_message(" Exception name: %s", exception_name); 644 } else if (is_fatal) { 645 CONTEXT context_buffer; 646 PCONTEXT context; 647 648 printf_message(" (Non-continuable exception debug event)"); 649 context = FillInThreadContext(&process_information, &context_buffer); 650 write_minidump(argv[cmd_start], &process_information, event.dwThreadId, &event.u.Exception.ExceptionRecord, context); 651 printf_message(""); 652#ifdef SDLPROCDUMP_PRINTSTACK 653 print_stacktrace(&process_information, event.u.Exception.ExceptionRecord.ExceptionAddress, context); 654#else 655 printf_message("No support for printing stacktrack for current architecture"); 656#endif 657 DebugActiveProcessStop(event.dwProcessId); 658 process_alive = FALSE; 659 } 660 continue_status = DBG_EXCEPTION_NOT_HANDLED; 661 break; 662 } 663 case CREATE_PROCESS_DEBUG_EVENT: 664 load_dbghelp(); 665 if (!dyn_dbghelp.pSymInitialize) { 666 printf_message("Cannot find pSymInitialize in dbghelp.dll: no stacktrace"); 667 break; 668 } 669 /* Don't invade process on CI: downloading symbols will cause test timeouts */ 670 if (!dyn_dbghelp.pSymInitialize(process_information.hProcess, NULL, FALSE)) { 671 printf_windows_message("SymInitialize failed: no stacktrace"); 672 break; 673 } 674 break; 675 case EXIT_PROCESS_DEBUG_EVENT: 676 if (event.dwProcessId == process_information.dwProcessId) { 677 process_alive = 0; 678 DebugActiveProcessStop(event.dwProcessId); 679 } 680 break; 681 } 682 success = ContinueDebugEvent(event.dwProcessId, event.dwThreadId, continue_status); 683 if (!process_alive) { 684 DebugActiveProcessStop(event.dwProcessId); 685 } 686 } 687 } 688 if (dyn_dbghelp.pSymCleanup) { 689 dyn_dbghelp.pSymCleanup(process_information.hProcess); 690 } 691 unload_dbghelp(); 692 693 exit_code = 1; 694 success = GetExitCodeProcess(process_information.hProcess, &exit_code); 695 696 if (!success) { 697 printf_message("Failed to get process exit code"); 698 return 1; 699 } 700 701 CloseHandle(process_information.hThread); 702 CloseHandle(process_information.hProcess); 703 704 return exit_code; 705} 706[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.