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