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]