25 #include "C4Version.h"
30 #if defined(__CRT_WIDE) || (defined(_MSC_VER) && _MSC_VER >= 1900)
31 #define USE_WIDE_ASSERT
34 static bool FirstCrash =
true;
37 #define OC_MACHINE_UNKNOWN 0x0
38 #define OC_MACHINE_X86 0x1
39 #define OC_MACHINE_X64 0x2
40 #if defined(_M_X64) || defined(__amd64)
41 # define OC_MACHINE OC_MACHINE_X64
42 #elif defined(_M_IX86) || defined(__i386__)
43 # define OC_MACHINE OC_MACHINE_X86
45 # define OC_MACHINE OC_MACHINE_UNKNOWN
48 const size_t DumpBufferSize = 2048;
49 char DumpBuffer[DumpBufferSize];
50 char SymbolBuffer[DumpBufferSize];
53 void SafeTextDump(LPEXCEPTION_POINTERS exc,
int fd,
const wchar_t *dump_filename)
56 # define LOG_SNPRINTF _snprintf
58 # define LOG_SNPRINTF snprintf
60 #define LOG_STATIC_TEXT(text) write(fd, text, sizeof(text) - 1)
61 #define LOG_DYNAMIC_TEXT(...) write(fd, DumpBuffer, LOG_SNPRINTF(DumpBuffer, DumpBufferSize-1, __VA_ARGS__))
65 # define POINTER_FORMAT_SUFFIX PRIdPTR
66 #elif defined(_MSC_VER)
67 # define POINTER_FORMAT_SUFFIX "Ix"
68 #elif defined(__GNUC__)
69 # define POINTER_FORMAT_SUFFIX "zx"
71 # define POINTER_FORMAT_SUFFIX "p"
73 #if OC_MACHINE == OC_MACHINE_X64
74 # define POINTER_FORMAT "0x%016" POINTER_FORMAT_SUFFIX
75 #elif OC_MACHINE == OC_MACHINE_X86
76 # define POINTER_FORMAT "0x%08" POINTER_FORMAT_SUFFIX
78 # define POINTER_FORMAT "0x%" POINTER_FORMAT_SUFFIX
81 #ifndef STATUS_ASSERTION_FAILURE
82 # define STATUS_ASSERTION_FAILURE ((DWORD)0xC0000420L)
85 LOG_STATIC_TEXT(
"**********************************************************************\n");
86 LOG_STATIC_TEXT(
"* UNHANDLED EXCEPTION\n");
87 if (OC_BUILD_ID[0] !=
'\0')
88 LOG_STATIC_TEXT(
"* Build Identifier: " OC_BUILD_ID
"\n");
89 if (exc->ExceptionRecord->ExceptionCode != STATUS_ASSERTION_FAILURE && dump_filename && dump_filename[0] != L
'\0')
91 int cch = WideCharToMultiByte(CP_UTF8, 0, dump_filename, -1, SymbolBuffer,
sizeof(SymbolBuffer),
nullptr,
nullptr);
94 LOG_STATIC_TEXT(
"* A crash dump may have been written to ");
95 write(fd, SymbolBuffer, cch - 1);
96 LOG_STATIC_TEXT(
"\n");
97 LOG_STATIC_TEXT(
"* If this file exists, please send it to a developer for investigation.\n");
100 LOG_STATIC_TEXT(
"**********************************************************************\n");
102 switch (exc->ExceptionRecord->ExceptionCode)
104 #define LOG_EXCEPTION(code, text) case code: LOG_STATIC_TEXT(#code ": " text "\n"); break
105 LOG_EXCEPTION(EXCEPTION_ACCESS_VIOLATION,
"The thread tried to read from or write to a virtual address for which it does not have the appropriate access.");
106 LOG_EXCEPTION(EXCEPTION_ILLEGAL_INSTRUCTION,
"The thread tried to execute an invalid instruction.");
107 LOG_EXCEPTION(EXCEPTION_IN_PAGE_ERROR,
"The thread tried to access a page that was not present, and the system was unable to load the page.");
108 LOG_EXCEPTION(EXCEPTION_NONCONTINUABLE_EXCEPTION,
"The thread tried to continue execution after a noncontinuable exception occurred.");
109 LOG_EXCEPTION(EXCEPTION_PRIV_INSTRUCTION,
"The thread tried to execute an instruction whose operation is not allowed in the current machine mode.");
110 LOG_EXCEPTION(EXCEPTION_STACK_OVERFLOW,
"The thread used up its stack.");
111 LOG_EXCEPTION(EXCEPTION_GUARD_PAGE,
"The thread accessed memory allocated with the PAGE_GUARD modifier.");
112 LOG_EXCEPTION(STATUS_ASSERTION_FAILURE,
"The thread specified a pre- or postcondition that did not hold.");
115 LOG_DYNAMIC_TEXT(
"%#08x: The thread raised an unknown exception.\n",
static_cast<unsigned int>(exc->ExceptionRecord->ExceptionCode));
118 if (exc->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE)
119 LOG_STATIC_TEXT(
"This is a non-continuable exception.\n");
121 LOG_STATIC_TEXT(
"This is a continuable exception.\n");
124 switch (exc->ExceptionRecord->ExceptionCode)
126 case EXCEPTION_ACCESS_VIOLATION:
127 case EXCEPTION_IN_PAGE_ERROR:
128 if (exc->ExceptionRecord->NumberParameters < 2)
130 LOG_STATIC_TEXT(
"Additional information for the exception was not provided.\n");
133 LOG_STATIC_TEXT(
"Additional information for the exception: The thread ");
134 switch (exc->ExceptionRecord->ExceptionInformation[0])
136 #ifndef EXCEPTION_READ_FAULT
137 # define EXCEPTION_READ_FAULT 0
138 # define EXCEPTION_WRITE_FAULT 1
139 # define EXCEPTION_EXECUTE_FAULT 8
141 case EXCEPTION_READ_FAULT: LOG_STATIC_TEXT(
"tried to read from memory");
break;
142 case EXCEPTION_WRITE_FAULT: LOG_STATIC_TEXT(
"tried to write to memory");
break;
143 case EXCEPTION_EXECUTE_FAULT: LOG_STATIC_TEXT(
"caused an user-mode DEP violation");
break;
144 default: LOG_DYNAMIC_TEXT(
"tried to access (%#x) memory",
static_cast<unsigned int>(exc->ExceptionRecord->ExceptionInformation[0]));
break;
146 LOG_DYNAMIC_TEXT(
" at address " POINTER_FORMAT
".\n",
static_cast<size_t>(exc->ExceptionRecord->ExceptionInformation[1]));
147 if (exc->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR)
149 if (exc->ExceptionRecord->NumberParameters >= 3)
150 LOG_DYNAMIC_TEXT(
"The NTSTATUS code that resulted in this exception was " POINTER_FORMAT
".\n",
static_cast<size_t>(exc->ExceptionRecord->ExceptionInformation[2]));
152 LOG_STATIC_TEXT(
"The NTSTATUS code that resulted in this exception was not provided.\n");
156 case STATUS_ASSERTION_FAILURE:
157 if (exc->ExceptionRecord->NumberParameters < 3)
159 LOG_STATIC_TEXT(
"Additional information for the exception was not provided.\n");
162 #ifdef USE_WIDE_ASSERT
163 # define ASSERTION_INFO_FORMAT "%ls"
164 # define ASSERTION_INFO_TYPE wchar_t *
166 # define ASSERTION_INFO_FORMAT "%s"
167 # define ASSERTION_INFO_TYPE char *
169 LOG_DYNAMIC_TEXT(
"Additional information for the exception:\n Assertion that failed: " ASSERTION_INFO_FORMAT
"\n File: " ASSERTION_INFO_FORMAT
"\n Line: %d\n",
170 reinterpret_cast<ASSERTION_INFO_TYPE
>(exc->ExceptionRecord->ExceptionInformation[0]),
171 reinterpret_cast<ASSERTION_INFO_TYPE
>(exc->ExceptionRecord->ExceptionInformation[1]),
172 (
int) exc->ExceptionRecord->ExceptionInformation[2]);
177 #if OC_MACHINE == OC_MACHINE_X64
178 LOG_STATIC_TEXT(
"\nProcessor registers (x86_64):\n");
179 LOG_DYNAMIC_TEXT(
"RAX: " POINTER_FORMAT
", RBX: " POINTER_FORMAT
", RCX: " POINTER_FORMAT
", RDX: " POINTER_FORMAT
"\n",
180 static_cast<size_t>(exc->ContextRecord->Rax),
static_cast<size_t>(exc->ContextRecord->Rbx),
181 static_cast<size_t>(exc->ContextRecord->Rcx),
static_cast<size_t>(exc->ContextRecord->Rdx));
182 LOG_DYNAMIC_TEXT(
"RBP: " POINTER_FORMAT
", RSI: " POINTER_FORMAT
", RDI: " POINTER_FORMAT
", R8: " POINTER_FORMAT
"\n",
183 static_cast<size_t>(exc->ContextRecord->Rbp),
static_cast<size_t>(exc->ContextRecord->Rsi),
184 static_cast<size_t>(exc->ContextRecord->Rdi),
static_cast<size_t>(exc->ContextRecord->R8));
185 LOG_DYNAMIC_TEXT(
" R9: " POINTER_FORMAT
", R10: " POINTER_FORMAT
", R11: " POINTER_FORMAT
", R12: " POINTER_FORMAT
"\n",
186 static_cast<size_t>(exc->ContextRecord->R9),
static_cast<size_t>(exc->ContextRecord->R10),
187 static_cast<size_t>(exc->ContextRecord->R11),
static_cast<size_t>(exc->ContextRecord->R12));
188 LOG_DYNAMIC_TEXT(
"R13: " POINTER_FORMAT
", R14: " POINTER_FORMAT
", R15: " POINTER_FORMAT
"\n",
189 static_cast<size_t>(exc->ContextRecord->R13),
static_cast<size_t>(exc->ContextRecord->R14),
190 static_cast<size_t>(exc->ContextRecord->R15));
191 LOG_DYNAMIC_TEXT(
"RSP: " POINTER_FORMAT
", RIP: " POINTER_FORMAT
"\n",
192 static_cast<size_t>(exc->ContextRecord->Rsp),
static_cast<size_t>(exc->ContextRecord->Rip));
193 #elif OC_MACHINE == OC_MACHINE_X86
194 LOG_STATIC_TEXT(
"\nProcessor registers (x86):\n");
195 LOG_DYNAMIC_TEXT(
"EAX: " POINTER_FORMAT
", EBX: " POINTER_FORMAT
", ECX: " POINTER_FORMAT
", EDX: " POINTER_FORMAT
"\n",
196 static_cast<size_t>(exc->ContextRecord->Eax),
static_cast<size_t>(exc->ContextRecord->Ebx),
197 static_cast<size_t>(exc->ContextRecord->Ecx),
static_cast<size_t>(exc->ContextRecord->Edx));
198 LOG_DYNAMIC_TEXT(
"ESI: " POINTER_FORMAT
", EDI: " POINTER_FORMAT
"\n",
199 static_cast<size_t>(exc->ContextRecord->Esi),
static_cast<size_t>(exc->ContextRecord->Edi));
200 LOG_DYNAMIC_TEXT(
"EBP: " POINTER_FORMAT
", ESP: " POINTER_FORMAT
", EIP: " POINTER_FORMAT
"\n",
201 static_cast<size_t>(exc->ContextRecord->Ebp),
static_cast<size_t>(exc->ContextRecord->Esp),
202 static_cast<size_t>(exc->ContextRecord->Eip));
204 #if OC_MACHINE == OC_MACHINE_X64 || OC_MACHINE == OC_MACHINE_X86
205 LOG_DYNAMIC_TEXT(
"EFLAGS: 0x%08x (%c%c%c%c%c%c%c)\n",
static_cast<unsigned int>(exc->ContextRecord->EFlags),
206 exc->ContextRecord->EFlags & 0x800 ?
'O' :
'.',
207 exc->ContextRecord->EFlags & 0x400 ?
'D' :
'.',
208 exc->ContextRecord->EFlags & 0x80 ?
'S' :
'.',
209 exc->ContextRecord->EFlags & 0x40 ?
'Z' :
'.',
210 exc->ContextRecord->EFlags & 0x10 ?
'A' :
'.',
211 exc->ContextRecord->EFlags & 0x4 ?
'P' :
'.',
212 exc->ContextRecord->EFlags & 0x1 ?
'C' :
'.');
216 LOG_STATIC_TEXT(
"\nStack contents:\n");
217 MEMORY_BASIC_INFORMATION stack_info;
218 intptr_t stack_pointer =
219 #if OC_MACHINE == OC_MACHINE_X64
220 exc->ContextRecord->Rsp
221 #elif OC_MACHINE == OC_MACHINE_X86
222 exc->ContextRecord->Esp
225 if (VirtualQuery(
reinterpret_cast<LPCVOID
>(stack_pointer), &stack_info,
sizeof(stack_info)))
227 intptr_t stack_base =
reinterpret_cast<intptr_t
>(stack_info.BaseAddress);
228 intptr_t dump_min = std::max<intptr_t>(stack_base, (stack_pointer - 256) & ~0xF);
229 intptr_t dump_max = std::min<intptr_t>(stack_base + stack_info.RegionSize, (stack_pointer + 256) | 0xF);
231 for (intptr_t dump_row_base = dump_min & ~0xF; dump_row_base < dump_max; dump_row_base += 0x10)
233 LOG_DYNAMIC_TEXT(POINTER_FORMAT
": ", dump_row_base);
235 for (intptr_t dump_row_cursor = dump_row_base; dump_row_cursor < dump_row_base + 16; ++dump_row_cursor)
237 if (dump_row_cursor < dump_min || dump_row_cursor > dump_max)
238 LOG_STATIC_TEXT(
" ");
240 LOG_DYNAMIC_TEXT(
"%02x ", (
unsigned int)*
reinterpret_cast<unsigned char*
>(dump_row_cursor));
242 LOG_STATIC_TEXT(
" ");
244 for (intptr_t dump_row_cursor = dump_row_base; dump_row_cursor < dump_row_base + 16; ++dump_row_cursor)
246 if (dump_row_cursor < dump_min || dump_row_cursor > dump_max)
247 LOG_STATIC_TEXT(
" ");
250 unsigned char c = *
reinterpret_cast<unsigned char*
>(dump_row_cursor);
251 if (c < 0x20 || (c > 0x7e && c < 0xa1))
252 LOG_STATIC_TEXT(
".");
254 LOG_DYNAMIC_TEXT(
"%c",
static_cast<char>(c));
257 LOG_STATIC_TEXT(
"\n");
262 LOG_STATIC_TEXT(
"[Failed to access stack memory]\n");
266 SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
267 HANDLE process = GetCurrentProcess();
268 if (SymInitialize(process,
nullptr,
true))
270 LOG_STATIC_TEXT(
"\nStack trace:\n");
271 auto frame = STACKFRAME64();
273 CONTEXT context = *exc->ContextRecord;
275 frame.AddrPC.Mode = AddrModeFlat;
276 frame.AddrStack.Mode = AddrModeFlat;
277 frame.AddrFrame.Mode = AddrModeFlat;
278 #if OC_MACHINE == OC_MACHINE_X64
279 image_type = IMAGE_FILE_MACHINE_AMD64;
280 frame.AddrPC.Offset = context.Rip;
281 frame.AddrStack.Offset = context.Rsp;
283 frame.AddrFrame.Offset = context.Rbp;
284 #elif OC_MACHINE == OC_MACHINE_X86
285 image_type = IMAGE_FILE_MACHINE_I386;
286 frame.AddrPC.Offset = context.Eip;
287 frame.AddrStack.Offset = context.Esp;
288 frame.AddrFrame.Offset = context.Ebp;
291 SYMBOL_INFO *symbol =
reinterpret_cast<SYMBOL_INFO*
>(SymbolBuffer);
292 static_assert(DumpBufferSize >=
sizeof(*symbol),
"SYMBOL_INFO too large to fit into buffer");
293 IMAGEHLP_MODULE64 *module =
reinterpret_cast<IMAGEHLP_MODULE64*
>(SymbolBuffer);
294 static_assert(DumpBufferSize >=
sizeof(*module),
"IMAGEHLP_MODULE64 too large to fit into buffer");
295 IMAGEHLP_LINE64 *line =
reinterpret_cast<IMAGEHLP_LINE64*
>(SymbolBuffer);
296 static_assert(DumpBufferSize >=
sizeof(*line),
"IMAGEHLP_LINE64 too large to fit into buffer");
297 int frame_number = 0;
298 while (StackWalk64(image_type, process, GetCurrentThread(), &frame, &context,
nullptr, SymFunctionTableAccess64, SymGetModuleBase64,
nullptr))
300 LOG_DYNAMIC_TEXT(
"#%3d ", frame_number);
301 module->SizeOfStruct =
sizeof(*module);
302 DWORD64 image_base = 0;
303 if (SymGetModuleInfo64(process, frame.AddrPC.Offset, module))
305 LOG_DYNAMIC_TEXT(
"%s", module->ModuleName);
306 image_base = module->BaseOfImage;
309 symbol->MaxNameLen = DumpBufferSize -
sizeof(*symbol);
310 symbol->SizeOfStruct =
sizeof(*symbol);
311 if (SymFromAddr(process, frame.AddrPC.Offset, &disp64, symbol))
313 LOG_DYNAMIC_TEXT(
"!%s+%#lx", symbol->Name,
static_cast<long>(disp64));
315 else if (image_base > 0)
317 LOG_DYNAMIC_TEXT(
"+%#lx",
static_cast<long>(frame.AddrPC.Offset - image_base));
321 LOG_DYNAMIC_TEXT(
"%#lx",
static_cast<long>(frame.AddrPC.Offset));
324 line->SizeOfStruct =
sizeof(*line);
325 if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &disp, line))
327 LOG_DYNAMIC_TEXT(
" [%s @ %u]", line->FileName,
static_cast<unsigned int>(line->LineNumber));
329 LOG_STATIC_TEXT(
"\n");
336 LOG_STATIC_TEXT(
"[Stack trace not available: failed to initialize Debugging Help Library]\n");
341 while((snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)) == INVALID_HANDLE_VALUE)
342 if (GetLastError() != ERROR_BAD_LENGTH)
break;
343 if (snapshot != INVALID_HANDLE_VALUE)
345 LOG_STATIC_TEXT(
"\nLoaded modules:\n");
346 MODULEENTRY32 *module =
reinterpret_cast<MODULEENTRY32*
>(SymbolBuffer);
347 static_assert(DumpBufferSize >=
sizeof(*module),
"MODULEENTRY32 too large to fit into buffer");
348 module->dwSize =
sizeof(*module);
349 for (BOOL success = Module32First(snapshot, module); success; success = Module32Next(snapshot, module))
351 LOG_DYNAMIC_TEXT(
"%32ls loaded at " POINTER_FORMAT
" - " POINTER_FORMAT
" (%ls)\n", module->szModule,
352 reinterpret_cast<size_t>(module->modBaseAddr),
reinterpret_cast<size_t>(module->modBaseAddr + module->modBaseSize),
355 CloseHandle(snapshot);
357 #undef POINTER_FORMAT_SUFFIX
358 #undef POINTER_FORMAT
360 #undef LOG_DYNAMIC_TEXT
361 #undef LOG_STATIC_TEXT
364 LONG WINAPI GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
368 MDST_BuildId = LastReservedStream + 1
371 if (!FirstCrash)
return EXCEPTION_EXECUTE_HANDLER;
376 wchar_t *filename =
reinterpret_cast<wchar_t*
>(DumpBuffer);
377 const size_t filename_buffer_size = DumpBufferSize /
sizeof(wchar_t);
381 assert (GetLastError() == ERROR_NO_UNICODE_TRANSLATION);
383 DWORD temp_size = GetTempPath(filename_buffer_size, filename);
384 if (temp_size == 0 || temp_size > filename_buffer_size)
387 temp_size = GetCurrentDirectory(filename_buffer_size, filename);
388 if (temp_size == 0 || temp_size > filename_buffer_size)
396 HANDLE file = INVALID_HANDLE_VALUE;
398 if (filename[0] != L
'\0')
401 const wchar_t tmpl[] = TEXT(C4ENGINENICK) L
"-crash-YYYY-MM-DD-HH-MM-SS.dmp";
402 size_t path_len = wcslen(filename);
403 if (path_len +
sizeof(tmpl) /
sizeof(*tmpl) > filename_buffer_size)
412 if (filename[path_len - 1] != L
'\\')
414 filename[path_len] = L
'\\';
415 filename[++path_len] = L
'\0';
419 wsprintf(&filename[path_len], L
"%s-crash-%04d-%02d-%02d-%02d-%02d-%02d.dmp",
420 TEXT(C4ENGINENICK), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
424 if (filename[0] != L
'\0')
426 file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE,
nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
nullptr);
428 if (file == INVALID_HANDLE_VALUE)
434 SafeTextDump(pExceptionPointers,
GetLogFD(), filename);
436 if (file != INVALID_HANDLE_VALUE)
438 auto user_stream_info = MINIDUMP_USER_STREAM_INFORMATION();
439 auto user_stream = MINIDUMP_USER_STREAM();
440 char build_id[] = OC_BUILD_ID;
441 if (OC_BUILD_ID[0] !=
'\0')
443 user_stream.Type = MDST_BuildId;
444 user_stream.Buffer = build_id;
445 user_stream.BufferSize =
sizeof(build_id) - 1;
446 user_stream_info.UserStreamCount = 1;
447 user_stream_info.UserStreamArray = &user_stream;
450 MINIDUMP_EXCEPTION_INFORMATION ExpParam;
451 ExpParam.ThreadId = GetCurrentThreadId();
452 ExpParam.ExceptionPointers = pExceptionPointers;
453 ExpParam.ClientPointers =
true;
454 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
455 file, MiniDumpNormal, &ExpParam, &user_stream_info,
nullptr);
460 return EXCEPTION_EXECUTE_HANDLER;
470 #ifdef USE_WIDE_ASSERT
471 typedef void (__cdecl *ASSERT_FUNC)(
const wchar_t *,
const wchar_t *, unsigned);
472 const ASSERT_FUNC assert_func =
475 typedef void (__cdecl *ASSERT_FUNC)(
const char *,
const char *, int);
476 const ASSERT_FUNC assert_func =
479 unsigned char trampoline[] = {
480 #if OC_MACHINE == OC_MACHINE_X64
482 0x48 , 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
485 #elif OC_MACHINE == OC_MACHINE_X86
489 0xB8, 0xCC, 0xCC, 0xCC, 0xCC,
494 unsigned char trampoline_backup[
sizeof(trampoline)];
495 void HookAssert(ASSERT_FUNC hook)
498 memcpy(trampoline + 2, (
void*)&hook,
sizeof(
void*));
500 DWORD old_protect = 0;
501 if (!VirtualProtect((LPVOID)assert_func,
sizeof(trampoline), PAGE_EXECUTE_READWRITE, &old_protect))
504 memcpy(trampoline_backup, (
void*)assert_func,
sizeof(trampoline_backup));
505 memcpy((
void*)assert_func, trampoline,
sizeof(trampoline));
507 VirtualProtect((LPVOID)assert_func,
sizeof(trampoline), old_protect, &old_protect);
509 FlushInstructionCache(GetCurrentProcess(), (LPCVOID)assert_func,
sizeof(trampoline));
513 DWORD old_protect = 0;
514 if (!VirtualProtect((LPVOID)assert_func,
sizeof(trampoline_backup), PAGE_EXECUTE_READWRITE, &old_protect))
518 memcpy((
void*)assert_func, trampoline_backup,
sizeof(trampoline_backup));
519 VirtualProtect((LPVOID)assert_func,
sizeof(trampoline_backup), old_protect, &old_protect);
520 FlushInstructionCache(GetCurrentProcess(), (LPCVOID)assert_func,
sizeof(trampoline_backup));
523 struct dump_thread_t {
525 #ifdef USE_WIDE_ASSERT
534 static DWORD WINAPI dump_thread(LPVOID t)
536 dump_thread_t *data =
static_cast<dump_thread_t*
>(t);
539 if (SuspendThread(data->thread) == -1)
543 auto ctx = CONTEXT();
545 #define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | \
546 CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS)
548 ctx.ContextFlags = CONTEXT_ALL;
549 BOOL result = GetThreadContext(data->thread, &ctx);
552 auto erec = EXCEPTION_RECORD();
553 erec.ExceptionCode = STATUS_ASSERTION_FAILURE;
554 erec.ExceptionFlags = 0L;
555 erec.ExceptionInformation[0] = (ULONG_PTR)data->expression;
556 erec.ExceptionInformation[1] = (ULONG_PTR)data->file;
557 erec.ExceptionInformation[2] = (ULONG_PTR)data->line;
558 erec.NumberParameters = 3;
560 erec.ExceptionAddress = (LPVOID)
561 #if OC_MACHINE == OC_MACHINE_X64
563 #elif OC_MACHINE == OC_MACHINE_X86
569 EXCEPTION_POINTERS eptr;
570 eptr.ContextRecord = &ctx;
571 eptr.ExceptionRecord = &erec;
575 SafeTextDump(&eptr,
GetLogFD(),
nullptr);
578 if (ResumeThread(data->thread) == -1)
584 #ifdef USE_WIDE_ASSERT
585 void __cdecl assertion_handler(
const wchar_t *expression,
const wchar_t *file,
unsigned line)
587 void __cdecl assertion_handler(
const char *expression,
const char *file,
int line)
592 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &this_thread, 0, FALSE, DUPLICATE_SAME_ACCESS);
593 dump_thread_t dump_thread_data = {
595 expression, file, line
597 HANDLE ctx_thread = CreateThread(
nullptr, 0L, &dump_thread, &dump_thread_data, 0L,
nullptr);
598 WaitForSingleObject(ctx_thread,
INFINITE);
599 CloseHandle(this_thread);
600 CloseHandle(ctx_thread);
604 assert_func(expression, file, line);
606 HookAssert(&assertion_handler);
618 typedef BOOL (WINAPI *SetProcessUserModeExceptionPolicyProc)(
DWORD);
619 typedef BOOL (WINAPI *GetProcessUserModeExceptionPolicyProc)(LPDWORD);
620 HMODULE kernel32 = LoadLibrary(TEXT(
"kernel32"));
621 const SetProcessUserModeExceptionPolicyProc SetProcessUserModeExceptionPolicy =
622 (SetProcessUserModeExceptionPolicyProc)GetProcAddress(kernel32,
"SetProcessUserModeExceptionPolicy");
623 const GetProcessUserModeExceptionPolicyProc GetProcessUserModeExceptionPolicy =
624 (GetProcessUserModeExceptionPolicyProc)GetProcAddress(kernel32,
"GetProcessUserModeExceptionPolicy");
625 #ifndef PROCESS_CALLBACK_FILTER_ENABLED
626 # define PROCESS_CALLBACK_FILTER_ENABLED 0x1
628 if (SetProcessUserModeExceptionPolicy && GetProcessUserModeExceptionPolicy)
631 if (GetProcessUserModeExceptionPolicy(&flags))
633 SetProcessUserModeExceptionPolicy(flags & ~PROCESS_CALLBACK_FILTER_ENABLED);
636 FreeLibrary(kernel32);
638 SetUnhandledExceptionFilter(GenerateDump);
642 if (!IsDebuggerPresent())
643 HookAssert(&assertion_handler);
void InstallCrashHandler()
char UserDataPath[CFG_MaxString+1]