OpenTTD Source: src/os/windows/crashlog_win.cpp Source File

OpenTTD Source 1.11.0-RC1

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
80 static CrashLogWindows *current;
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
120 struct DebugFileInfo {
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) {
551 CrashLog::AfterCrashLogCleanup();
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
563 if (SaveloadCrashWithMissingNewGRFs()) {
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);
573 CrashLogWindows::current = log;
574 char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog));
575 log->WriteCrashDump(log->crashdump_filename, lastof(log->crashdump_filename));
576 log->AppendDecodedStacktrace(buf, lastof(log->crashlog));
577 log->WriteCrashLog(log->crashlog, log->crashlog_filename, lastof(log->crashlog_filename));
578 log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename));
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
597 CrashLog::AfterCrashLogCleanup();
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 {
608 CrashLog::InitThread();
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
730 CrashLog::AfterCrashLogCleanup();
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:
750 CrashLog::AfterCrashLogCleanup();
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:1119
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:523
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:364
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:187
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:442
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:367
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:383
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