12 #include "../../stdafx.h" 
   13 #include "../../crashlog.h" 
   15 #include "../../core/alloc_func.hpp" 
   16 #include "../../core/math_func.hpp" 
   17 #include "../../string_func.h" 
   18 #include "../../fileio_func.h" 
   19 #include "../../strings_func.h" 
   20 #include "../../gamelog.h" 
   21 #include "../../saveload/saveload.h" 
   22 #include "../../video/video_driver.hpp" 
   27 #include "../../safeguards.h" 
   29 static const uint MAX_SYMBOL_LEN = 512;
 
   30 static const uint MAX_FRAMES     = 64;
 
   34 #define PRINTF_PTR "0x%016IX" 
   36 #define PRINTF_PTR "0x%08X" 
   44   EXCEPTION_POINTERS *
ep;
 
   46    char *LogOSVersion(
char *buffer, 
const char *last) 
const;
 
   47    char *LogError(
char *buffer, 
const char *last, 
const char *
message) 
const;
 
   48    char *LogStacktrace(
char *buffer, 
const char *last) 
const;
 
   49    char *LogRegisters(
char *buffer, 
const char *last) 
const;
 
   50    char *LogModules(
char *buffer, 
const char *last) 
const;
 
   53    int WriteCrashDump(
char *filename, 
const char *filename_last) 
const;
 
   54   char *AppendDecodedStacktrace(
char *buffer, 
const char *last) 
const;
 
   56   char *AppendDecodedStacktrace(
char *buffer, 
const char *last)
 const { 
return buffer; }
 
   89  char *CrashLogWindows::LogOSVersion(
char *buffer, 
const char *last)
 const 
   92   os.dwOSVersionInfoSize = 
sizeof(os);
 
   95   return buffer + 
seprintf(buffer, last,
 
   98       " Release:  %d.%d.%d (%s)\n",
 
   99       (
int)os.dwMajorVersion,
 
  100       (
int)os.dwMinorVersion,
 
  101       (
int)os.dwBuildNumber,
 
  107  char *CrashLogWindows::LogError(
char *buffer, 
const char *last, 
const char *message)
 const 
  109   return buffer + 
seprintf(buffer, last,
 
  113       " Location:  %.16IX\n" 
  118       (
int)
ep->ExceptionRecord->ExceptionCode,
 
  119       (
size_t)
ep->ExceptionRecord->ExceptionAddress,
 
  120       message == NULL ? 
"<none>" : message
 
  127   SYSTEMTIME file_time;
 
  130 static uint32 *_crc_table;
 
  132 static void MakeCRCTable(uint32 *table)
 
  134   uint32 crc, poly = 0xEDB88320L;
 
  140   for (i = 0; i != 256; i++) {
 
  142     for (j = 8; j != 0; j--) {
 
  143       crc = (crc & 1 ? (crc >> 1) ^ poly : crc >> 1);
 
  149 static uint32 CalcCRC(byte *data, uint size, uint32 crc)
 
  151   for (; size > 0; size--) {
 
  152     crc = ((crc >> 8) & 0x00FFFFFF) ^ _crc_table[(crc ^ *data++) & 0xFF];
 
  157 static void GetFileInfo(
DebugFileInfo *dfi, 
const TCHAR *filename)
 
  160   memset(dfi, 0, 
sizeof(*dfi));
 
  162   file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
 
  163   if (file != INVALID_HANDLE_VALUE) {
 
  168     uint32 crc = (uint32)-1;
 
  171       if (ReadFile(file, buffer, 
sizeof(buffer), &numread, NULL) == 0 || numread == 0) {
 
  175       crc = CalcCRC(buffer, numread, crc);
 
  177     dfi->size = filesize;
 
  178     dfi->crc32 = crc ^ (uint32)-1;
 
  180     if (GetFileTime(file, NULL, NULL, &write_time)) {
 
  181       FileTimeToSystemTime(&write_time, &dfi->file_time);
 
  188 static char *PrintModuleInfo(
char *output, 
const char *last, HMODULE mod)
 
  190   TCHAR buffer[MAX_PATH];
 
  193   GetModuleFileName(mod, buffer, MAX_PATH);
 
  194   GetFileInfo(&dfi, buffer);
 
  195   output += 
seprintf(output, last, 
" %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n",
 
  201     dfi.file_time.wMonth,
 
  204     dfi.file_time.wMinute,
 
  205     dfi.file_time.wSecond
 
  210  char *CrashLogWindows::LogModules(
char *output, 
const char *last)
 const 
  212   MakeCRCTable(
AllocaM(uint32, 256));
 
  213   BOOL (WINAPI *EnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD);
 
  215   output += 
seprintf(output, last, 
"Module information:\n");
 
  217   if (
LoadLibraryList((Function*)&EnumProcessModules, 
"psapi.dll\0EnumProcessModules\0\0")) {
 
  218     HMODULE modules[100];
 
  222     HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
 
  224       res = EnumProcessModules(proc, modules, 
sizeof(modules), &needed);
 
  227         size_t count = 
min(needed / 
sizeof(HMODULE), 
lengthof(modules));
 
  229         for (
size_t i = 0; i != count; i++) output = PrintModuleInfo(output, last, modules[i]);
 
  230         return output + 
seprintf(output, last, 
"\n");
 
  234   output = PrintModuleInfo(output, last, NULL);
 
  235   return output + 
seprintf(output, last, 
"\n");
 
  238  char *CrashLogWindows::LogRegisters(
char *buffer, 
const char *last)
 const 
  240   buffer += 
seprintf(buffer, last, 
"Registers:\n");
 
  243     " RAX: %.16I64X RBX: %.16I64X RCX: %.16I64X RDX: %.16I64X\n" 
  244     " RSI: %.16I64X RDI: %.16I64X RBP: %.16I64X RSP: %.16I64X\n" 
  245     " R8:  %.16I64X R9:  %.16I64X R10: %.16I64X R11: %.16I64X\n" 
  246     " R12: %.16I64X R13: %.16I64X R14: %.16I64X R15: %.16I64X\n" 
  247     " RIP: %.16I64X EFLAGS: %.8lX\n",
 
  248     ep->ContextRecord->Rax,
 
  249     ep->ContextRecord->Rbx,
 
  250     ep->ContextRecord->Rcx,
 
  251     ep->ContextRecord->Rdx,
 
  252     ep->ContextRecord->Rsi,
 
  253     ep->ContextRecord->Rdi,
 
  254     ep->ContextRecord->Rbp,
 
  255     ep->ContextRecord->Rsp,
 
  256     ep->ContextRecord->R8,
 
  257     ep->ContextRecord->R9,
 
  258     ep->ContextRecord->R10,
 
  259     ep->ContextRecord->R11,
 
  260     ep->ContextRecord->R12,
 
  261     ep->ContextRecord->R13,
 
  262     ep->ContextRecord->R14,
 
  263     ep->ContextRecord->R15,
 
  264     ep->ContextRecord->Rip,
 
  265     ep->ContextRecord->EFlags
 
  269     " EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\n" 
  270     " ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\n" 
  271     " EIP: %.8X EFLAGS: %.8X\n",
 
  272     (
int)
ep->ContextRecord->Eax,
 
  273     (
int)
ep->ContextRecord->Ebx,
 
  274     (
int)
ep->ContextRecord->Ecx,
 
  275     (
int)
ep->ContextRecord->Edx,
 
  276     (
int)
ep->ContextRecord->Esi,
 
  277     (
int)
ep->ContextRecord->Edi,
 
  278     (
int)
ep->ContextRecord->Ebp,
 
  279     (
int)
ep->ContextRecord->Esp,
 
  280     (
int)
ep->ContextRecord->Eip,
 
  281     (
int)
ep->ContextRecord->EFlags
 
  285   buffer += 
seprintf(buffer, last, 
"\n Bytes at instruction pointer:\n");
 
  287   byte *b = (byte*)
ep->ContextRecord->Rip;
 
  289   byte *b = (byte*)
ep->ContextRecord->Eip;
 
  291   for (
int i = 0; i != 24; i++) {
 
  292     if (IsBadReadPtr(b, 1)) {
 
  293       buffer += 
seprintf(buffer, last, 
" ??"); 
 
  295       buffer += 
seprintf(buffer, last, 
" %.2X", *b);
 
  299   return buffer + 
seprintf(buffer, last, 
"\n\n");
 
  302  char *CrashLogWindows::LogStacktrace(
char *buffer, 
const char *last)
 const 
  304   buffer += 
seprintf(buffer, last, 
"Stack trace:\n");
 
  306   uint32 *b = (uint32*)
ep->ContextRecord->Rsp;
 
  308   uint32 *b = (uint32*)
ep->ContextRecord->Esp;
 
  310   for (
int j = 0; j != 24; j++) {
 
  311     for (
int i = 0; i != 8; i++) {
 
  312       if (IsBadReadPtr(b, 
sizeof(uint32))) {
 
  313         buffer += 
seprintf(buffer, last, 
" ????????"); 
 
  315         buffer += 
seprintf(buffer, last, 
" %.8X", *b);
 
  319     buffer += 
seprintf(buffer, last, 
"\n");
 
  321   return buffer + 
seprintf(buffer, last, 
"\n");
 
  324 #if defined(_MSC_VER) 
  325 #pragma warning(disable:4091) 
  327 #pragma warning(default:4091) 
  329 char *CrashLogWindows::AppendDecodedStacktrace(
char *buffer, 
const char *last)
 const 
  332   static const char dbg_import[] =
 
  338     M("SymFunctionTableAccess64")
 
  339     M("SymGetModuleBase64")
 
  340     M("SymGetModuleInfo64")
 
  341     M("SymGetSymFromAddr64")
 
  342     M("SymGetLineFromAddr64")
 
  348     BOOL (WINAPI * pSymInitialize)(HANDLE, PCSTR, BOOL);
 
  349     BOOL (WINAPI * pSymSetOptions)(DWORD);
 
  350     BOOL (WINAPI * pSymCleanup)(HANDLE);
 
  351     BOOL (WINAPI * pStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
 
  352     PVOID (WINAPI * pSymFunctionTableAccess64)(HANDLE, DWORD64);
 
  353     DWORD64 (WINAPI * pSymGetModuleBase64)(HANDLE, DWORD64);
 
  354     BOOL (WINAPI * pSymGetModuleInfo64)(HANDLE, DWORD64, PIMAGEHLP_MODULE64);
 
  355     BOOL (WINAPI * pSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
 
  356     BOOL (WINAPI * pSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
 
  359   buffer += 
seprintf(buffer, last, 
"\nDecoded stack trace:\n");
 
  364     HANDLE hCur = GetCurrentProcess();
 
  365     proc.pSymInitialize(hCur, NULL, TRUE);
 
  367     proc.pSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_UNDNAME);
 
  371     memset(&frame, 0, 
sizeof(frame));
 
  373     frame.AddrPC.Offset = 
ep->ContextRecord->Rip;
 
  374     frame.AddrFrame.Offset = 
ep->ContextRecord->Rbp;
 
  375     frame.AddrStack.Offset = 
ep->ContextRecord->Rsp;
 
  377     frame.AddrPC.Offset = 
ep->ContextRecord->Eip;
 
  378     frame.AddrFrame.Offset = 
ep->ContextRecord->Ebp;
 
  379     frame.AddrStack.Offset = 
ep->ContextRecord->Esp;
 
  381     frame.AddrPC.Mode = AddrModeFlat;
 
  382     frame.AddrFrame.Mode = AddrModeFlat;
 
  383     frame.AddrStack.Mode = AddrModeFlat;
 
  387     memcpy(&ctx, 
ep->ContextRecord, 
sizeof(ctx));
 
  390     IMAGEHLP_SYMBOL64 *sym_info = (IMAGEHLP_SYMBOL64*)alloca(
sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMBOL_LEN - 1);
 
  391     sym_info->SizeOfStruct = 
sizeof(IMAGEHLP_SYMBOL64);
 
  392     sym_info->MaxNameLength = MAX_SYMBOL_LEN;
 
  395     for (uint num = 0; num < MAX_FRAMES; num++) {
 
  396       if (!proc.pStackWalk64(
 
  398         IMAGE_FILE_MACHINE_AMD64,
 
  400         IMAGE_FILE_MACHINE_I386,
 
  402         hCur, GetCurrentThread(), &frame, &ctx, NULL, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, NULL)) 
break;
 
  404       if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
 
  405         buffer += 
seprintf(buffer, last, 
" <infinite loop>\n");
 
  410       const char *mod_name = 
"???";
 
  412       IMAGEHLP_MODULE64 module;
 
  413       module.SizeOfStruct = 
sizeof(module);
 
  414       if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) {
 
  415         mod_name = module.ModuleName;
 
  419       buffer += 
seprintf(buffer, last, 
"[%02d] %-20s " PRINTF_PTR, num, mod_name, frame.AddrPC.Offset);
 
  423       if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
 
  424         buffer += 
seprintf(buffer, last, 
" %s + %I64u", sym_info->Name, offset);
 
  427         IMAGEHLP_LINE64 line;
 
  428         line.SizeOfStruct = 
sizeof(IMAGEHLP_LINE64);
 
  429         if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
 
  430           buffer += 
seprintf(buffer, last, 
" (%s:%d)", line.FileName, line.LineNumber);
 
  433       buffer += 
seprintf(buffer, last, 
"\n");
 
  436     proc.pSymCleanup(hCur);
 
  439   return buffer + 
seprintf(buffer, last, 
"\n*** End of additional info ***\n");
 
  445   HMODULE dbghelp = LoadLibrary(_T(
"dbghelp.dll"));
 
  446   if (dbghelp != NULL) {
 
  447     typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE,
 
  449         CONST PMINIDUMP_EXCEPTION_INFORMATION,
 
  450         CONST PMINIDUMP_USER_STREAM_INFORMATION,
 
  451         CONST PMINIDUMP_CALLBACK_INFORMATION);
 
  452     MiniDumpWriteDump_t funcMiniDumpWriteDump = (MiniDumpWriteDump_t)GetProcAddress(dbghelp, 
"MiniDumpWriteDump");
 
  453     if (funcMiniDumpWriteDump != NULL) {
 
  455       HANDLE file  = CreateFile(
OTTD2FS(filename), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
 
  456       HANDLE proc  = GetCurrentProcess();
 
  457       DWORD procid = GetCurrentProcessId();
 
  458       MINIDUMP_EXCEPTION_INFORMATION mdei;
 
  459       MINIDUMP_USER_STREAM userstream;
 
  460       MINIDUMP_USER_STREAM_INFORMATION musi;
 
  462       userstream.Type        = LastReservedStream + 1;
 
  463       userstream.Buffer      = (
void*)this->
crashlog;
 
  464       userstream.BufferSize  = (ULONG)strlen(this->
crashlog) + 1;
 
  466       musi.UserStreamCount   = 1;
 
  467       musi.UserStreamArray   = &userstream;
 
  469       mdei.ThreadId = GetCurrentThreadId();
 
  470       mdei.ExceptionPointers  = 
ep;
 
  471       mdei.ClientPointers     = 
false;
 
  473       funcMiniDumpWriteDump(proc, procid, file, MiniDumpWithDataSegs, &mdei, &musi, NULL);
 
  478     FreeLibrary(dbghelp);
 
  484 extern bool CloseConsoleLogIfActive();
 
  485 static void ShowCrashlogWindow();
 
  493 static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
 
  495   if (CrashLogWindows::current != NULL) {
 
  501     static const TCHAR _emergency_crash[] =
 
  502       _T(
"A serious fault condition occurred in the game. The game will shut down.\n")
 
  503       _T(
"As you loaded an emergency savegame no crash information will be generated.\n");
 
  504     MessageBox(NULL, _emergency_crash, _T(
"Fatal Application Failure"), MB_ICONERROR);
 
  509     static const TCHAR _saveload_crash[] =
 
  510       _T(
"A serious fault condition occurred in the game. The game will shut down.\n")
 
  511       _T(
"As you loaded an savegame for which you do not have the required NewGRFs\n")
 
  512       _T(
"no crash information will be generated.\n");
 
  513     MessageBox(NULL, _saveload_crash, _T(
"Fatal Application Failure"), MB_ICONERROR);
 
  518   CrashLogWindows::current = log;
 
  526   CloseConsoleLogIfActive();
 
  530     ep->ContextRecord->Rip = (DWORD64)ShowCrashlogWindow;
 
  531     ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
 
  533     ep->ContextRecord->Eip = (DWORD)ShowCrashlogWindow;
 
  534     ep->ContextRecord->Esp = (DWORD)_safe_esp;
 
  536     return EXCEPTION_CONTINUE_EXECUTION;
 
  540   return EXCEPTION_EXECUTE_HANDLER;
 
  543 static void CDECL CustomAbort(
int signal)
 
  545   RaiseException(0xE1212012, 0, 0, NULL);
 
  552   RtlCaptureContext(&ctx);
 
  558   _safe_esp = (
void *)(ctx.Rsp - 8);
 
  560 #if defined(_MSC_VER) 
  565   asm(
"movl %esp, __safe_esp");
 
  570   signal(SIGABRT, CustomAbort);
 
  571 #if defined(_MSC_VER) 
  573   _set_abort_behavior(0, _WRITE_ABORT_MSG);
 
  575   SetUnhandledExceptionFilter(ExceptionHandler);
 
  580 static bool _expanded;
 
  582 static const TCHAR _crash_desc[] =
 
  583   _T(
"A serious fault condition occurred in the game. The game will shut down.\n")
 
  584   _T("Please send the crash information and the crash.dmp file (if any) to the developers.\n")
 
  585   _T("This will greatly help debugging. The correct place to do this is http:
 
  586   _T("The information contained in the report is displayed below.\n")
 
  587   _T("Press \"Emergency save\" to attempt saving the game. Generated file(s):\n")
 
  590 static const TCHAR _save_succeeded[] =
 
  591   _T("Emergency save succeeded.\nIts location is '%s'.\n")
 
  592   _T("Be aware that critical parts of the internal game state may have become ")
 
  593   _T("corrupted. The saved game is not guaranteed to work.");
 
  595 static const TCHAR * const _expand_texts[] = {_T(
"S&how report >>"), _T(
"&Hide report <<") };
 
  597 static void SetWndSize(HWND wnd, 
int mode)
 
  601   GetWindowRect(wnd, &r);
 
  602   SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
 
  605     GetWindowRect(GetDlgItem(wnd, 11), &r2);
 
  606     int offs = r2.bottom - r2.top + 10;
 
  607     if (mode == 0) offs = -offs;
 
  608     SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
 
  609       r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
 
  611     SetWindowPos(wnd, HWND_TOPMOST,
 
  612       (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
 
  613       (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
 
  622 static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
  625     case WM_INITDIALOG: {
 
  628       TCHAR crash_msgW[
lengthof(CrashLogWindows::current->crashlog)];
 
  630       const char *unix_nl = CrashLogWindows::current->
crashlog;
 
  631       char dos_nl[
lengthof(CrashLogWindows::current->crashlog)];
 
  634       while ((c = Utf8Consume(&unix_nl)) && p < 
lastof(dos_nl) - 4) { 
 
  641       size_t len = _tcslen(_crash_desc) + 2;
 
  642       len += _tcslen(
OTTD2FS(CrashLogWindows::current->crashlog_filename)) + 2;
 
  643       len += _tcslen(
OTTD2FS(CrashLogWindows::current->crashdump_filename)) + 2;
 
  644       len += _tcslen(
OTTD2FS(CrashLogWindows::current->screenshot_filename)) + 1;
 
  646       TCHAR *text = 
AllocaM(TCHAR, len);
 
  647       _sntprintf(text, len, _crash_desc, 
OTTD2FS(CrashLogWindows::current->crashlog_filename));
 
  648       if (
OTTD2FS(CrashLogWindows::current->crashdump_filename)[0] != _T(
'\0')) {
 
  649         _tcscat(text, _T(
"\n"));
 
  650         _tcscat(text, 
OTTD2FS(CrashLogWindows::current->crashdump_filename));
 
  652       if (
OTTD2FS(CrashLogWindows::current->screenshot_filename)[0] != _T(
'\0')) {
 
  653         _tcscat(text, _T(
"\n"));
 
  654         _tcscat(text, 
OTTD2FS(CrashLogWindows::current->screenshot_filename));
 
  657       SetDlgItemText(wnd, 10, text);
 
  659       SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
 
  668           char filename[MAX_PATH];
 
  669           if (CrashLogWindows::current->WriteSavegame(filename, 
lastof(filename))) {
 
  670             size_t len = _tcslen(_save_succeeded) + _tcslen(
OTTD2FS(filename)) + 1;
 
  671             TCHAR *text = 
AllocaM(TCHAR, len);
 
  672             _sntprintf(text, len, _save_succeeded, 
OTTD2FS(filename));
 
  673             MessageBox(wnd, text, _T(
"Save successful"), MB_ICONINFORMATION);
 
  675             MessageBox(wnd, _T(
"Save failed"), _T(
"Save failed"), MB_ICONINFORMATION);
 
  680           SetWndSize(wnd, _expanded);
 
  692 static void ShowCrashlogWindow()
 
  695   ShowWindow(GetActiveWindow(), FALSE);
 
  696   DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(100), NULL, CrashDialogFunc);