OpenTTD Source  1.11.2
crashlog_win.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "../../stdafx.h"
11 #include "../../crashlog.h"
12 #include "win32.h"
13 #include "../../core/alloc_func.hpp"
14 #include "../../core/math_func.hpp"
15 #include "../../string_func.h"
16 #include "../../fileio_func.h"
17 #include "../../strings_func.h"
18 #include "../../gamelog.h"
19 #include "../../saveload/saveload.h"
20 #include "../../video/video_driver.hpp"
21 
22 #include <windows.h>
23 #include <mmsystem.h>
24 #include <signal.h>
25 
26 #include "../../safeguards.h"
27 
28 /* printf format specification for 32/64-bit addresses. */
29 #ifdef _M_AMD64
30 #define PRINTF_PTR "0x%016IX"
31 #else
32 #define PRINTF_PTR "0x%08X"
33 #endif
34 
38 class CrashLogWindows : public CrashLog {
40  EXCEPTION_POINTERS *ep;
41 
42  char *LogOSVersion(char *buffer, const char *last) const override;
43  char *LogError(char *buffer, const char *last, const char *message) const override;
44  char *LogStacktrace(char *buffer, const char *last) const override;
45  char *LogRegisters(char *buffer, const char *last) const override;
46  char *LogModules(char *buffer, const char *last) const override;
47 public:
48 #if defined(_MSC_VER)
49  int WriteCrashDump(char *filename, const char *filename_last) const override;
50  char *AppendDecodedStacktrace(char *buffer, const char *last) const;
51 #else
52  char *AppendDecodedStacktrace(char *buffer, const char *last) const { return buffer; }
53 #endif /* _MSC_VER */
54 
56  char crashlog[65536];
58  char crashlog_filename[MAX_PATH];
60  char crashdump_filename[MAX_PATH];
62  char screenshot_filename[MAX_PATH];
63 
68  CrashLogWindows(EXCEPTION_POINTERS *ep = nullptr) :
69  ep(ep)
70  {
71  this->crashlog[0] = '\0';
72  this->crashlog_filename[0] = '\0';
73  this->crashdump_filename[0] = '\0';
74  this->screenshot_filename[0] = '\0';
75  }
76 
81 };
82 
83 /* static */ CrashLogWindows *CrashLogWindows::current = nullptr;
84 
85 /* virtual */ char *CrashLogWindows::LogOSVersion(char *buffer, const char *last) const
86 {
87  _OSVERSIONINFOA os;
88  os.dwOSVersionInfoSize = sizeof(os);
89  GetVersionExA(&os);
90 
91  return buffer + seprintf(buffer, last,
92  "Operating system:\n"
93  " Name: Windows\n"
94  " Release: %d.%d.%d (%s)\n",
95  (int)os.dwMajorVersion,
96  (int)os.dwMinorVersion,
97  (int)os.dwBuildNumber,
98  os.szCSDVersion
99  );
100 
101 }
102 
103 /* virtual */ char *CrashLogWindows::LogError(char *buffer, const char *last, const char *message) const
104 {
105  return buffer + seprintf(buffer, last,
106  "Crash reason:\n"
107  " Exception: %.8X\n"
108 #ifdef _M_AMD64
109  " Location: %.16IX\n"
110 #else
111  " Location: %.8X\n"
112 #endif
113  " Message: %s\n\n",
114  (int)ep->ExceptionRecord->ExceptionCode,
115  (size_t)ep->ExceptionRecord->ExceptionAddress,
116  message == nullptr ? "<none>" : message
117  );
118 }
119 
121  uint32 size;
122  uint32 crc32;
123  SYSTEMTIME file_time;
124 };
125 
126 static uint32 *_crc_table;
127 
128 static void MakeCRCTable(uint32 *table)
129 {
130  uint32 crc, poly = 0xEDB88320L;
131  int i;
132  int j;
133 
134  _crc_table = table;
135 
136  for (i = 0; i != 256; i++) {
137  crc = i;
138  for (j = 8; j != 0; j--) {
139  crc = (crc & 1 ? (crc >> 1) ^ poly : crc >> 1);
140  }
141  table[i] = crc;
142  }
143 }
144 
145 static uint32 CalcCRC(byte *data, uint size, uint32 crc)
146 {
147  for (; size > 0; size--) {
148  crc = ((crc >> 8) & 0x00FFFFFF) ^ _crc_table[(crc ^ *data++) & 0xFF];
149  }
150  return crc;
151 }
152 
153 static void GetFileInfo(DebugFileInfo *dfi, const wchar_t *filename)
154 {
155  HANDLE file;
156  memset(dfi, 0, sizeof(*dfi));
157 
158  file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 0);
159  if (file != INVALID_HANDLE_VALUE) {
160  byte buffer[1024];
161  DWORD numread;
162  uint32 filesize = 0;
163  FILETIME write_time;
164  uint32 crc = (uint32)-1;
165 
166  for (;;) {
167  if (ReadFile(file, buffer, sizeof(buffer), &numread, nullptr) == 0 || numread == 0) {
168  break;
169  }
170  filesize += numread;
171  crc = CalcCRC(buffer, numread, crc);
172  }
173  dfi->size = filesize;
174  dfi->crc32 = crc ^ (uint32)-1;
175 
176  if (GetFileTime(file, nullptr, nullptr, &write_time)) {
177  FileTimeToSystemTime(&write_time, &dfi->file_time);
178  }
179  CloseHandle(file);
180  }
181 }
182 
183 
184 static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
185 {
186  wchar_t buffer[MAX_PATH];
187  DebugFileInfo dfi;
188 
189  GetModuleFileName(mod, buffer, MAX_PATH);
190  GetFileInfo(&dfi, buffer);
191  output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n",
192  FS2OTTD(buffer),
193  mod,
194  dfi.size,
195  dfi.crc32,
196  dfi.file_time.wYear,
197  dfi.file_time.wMonth,
198  dfi.file_time.wDay,
199  dfi.file_time.wHour,
200  dfi.file_time.wMinute,
201  dfi.file_time.wSecond
202  );
203  return output;
204 }
205 
206 /* virtual */ char *CrashLogWindows::LogModules(char *output, const char *last) const
207 {
208  MakeCRCTable(AllocaM(uint32, 256));
209  BOOL (WINAPI *EnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD);
210 
211  output += seprintf(output, last, "Module information:\n");
212 
213  if (LoadLibraryList((Function*)&EnumProcessModules, "psapi.dll\0EnumProcessModules\0\0")) {
214  HMODULE modules[100];
215  DWORD needed;
216  BOOL res;
217 
218  HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
219  if (proc != nullptr) {
220  res = EnumProcessModules(proc, modules, sizeof(modules), &needed);
221  CloseHandle(proc);
222  if (res) {
223  size_t count = std::min<DWORD>(needed / sizeof(HMODULE), lengthof(modules));
224 
225  for (size_t i = 0; i != count; i++) output = PrintModuleInfo(output, last, modules[i]);
226  return output + seprintf(output, last, "\n");
227  }
228  }
229  }
230  output = PrintModuleInfo(output, last, nullptr);
231  return output + seprintf(output, last, "\n");
232 }
233 
234 /* virtual */ char *CrashLogWindows::LogRegisters(char *buffer, const char *last) const
235 {
236  buffer += seprintf(buffer, last, "Registers:\n");
237 #ifdef _M_AMD64
238  buffer += seprintf(buffer, last,
239  " RAX: %.16I64X RBX: %.16I64X RCX: %.16I64X RDX: %.16I64X\n"
240  " RSI: %.16I64X RDI: %.16I64X RBP: %.16I64X RSP: %.16I64X\n"
241  " R8: %.16I64X R9: %.16I64X R10: %.16I64X R11: %.16I64X\n"
242  " R12: %.16I64X R13: %.16I64X R14: %.16I64X R15: %.16I64X\n"
243  " RIP: %.16I64X EFLAGS: %.8lX\n",
244  ep->ContextRecord->Rax,
245  ep->ContextRecord->Rbx,
246  ep->ContextRecord->Rcx,
247  ep->ContextRecord->Rdx,
248  ep->ContextRecord->Rsi,
249  ep->ContextRecord->Rdi,
250  ep->ContextRecord->Rbp,
251  ep->ContextRecord->Rsp,
252  ep->ContextRecord->R8,
253  ep->ContextRecord->R9,
254  ep->ContextRecord->R10,
255  ep->ContextRecord->R11,
256  ep->ContextRecord->R12,
257  ep->ContextRecord->R13,
258  ep->ContextRecord->R14,
259  ep->ContextRecord->R15,
260  ep->ContextRecord->Rip,
261  ep->ContextRecord->EFlags
262  );
263 #elif defined(_M_IX86)
264  buffer += seprintf(buffer, last,
265  " EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\n"
266  " ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\n"
267  " EIP: %.8X EFLAGS: %.8X\n",
268  (int)ep->ContextRecord->Eax,
269  (int)ep->ContextRecord->Ebx,
270  (int)ep->ContextRecord->Ecx,
271  (int)ep->ContextRecord->Edx,
272  (int)ep->ContextRecord->Esi,
273  (int)ep->ContextRecord->Edi,
274  (int)ep->ContextRecord->Ebp,
275  (int)ep->ContextRecord->Esp,
276  (int)ep->ContextRecord->Eip,
277  (int)ep->ContextRecord->EFlags
278  );
279 #elif defined(_M_ARM64)
280  buffer += seprintf(buffer, last,
281  " X0: %.16I64X X1: %.16I64X X2: %.16I64X X3: %.16I64X\n"
282  " X4: %.16I64X X5: %.16I64X X6: %.16I64X X7: %.16I64X\n"
283  " X8: %.16I64X X9: %.16I64X X10: %.16I64X X11: %.16I64X\n"
284  " X12: %.16I64X X13: %.16I64X X14: %.16I64X X15: %.16I64X\n"
285  " X16: %.16I64X X17: %.16I64X X18: %.16I64X X19: %.16I64X\n"
286  " X20: %.16I64X X21: %.16I64X X22: %.16I64X X23: %.16I64X\n"
287  " X24: %.16I64X X25: %.16I64X X26: %.16I64X X27: %.16I64X\n"
288  " X28: %.16I64X Fp: %.16I64X Lr: %.16I64X\n",
289  ep->ContextRecord->X0,
290  ep->ContextRecord->X1,
291  ep->ContextRecord->X2,
292  ep->ContextRecord->X3,
293  ep->ContextRecord->X4,
294  ep->ContextRecord->X5,
295  ep->ContextRecord->X6,
296  ep->ContextRecord->X7,
297  ep->ContextRecord->X8,
298  ep->ContextRecord->X9,
299  ep->ContextRecord->X10,
300  ep->ContextRecord->X11,
301  ep->ContextRecord->X12,
302  ep->ContextRecord->X13,
303  ep->ContextRecord->X14,
304  ep->ContextRecord->X15,
305  ep->ContextRecord->X16,
306  ep->ContextRecord->X17,
307  ep->ContextRecord->X18,
308  ep->ContextRecord->X19,
309  ep->ContextRecord->X20,
310  ep->ContextRecord->X21,
311  ep->ContextRecord->X22,
312  ep->ContextRecord->X23,
313  ep->ContextRecord->X24,
314  ep->ContextRecord->X25,
315  ep->ContextRecord->X26,
316  ep->ContextRecord->X27,
317  ep->ContextRecord->X28,
318  ep->ContextRecord->Fp,
319  ep->ContextRecord->Lr
320  );
321 #endif
322 
323  buffer += seprintf(buffer, last, "\n Bytes at instruction pointer:\n");
324 #ifdef _M_AMD64
325  byte *b = (byte*)ep->ContextRecord->Rip;
326 #elif defined(_M_IX86)
327  byte *b = (byte*)ep->ContextRecord->Eip;
328 #elif defined(_M_ARM64)
329  byte *b = (byte*)ep->ContextRecord->Pc;
330 #endif
331  for (int i = 0; i != 24; i++) {
332  if (IsBadReadPtr(b, 1)) {
333  buffer += seprintf(buffer, last, " ??"); // OCR: WAS: , 0);
334  } else {
335  buffer += seprintf(buffer, last, " %.2X", *b);
336  }
337  b++;
338  }
339  return buffer + seprintf(buffer, last, "\n\n");
340 }
341 
342 /* virtual */ char *CrashLogWindows::LogStacktrace(char *buffer, const char *last) const
343 {
344  buffer += seprintf(buffer, last, "Stack trace:\n");
345 #ifdef _M_AMD64
346  uint32 *b = (uint32*)ep->ContextRecord->Rsp;
347 #elif defined(_M_IX86)
348  uint32 *b = (uint32*)ep->ContextRecord->Esp;
349 #elif defined(_M_ARM64)
350  uint32 *b = (uint32*)ep->ContextRecord->Sp;
351 #endif
352  for (int j = 0; j != 24; j++) {
353  for (int i = 0; i != 8; i++) {
354  if (IsBadReadPtr(b, sizeof(uint32))) {
355  buffer += seprintf(buffer, last, " ????????"); // OCR: WAS - , 0);
356  } else {
357  buffer += seprintf(buffer, last, " %.8X", *b);
358  }
359  b++;
360  }
361  buffer += seprintf(buffer, last, "\n");
362  }
363  return buffer + seprintf(buffer, last, "\n");
364 }
365 
366 #if defined(_MSC_VER)
367 static const uint MAX_SYMBOL_LEN = 512;
368 static const uint MAX_FRAMES = 64;
369 
370 #pragma warning(disable:4091)
371 #include <dbghelp.h>
372 #pragma warning(default:4091)
373 
374 char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) const
375 {
376 #define M(x) x "\0"
377  static const char dbg_import[] =
378  M("dbghelp.dll")
379  M("SymInitialize")
380  M("SymSetOptions")
381  M("SymCleanup")
382  M("StackWalk64")
383  M("SymFunctionTableAccess64")
384  M("SymGetModuleBase64")
385  M("SymGetModuleInfo64")
386  M("SymGetSymFromAddr64")
387  M("SymGetLineFromAddr64")
388  M("")
389  ;
390 #undef M
391 
392  struct ProcPtrs {
393  BOOL (WINAPI * pSymInitialize)(HANDLE, PCSTR, BOOL);
394  BOOL (WINAPI * pSymSetOptions)(DWORD);
395  BOOL (WINAPI * pSymCleanup)(HANDLE);
396  BOOL (WINAPI * pStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
397  PVOID (WINAPI * pSymFunctionTableAccess64)(HANDLE, DWORD64);
398  DWORD64 (WINAPI * pSymGetModuleBase64)(HANDLE, DWORD64);
399  BOOL (WINAPI * pSymGetModuleInfo64)(HANDLE, DWORD64, PIMAGEHLP_MODULE64);
400  BOOL (WINAPI * pSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
401  BOOL (WINAPI * pSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
402  } proc;
403 
404  buffer += seprintf(buffer, last, "\nDecoded stack trace:\n");
405 
406  /* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
407  if (LoadLibraryList((Function*)&proc, dbg_import)) {
408  /* Initialize symbol handler. */
409  HANDLE hCur = GetCurrentProcess();
410  proc.pSymInitialize(hCur, nullptr, TRUE);
411  /* Load symbols only when needed, fail silently on errors, demangle symbol names. */
412  proc.pSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_UNDNAME);
413 
414  /* Initialize starting stack frame from context record. */
415  STACKFRAME64 frame;
416  memset(&frame, 0, sizeof(frame));
417 #ifdef _M_AMD64
418  frame.AddrPC.Offset = ep->ContextRecord->Rip;
419  frame.AddrFrame.Offset = ep->ContextRecord->Rbp;
420  frame.AddrStack.Offset = ep->ContextRecord->Rsp;
421 #elif defined(_M_IX86)
422  frame.AddrPC.Offset = ep->ContextRecord->Eip;
423  frame.AddrFrame.Offset = ep->ContextRecord->Ebp;
424  frame.AddrStack.Offset = ep->ContextRecord->Esp;
425 #elif defined(_M_ARM64)
426  frame.AddrPC.Offset = ep->ContextRecord->Pc;
427  frame.AddrFrame.Offset = ep->ContextRecord->Fp;
428  frame.AddrStack.Offset = ep->ContextRecord->Sp;
429 #endif
430  frame.AddrPC.Mode = AddrModeFlat;
431  frame.AddrFrame.Mode = AddrModeFlat;
432  frame.AddrStack.Mode = AddrModeFlat;
433 
434  /* Copy context record as StackWalk64 may modify it. */
435  CONTEXT ctx;
436  memcpy(&ctx, ep->ContextRecord, sizeof(ctx));
437 
438  /* Allocate space for symbol info. */
439  IMAGEHLP_SYMBOL64 *sym_info = (IMAGEHLP_SYMBOL64*)alloca(sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMBOL_LEN - 1);
440  sym_info->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
441  sym_info->MaxNameLength = MAX_SYMBOL_LEN;
442 
443  /* Walk stack at most MAX_FRAMES deep in case the stack is corrupt. */
444  for (uint num = 0; num < MAX_FRAMES; num++) {
445  if (!proc.pStackWalk64(
446 #ifdef _M_AMD64
447  IMAGE_FILE_MACHINE_AMD64,
448 #else
449  IMAGE_FILE_MACHINE_I386,
450 #endif
451  hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
452 
453  if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
454  buffer += seprintf(buffer, last, " <infinite loop>\n");
455  break;
456  }
457 
458  /* Get module name. */
459  const char *mod_name = "???";
460 
461  IMAGEHLP_MODULE64 module;
462  module.SizeOfStruct = sizeof(module);
463  if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) {
464  mod_name = module.ModuleName;
465  }
466 
467  /* Print module and instruction pointer. */
468  buffer += seprintf(buffer, last, "[%02d] %-20s " PRINTF_PTR, num, mod_name, frame.AddrPC.Offset);
469 
470  /* Get symbol name and line info if possible. */
471  DWORD64 offset;
472  if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
473  buffer += seprintf(buffer, last, " %s + %I64u", sym_info->Name, offset);
474 
475  DWORD line_offs;
476  IMAGEHLP_LINE64 line;
477  line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
478  if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
479  buffer += seprintf(buffer, last, " (%s:%d)", line.FileName, line.LineNumber);
480  }
481  }
482  buffer += seprintf(buffer, last, "\n");
483  }
484 
485  proc.pSymCleanup(hCur);
486  }
487 
488  return buffer + seprintf(buffer, last, "\n*** End of additional info ***\n");
489 }
490 
491 /* virtual */ int CrashLogWindows::WriteCrashDump(char *filename, const char *filename_last) const
492 {
493  int ret = 0;
494  HMODULE dbghelp = LoadLibrary(L"dbghelp.dll");
495  if (dbghelp != nullptr) {
496  typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE,
497  MINIDUMP_TYPE,
498  CONST PMINIDUMP_EXCEPTION_INFORMATION,
499  CONST PMINIDUMP_USER_STREAM_INFORMATION,
500  CONST PMINIDUMP_CALLBACK_INFORMATION);
501  MiniDumpWriteDump_t funcMiniDumpWriteDump = (MiniDumpWriteDump_t)GetProcAddress(dbghelp, "MiniDumpWriteDump");
502  if (funcMiniDumpWriteDump != nullptr) {
503  seprintf(filename, filename_last, "%scrash.dmp", _personal_dir.c_str());
504  HANDLE file = CreateFile(OTTD2FS(filename), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0);
505  HANDLE proc = GetCurrentProcess();
506  DWORD procid = GetCurrentProcessId();
507  MINIDUMP_EXCEPTION_INFORMATION mdei;
508  MINIDUMP_USER_STREAM userstream;
509  MINIDUMP_USER_STREAM_INFORMATION musi;
510 
511  userstream.Type = LastReservedStream + 1;
512  userstream.Buffer = (void*)this->crashlog;
513  userstream.BufferSize = (ULONG)strlen(this->crashlog) + 1;
514 
515  musi.UserStreamCount = 1;
516  musi.UserStreamArray = &userstream;
517 
518  mdei.ThreadId = GetCurrentThreadId();
519  mdei.ExceptionPointers = ep;
520  mdei.ClientPointers = false;
521 
522  funcMiniDumpWriteDump(proc, procid, file, MiniDumpWithDataSegs, &mdei, &musi, nullptr);
523  ret = 1;
524  } else {
525  ret = -1;
526  }
527  FreeLibrary(dbghelp);
528  }
529  return ret;
530 }
531 #endif /* _MSC_VER */
532 
533 extern bool CloseConsoleLogIfActive();
534 static void ShowCrashlogWindow();
535 
540 thread_local void *_safe_esp = nullptr;
541 
542 static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
543 {
544  /* Restore system timer resolution. */
545  timeEndPeriod(1);
546 
547  /* Disable our event loop. */
548  SetWindowLongPtr(GetActiveWindow(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);
549 
550  if (CrashLogWindows::current != nullptr) {
552  ExitProcess(2);
553  }
554 
555  if (GamelogTestEmergency()) {
556  static const wchar_t _emergency_crash[] =
557  L"A serious fault condition occurred in the game. The game will shut down.\n"
558  L"As you loaded an emergency savegame no crash information will be generated.\n";
559  MessageBox(nullptr, _emergency_crash, L"Fatal Application Failure", MB_ICONERROR);
560  ExitProcess(3);
561  }
562 
564  static const wchar_t _saveload_crash[] =
565  L"A serious fault condition occurred in the game. The game will shut down.\n"
566  L"As you loaded an savegame for which you do not have the required NewGRFs\n"
567  L"no crash information will be generated.\n";
568  MessageBox(nullptr, _saveload_crash, L"Fatal Application Failure", MB_ICONERROR);
569  ExitProcess(3);
570  }
571 
572  CrashLogWindows *log = new CrashLogWindows(ep);
574  char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog));
576  log->AppendDecodedStacktrace(buf, lastof(log->crashlog));
579 
580  /* Close any possible log files */
581  CloseConsoleLogIfActive();
582 
583  if ((VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) && _safe_esp != nullptr) {
584 #ifdef _M_AMD64
585  ep->ContextRecord->Rip = (DWORD64)ShowCrashlogWindow;
586  ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
587 #elif defined(_M_IX86)
588  ep->ContextRecord->Eip = (DWORD)ShowCrashlogWindow;
589  ep->ContextRecord->Esp = (DWORD)_safe_esp;
590 #elif defined(_M_ARM64)
591  ep->ContextRecord->Pc = (DWORD64)ShowCrashlogWindow;
592  ep->ContextRecord->Sp = (DWORD64)_safe_esp;
593 #endif
594  return EXCEPTION_CONTINUE_EXECUTION;
595  }
596 
598  return EXCEPTION_EXECUTE_HANDLER;
599 }
600 
601 static void CDECL CustomAbort(int signal)
602 {
603  RaiseException(0xE1212012, 0, 0, nullptr);
604 }
605 
606 /* static */ void CrashLog::InitialiseCrashLog()
607 {
609 
610  /* SIGABRT is not an unhandled exception, so we need to intercept it. */
611  signal(SIGABRT, CustomAbort);
612 #if defined(_MSC_VER)
613  /* Don't show abort message as we will get the crashlog window anyway. */
614  _set_abort_behavior(0, _WRITE_ABORT_MSG);
615 #endif
616  SetUnhandledExceptionFilter(ExceptionHandler);
617 }
618 
619 /* static */ void CrashLog::InitThread()
620 {
621 #if defined(_M_AMD64) || defined(_M_ARM64)
622  CONTEXT ctx;
623  RtlCaptureContext(&ctx);
624 
625  /* The stack pointer for AMD64 must always be 16-byte aligned inside a
626  * function. As we are simulating a function call with the safe ESP value,
627  * we need to subtract 8 for the imaginary return address otherwise stack
628  * alignment would be wrong in the called function. */
629 # if defined(_M_ARM64)
630  _safe_esp = (void *)(ctx.Sp - 8);
631 # else
632  _safe_esp = (void *)(ctx.Rsp - 8);
633 # endif
634 #else
635  void *safe_esp;
636 # if defined(_MSC_VER)
637  _asm {
638  mov safe_esp, esp
639  }
640 # else
641  asm("movl %esp, _safe_esp");
642 # endif
643  _safe_esp = safe_esp;
644 #endif
645 }
646 
647 /* The crash log GUI */
648 
649 static bool _expanded;
650 
651 static const wchar_t _crash_desc[] =
652  L"A serious fault condition occurred in the game. The game will shut down.\n"
653  L"Please send the crash information and the crash.dmp file (if any) to the developers.\n"
654  L"This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues. "
655  L"The information contained in the report is displayed below.\n"
656  L"Press \"Emergency save\" to attempt saving the game. Generated file(s):\n"
657  L"%s";
658 
659 static const wchar_t _save_succeeded[] =
660  L"Emergency save succeeded.\nIts location is '%s'.\n"
661  L"Be aware that critical parts of the internal game state may have become "
662  L"corrupted. The saved game is not guaranteed to work.";
663 
664 static const wchar_t * const _expand_texts[] = {L"S&how report >>", L"&Hide report <<" };
665 
666 static void SetWndSize(HWND wnd, int mode)
667 {
668  RECT r, r2;
669 
670  GetWindowRect(wnd, &r);
671  SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
672 
673  if (mode >= 0) {
674  GetWindowRect(GetDlgItem(wnd, 11), &r2);
675  int offs = r2.bottom - r2.top + 10;
676  if (mode == 0) offs = -offs;
677  SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
678  r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
679  } else {
680  SetWindowPos(wnd, HWND_TOPMOST,
681  (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
682  (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
683  0, 0, SWP_NOSIZE);
684  }
685 }
686 
687 static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
688 {
689  switch (msg) {
690  case WM_INITDIALOG: {
691  /* We need to put the crash-log in a separate buffer because the default
692  * buffer in OTTD2FS is not large enough (512 chars) */
693  wchar_t crash_msgW[lengthof(CrashLogWindows::current->crashlog)];
694  /* Convert unix -> dos newlines because the edit box only supports that properly :( */
695  const char *unix_nl = CrashLogWindows::current->crashlog;
696  char dos_nl[lengthof(CrashLogWindows::current->crashlog)];
697  char *p = dos_nl;
698  WChar c;
699  while ((c = Utf8Consume(&unix_nl)) && p < lastof(dos_nl) - 4) { // 4 is max number of bytes per character
700  if (c == '\n') p += Utf8Encode(p, '\r');
701  p += Utf8Encode(p, c);
702  }
703  *p = '\0';
704 
705  /* Add path to crash.log and crash.dmp (if any) to the crash window text */
706  size_t len = wcslen(_crash_desc) + 2;
707  len += wcslen(OTTD2FS(CrashLogWindows::current->crashlog_filename)) + 2;
708  len += wcslen(OTTD2FS(CrashLogWindows::current->crashdump_filename)) + 2;
709  len += wcslen(OTTD2FS(CrashLogWindows::current->screenshot_filename)) + 1;
710 
711  wchar_t *text = AllocaM(wchar_t, len);
712  _snwprintf(text, len, _crash_desc, OTTD2FS(CrashLogWindows::current->crashlog_filename));
713  if (OTTD2FS(CrashLogWindows::current->crashdump_filename)[0] != L'\0') {
714  wcscat(text, L"\n");
715  wcscat(text, OTTD2FS(CrashLogWindows::current->crashdump_filename));
716  }
717  if (OTTD2FS(CrashLogWindows::current->screenshot_filename)[0] != L'\0') {
718  wcscat(text, L"\n");
719  wcscat(text, OTTD2FS(CrashLogWindows::current->screenshot_filename));
720  }
721 
722  SetDlgItemText(wnd, 10, text);
723  SetDlgItemText(wnd, 11, convert_to_fs(dos_nl, crash_msgW, lengthof(crash_msgW)));
724  SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
725  SetWndSize(wnd, -1);
726  } return TRUE;
727  case WM_COMMAND:
728  switch (wParam) {
729  case 12: // Close
731  ExitProcess(2);
732  case 13: // Emergency save
733  char filename[MAX_PATH];
734  if (CrashLogWindows::current->WriteSavegame(filename, lastof(filename))) {
735  size_t len = wcslen(_save_succeeded) + wcslen(OTTD2FS(filename)) + 1;
736  wchar_t *text = AllocaM(wchar_t, len);
737  _snwprintf(text, len, _save_succeeded, OTTD2FS(filename));
738  MessageBox(wnd, text, L"Save successful", MB_ICONINFORMATION);
739  } else {
740  MessageBox(wnd, L"Save failed", L"Save failed", MB_ICONINFORMATION);
741  }
742  break;
743  case 15: // Expand window to show crash-message
744  _expanded ^= 1;
745  SetWndSize(wnd, _expanded);
746  break;
747  }
748  return TRUE;
749  case WM_CLOSE:
751  ExitProcess(2);
752  }
753 
754  return FALSE;
755 }
756 
757 static void ShowCrashlogWindow()
758 {
759  ShowCursor(TRUE);
760  ShowWindow(GetActiveWindow(), FALSE);
761  DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(100), nullptr, CrashDialogFunc);
762 }
WChar
char32_t WChar
Type for wide characters, i.e.
Definition: string_type.h:35
FS2OTTD
const char * FS2OTTD(const wchar_t *name)
Convert to OpenTTD's encoding from wide characters.
Definition: win32.cpp:565
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:1122
CrashLog::AfterCrashLogCleanup
static void AfterCrashLogCleanup()
Try to close the sound/video stuff so it doesn't keep lingering around incorrect video states or so,...
Definition: crashlog.cpp:507
win32.h
CrashLog::WriteScreenshot
bool WriteScreenshot(char *filename, const char *filename_last) const
Write the (crash) screenshot to a file.
Definition: crashlog.cpp:423
CrashLogWindows
Windows implementation for the crash logger.
Definition: crashlog_win.cpp:38
CrashLogWindows::CrashLogWindows
CrashLogWindows(EXCEPTION_POINTERS *ep=nullptr)
A crash log is always generated when it's generated.
Definition: crashlog_win.cpp:68
LoadLibraryList
bool LoadLibraryList(Function proc[], const char *dll)
Helper function needed by dynamically loading libraries.
Definition: win32.cpp:56
DebugFileInfo
Definition: crashlog_win.cpp:120
Utf8Encode
size_t Utf8Encode(T buf, WChar c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:541
CrashLogWindows::crashlog_filename
char crashlog_filename[MAX_PATH]
Buffer for the filename of the crash log.
Definition: crashlog_win.cpp:58
_safe_esp
thread_local void * _safe_esp
Stack pointer for use when 'starting' the crash handler.
Definition: crashlog_win.cpp:540
CrashLogWindows::LogOSVersion
char * LogOSVersion(char *buffer, const char *last) const override
Writes OS' version to the buffer.
Definition: crashlog_win.cpp:85
CrashLogWindows::LogStacktrace
char * LogStacktrace(char *buffer, const char *last) const override
Writes the stack trace to the buffer, if there is information about it available.
Definition: crashlog_win.cpp:342
CrashLogWindows::ep
EXCEPTION_POINTERS * ep
Information about the encountered exception.
Definition: crashlog_win.cpp:40
CrashLog::InitThread
static void InitThread()
Prepare crash log handler for a newly started thread.
Definition: crashlog_osx.cpp:261
CrashLogWindows::current
static CrashLogWindows * current
Points to the current crash log.
Definition: crashlog_win.cpp:80
GamelogTestEmergency
bool GamelogTestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition: gamelog.cpp:420
convert_to_fs
wchar_t * convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen, bool console_cp)
Convert from OpenTTD's encoding to that of the environment in UNICODE.
Definition: win32.cpp:617
SaveloadCrashWithMissingNewGRFs
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition: afterload.cpp:365
CrashLog::message
static const char * message
Pointer to the error message.
Definition: crashlog.h:19
CrashLog
Helper class for creating crash logs.
Definition: crashlog.h:16
CrashLog::WriteCrashDump
virtual int WriteCrashDump(char *filename, const char *filename_last) const
Write the (crash) dump to a file.
Definition: crashlog.cpp:383
CrashLogWindows::crashdump_filename
char crashdump_filename[MAX_PATH]
Buffer for the filename of the crash dump.
Definition: crashlog_win.cpp:60
VideoDriver::GetInstance
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
Definition: video_driver.hpp:199
CrashLogWindows::crashlog
char crashlog[65536]
Buffer for the generated crash log.
Definition: crashlog_win.cpp:56
CrashLog::InitialiseCrashLog
static void InitialiseCrashLog()
Initialiser for crash logs; do the appropriate things so crashes are handled by our crash handler ins...
Definition: crashlog_osx.cpp:254
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:460
CrashLogWindows::LogModules
char * LogModules(char *buffer, const char *last) const override
Writes the dynamically linked libraries/modules to the buffer, if there is information about it avail...
Definition: crashlog_win.cpp:206
CrashLogWindows::screenshot_filename
char screenshot_filename[MAX_PATH]
Buffer for the filename of the crash screenshot.
Definition: crashlog_win.cpp:62
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:369
CrashLog::FillCrashLog
char * FillCrashLog(char *buffer, const char *last) const
Fill the crash log buffer with all data of a crash log.
Definition: crashlog.cpp:334
CrashLog::WriteCrashLog
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
Write the crash log to a file.
Definition: crashlog.cpp:369
CrashLogWindows::LogError
char * LogError(char *buffer, const char *last, const char *message) const override
Writes actually encountered error to the buffer.
Definition: crashlog_win.cpp:103
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:385
OTTD2FS
const wchar_t * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to wide characters.
Definition: win32.cpp:580
CrashLogWindows::LogRegisters
char * LogRegisters(char *buffer, const char *last) const override
Writes information about the data in the registers, if there is information about it available.
Definition: crashlog_win.cpp:234
AllocaM
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
Definition: alloc_func.hpp:132