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

OpenTTD Source 12.0-beta1

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
81 static CrashLogWindows *current;
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
121 struct DebugFileInfo {
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) {
540 CrashLog::AfterCrashLogCleanup();
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
552 if (SaveloadCrashWithMissingNewGRFs()) {
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);
562 CrashLogWindows::current = log;
563 char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog));
564 log->WriteCrashDump(log->crashdump_filename, lastof(log->crashdump_filename));
565 log->AppendDecodedStacktrace(buf, lastof(log->crashlog));
566 log->WriteCrashLog(log->crashlog, log->crashlog_filename, lastof(log->crashlog_filename));
567 log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename));
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
586 CrashLog::AfterCrashLogCleanup();
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 {
597 CrashLog::InitThread();
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
724 CrashLog::AfterCrashLogCleanup();
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:
746 CrashLog::AfterCrashLogCleanup();
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