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