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