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

OpenTTD Source 14.0-beta3

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/math_func.hpp"
14 #include "../../string_func.h"
15 #include "../../fileio_func.h"
16 #include "../../strings_func.h"
17 #include "../../gamelog.h"
18 #include "../../saveload/saveload.h"
19 #include "../../video/video_driver.hpp"
20 #include "../../library_loader.h"
21
22 #include <windows.h>
23 #include <mmsystem.h>
24 #include <signal.h>
25 #include <psapi.h>
26
27 #if defined(_MSC_VER)
28 # include <dbghelp.h>
29 #else
30 # include <setjmp.h>
31 #endif
32
33 #ifdef WITH_UNOFFICIAL_BREAKPAD
34 # include <client/windows/handler/exception_handler.h>
35 #endif
36
37 #include "../../safeguards.h"
38
40 static constexpr DWORD CUSTOM_ABORT_EXCEPTION = 0xE1212012;
41
43 static const std::map<DWORD, std::string> exception_code_to_name{
44 {EXCEPTION_ACCESS_VIOLATION, "EXCEPTION_ACCESS_VIOLATION"},
45 {EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"},
46 {EXCEPTION_BREAKPOINT, "EXCEPTION_BREAKPOINT"},
47 {EXCEPTION_DATATYPE_MISALIGNMENT, "EXCEPTION_DATATYPE_MISALIGNMENT"},
48 {EXCEPTION_FLT_DENORMAL_OPERAND, "EXCEPTION_FLT_DENORMAL_OPERAND"},
49 {EXCEPTION_FLT_DIVIDE_BY_ZERO, "EXCEPTION_FLT_DIVIDE_BY_ZERO"},
50 {EXCEPTION_FLT_INEXACT_RESULT, "EXCEPTION_FLT_INEXACT_RESULT"},
51 {EXCEPTION_FLT_INVALID_OPERATION, "EXCEPTION_FLT_INVALID_OPERATION"},
52 {EXCEPTION_FLT_OVERFLOW, "EXCEPTION_FLT_OVERFLOW"},
53 {EXCEPTION_FLT_STACK_CHECK, "EXCEPTION_FLT_STACK_CHECK"},
54 {EXCEPTION_FLT_UNDERFLOW, "EXCEPTION_FLT_UNDERFLOW"},
55 {EXCEPTION_GUARD_PAGE, "EXCEPTION_GUARD_PAGE"},
56 {EXCEPTION_ILLEGAL_INSTRUCTION, "EXCEPTION_ILLEGAL_INSTRUCTION"},
57 {EXCEPTION_IN_PAGE_ERROR, "EXCEPTION_IN_PAGE_ERROR"},
58 {EXCEPTION_INT_DIVIDE_BY_ZERO, "EXCEPTION_INT_DIVIDE_BY_ZERO"},
59 {EXCEPTION_INT_OVERFLOW, "EXCEPTION_INT_OVERFLOW"},
60 {EXCEPTION_INVALID_DISPOSITION, "EXCEPTION_INVALID_DISPOSITION"},
61 {EXCEPTION_INVALID_HANDLE, "EXCEPTION_INVALID_HANDLE"},
62 {EXCEPTION_NONCONTINUABLE_EXCEPTION, "EXCEPTION_NONCONTINUABLE_EXCEPTION"},
63 {EXCEPTION_PRIV_INSTRUCTION, "EXCEPTION_PRIV_INSTRUCTION"},
64 {EXCEPTION_SINGLE_STEP, "EXCEPTION_SINGLE_STEP"},
65 {EXCEPTION_STACK_OVERFLOW, "EXCEPTION_STACK_OVERFLOW"},
66 {STATUS_UNWIND_CONSOLIDATE, "STATUS_UNWIND_CONSOLIDATE"},
67 };
68
74 [[noreturn]] static void ImmediateExitProcess(uint exit_code)
75 {
76 /* TerminateProcess may fail in some special edge cases; fall back to ExitProcess in this case. */
77 TerminateProcess(GetCurrentProcess(), exit_code);
78 ExitProcess(exit_code);
79 }
80
84 class CrashLogWindows : public CrashLog {
86 EXCEPTION_POINTERS *ep;
87
88 void SurveyCrash(nlohmann::json &survey) const override
89 {
90 survey["id"] = ep->ExceptionRecord->ExceptionCode;
91 if (exception_code_to_name.count(ep->ExceptionRecord->ExceptionCode) > 0) {
92 survey["reason"] = exception_code_to_name.at(ep->ExceptionRecord->ExceptionCode);
93 } else {
94 survey["reason"] = "Unknown exception code";
95 }
96 }
97
98 void SurveyStacktrace(nlohmann::json &survey) const override;
99 public:
100
101 #ifdef WITH_UNOFFICIAL_BREAKPAD
102 static bool MinidumpCallback(const wchar_t *dump_dir, const wchar_t *minidump_id, void *context, EXCEPTION_POINTERS *, MDRawAssertionInfo *, bool succeeded)
103 {
104 CrashLogWindows *crashlog = reinterpret_cast<CrashLogWindows *>(context);
105
106 crashlog->crashdump_filename = crashlog->CreateFileName(".dmp");
107 std::rename(fmt::format("{}/{}.dmp", FS2OTTD(dump_dir), FS2OTTD(minidump_id)).c_str(), crashlog->crashdump_filename.c_str());
108 return succeeded;
109 }
110
111 bool WriteCrashDump() override
112 {
113 return google_breakpad::ExceptionHandler::WriteMinidump(OTTD2FS(_personal_dir), MinidumpCallback, this);
114 }
115 #endif
116
117 #if defined(_MSC_VER)
118 /* virtual */ bool TryExecute(std::string_view section_name, std::function<bool()> &&func) override
119 {
120 this->try_execute_active = true;
121 bool res;
122
123 __try {
124 res = func();
125 } __except (EXCEPTION_EXECUTE_HANDLER) {
126 fmt::print("Something went wrong when attempting to fill {} section of the crash log.\n", section_name);
127 res = false;
128 }
129
130 this->try_execute_active = false;
131 return res;
132 }
133 #else
134 /* virtual */ bool TryExecute(std::string_view section_name, std::function<bool()> &&func) override
135 {
136 this->try_execute_active = true;
137
138 /* Setup a longjump in case a crash happens. */
139 if (setjmp(this->internal_fault_jmp_buf) != 0) {
140 fmt::print("Something went wrong when attempting to fill {} section of the crash log.\n", section_name);
141
142 this->try_execute_active = false;
143 return false;
144 }
145
146 bool res = func();
147 this->try_execute_active = false;
148 return res;
149 }
150 #endif /* _MSC_VER */
151
156 CrashLogWindows(EXCEPTION_POINTERS *ep = nullptr) :
157 ep(ep)
158 {
159 }
160
161 #if !defined(_MSC_VER)
162
163 jmp_buf internal_fault_jmp_buf;
164 #endif
165
167 bool try_execute_active = false;
168
170 static CrashLogWindows *current;
171 };
172
173 /* static */ CrashLogWindows *CrashLogWindows::current = nullptr;
174
175 #if defined(_MSC_VER)
176 static const uint MAX_SYMBOL_LEN = 512;
177 static const uint MAX_FRAMES = 64;
178
179 /* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
180 {
181 LibraryLoader dbghelp("dbghelp.dll");
182 struct ProcPtrs {
183 BOOL (WINAPI * pSymInitialize)(HANDLE, PCSTR, BOOL);
184 BOOL (WINAPI * pSymSetOptions)(DWORD);
185 BOOL (WINAPI * pSymCleanup)(HANDLE);
186 BOOL (WINAPI * pStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
187 PVOID (WINAPI * pSymFunctionTableAccess64)(HANDLE, DWORD64);
188 DWORD64 (WINAPI * pSymGetModuleBase64)(HANDLE, DWORD64);
189 BOOL (WINAPI * pSymGetModuleInfo64)(HANDLE, DWORD64, PIMAGEHLP_MODULE64);
190 BOOL (WINAPI * pSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
191 BOOL (WINAPI * pSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
192 } proc = {
193 dbghelp.GetFunction("SymInitialize"),
194 dbghelp.GetFunction("SymSetOptions"),
195 dbghelp.GetFunction("SymCleanup"),
196 dbghelp.GetFunction("StackWalk64"),
197 dbghelp.GetFunction("SymFunctionTableAccess64"),
198 dbghelp.GetFunction("SymGetModuleBase64"),
199 dbghelp.GetFunction("SymGetModuleInfo64"),
200 dbghelp.GetFunction("SymGetSymFromAddr64"),
201 dbghelp.GetFunction("SymGetLineFromAddr64"),
202 };
203
204 survey = nlohmann::json::array();
205
206 /* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
207 if (!dbghelp.HasError()) {
208 /* Initialize symbol handler. */
209 HANDLE hCur = GetCurrentProcess();
210 proc.pSymInitialize(hCur, nullptr, TRUE);
211 /* Load symbols only when needed, fail silently on errors, demangle symbol names. */
212 proc.pSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_UNDNAME);
213
214 /* Initialize starting stack frame from context record. */
215 STACKFRAME64 frame;
216 memset(&frame, 0, sizeof(frame));
217 #ifdef _M_AMD64
218 frame.AddrPC.Offset = ep->ContextRecord->Rip;
219 frame.AddrFrame.Offset = ep->ContextRecord->Rbp;
220 frame.AddrStack.Offset = ep->ContextRecord->Rsp;
221 #elif defined(_M_IX86)
222 frame.AddrPC.Offset = ep->ContextRecord->Eip;
223 frame.AddrFrame.Offset = ep->ContextRecord->Ebp;
224 frame.AddrStack.Offset = ep->ContextRecord->Esp;
225 #elif defined(_M_ARM64)
226 frame.AddrPC.Offset = ep->ContextRecord->Pc;
227 frame.AddrFrame.Offset = ep->ContextRecord->Fp;
228 frame.AddrStack.Offset = ep->ContextRecord->Sp;
229 #endif
230 frame.AddrPC.Mode = AddrModeFlat;
231 frame.AddrFrame.Mode = AddrModeFlat;
232 frame.AddrStack.Mode = AddrModeFlat;
233
234 /* Copy context record as StackWalk64 may modify it. */
235 CONTEXT ctx;
236 memcpy(&ctx, ep->ContextRecord, sizeof(ctx));
237
238 /* Allocate space for symbol info. */
239 char sym_info_raw[sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMBOL_LEN - 1];
240 IMAGEHLP_SYMBOL64 *sym_info = (IMAGEHLP_SYMBOL64*)sym_info_raw;
241 sym_info->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
242 sym_info->MaxNameLength = MAX_SYMBOL_LEN;
243
244 /* Walk stack at most MAX_FRAMES deep in case the stack is corrupt. */
245 for (uint num = 0; num < MAX_FRAMES; num++) {
246 if (!proc.pStackWalk64(
247 #ifdef _M_AMD64
248 IMAGE_FILE_MACHINE_AMD64,
249 #else
250 IMAGE_FILE_MACHINE_I386,
251 #endif
252 hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
253
254 if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
255 survey.push_back("<infinite loop>");
256 break;
257 }
258
259 /* Get module name. */
260 const char *mod_name = "???";
261
262 IMAGEHLP_MODULE64 module;
263 module.SizeOfStruct = sizeof(module);
264 if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) {
265 mod_name = module.ModuleName;
266 }
267
268 /* Print module and instruction pointer. */
269 std::string message = fmt::format("{:20s} {:X}", mod_name, frame.AddrPC.Offset);
270
271 /* Get symbol name and line info if possible. */
272 DWORD64 offset;
273 if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
274 message += fmt::format(" {} + {}", sym_info->Name, offset);
275
276 DWORD line_offs;
277 IMAGEHLP_LINE64 line;
278 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
279 if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
280 message += fmt::format(" ({}:{})", line.FileName, line.LineNumber);
281 }
282 }
283
284 survey.push_back(message);
285 }
286
287 proc.pSymCleanup(hCur);
288 }
289 }
290 #else
291 /* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &) const
292 {
293 /* Not supported. */
294 }
295 #endif /* _MSC_VER */
296
297 extern bool CloseConsoleLogIfActive();
298 static void ShowCrashlogWindow();
299
304 thread_local void *_safe_esp = nullptr;
305
306 static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
307 {
308 /* Restore system timer resolution. */
309 timeEndPeriod(1);
310
311 /* Disable our event loop. */
312 SetWindowLongPtr(GetActiveWindow(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);
313
314 if (CrashLogWindows::current != nullptr) {
315 CrashLog::AfterCrashLogCleanup();
316 ImmediateExitProcess(2);
317 }
318
319 if (_gamelog.TestEmergency()) {
320 static const wchar_t _emergency_crash[] =
321 L"A serious fault condition occurred in the game. The game will shut down.\n"
322 L"As you loaded an emergency savegame no crash information will be generated.\n";
323 MessageBox(nullptr, _emergency_crash, L"Fatal Application Failure", MB_ICONERROR);
324 ImmediateExitProcess(3);
325 }
326
327 if (SaveloadCrashWithMissingNewGRFs()) {
328 static const wchar_t _saveload_crash[] =
329 L"A serious fault condition occurred in the game. The game will shut down.\n"
330 L"As you loaded an savegame for which you do not have the required NewGRFs\n"
331 L"no crash information will be generated.\n";
332 MessageBox(nullptr, _saveload_crash, L"Fatal Application Failure", MB_ICONERROR);
333 ImmediateExitProcess(3);
334 }
335
336 CrashLogWindows *log = new CrashLogWindows(ep);
337 CrashLogWindows::current = log;
338 log->MakeCrashLog();
339
340 /* Close any possible log files */
341 CloseConsoleLogIfActive();
342
343 if ((VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) && _safe_esp != nullptr) {
344 #ifdef _M_AMD64
345 ep->ContextRecord->Rip = (DWORD64)ShowCrashlogWindow;
346 ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
347 #elif defined(_M_IX86)
348 ep->ContextRecord->Eip = (DWORD)ShowCrashlogWindow;
349 ep->ContextRecord->Esp = (DWORD)_safe_esp;
350 #elif defined(_M_ARM64)
351 ep->ContextRecord->Pc = (DWORD64)ShowCrashlogWindow;
352 ep->ContextRecord->Sp = (DWORD64)_safe_esp;
353 #endif
354 return EXCEPTION_CONTINUE_EXECUTION;
355 }
356
357 CrashLog::AfterCrashLogCleanup();
358 return EXCEPTION_EXECUTE_HANDLER;
359 }
360
361 static LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS *ep)
362 {
363 if (CrashLogWindows::current != nullptr && CrashLogWindows::current->try_execute_active) {
364 #if defined(_MSC_VER)
365 return EXCEPTION_CONTINUE_SEARCH;
366 #else
367 longjmp(CrashLogWindows::current->internal_fault_jmp_buf, 1);
368 #endif
369 }
370
371 if (ep->ExceptionRecord->ExceptionCode == 0xC0000374 /* heap corruption */) {
372 return ExceptionHandler(ep);
373 }
374 if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
375 return ExceptionHandler(ep);
376 }
377 if (ep->ExceptionRecord->ExceptionCode == CUSTOM_ABORT_EXCEPTION) {
378 return ExceptionHandler(ep);
379 }
380
381 return EXCEPTION_CONTINUE_SEARCH;
382 }
383
384 static void CDECL CustomAbort(int)
385 {
386 RaiseException(CUSTOM_ABORT_EXCEPTION, 0, 0, nullptr);
387 }
388
389 /* static */ void CrashLog::InitialiseCrashLog()
390 {
391 CrashLog::InitThread();
392
393 /* SIGABRT is not an unhandled exception, so we need to intercept it. */
394 signal(SIGABRT, CustomAbort);
395 #if defined(_MSC_VER)
396 /* Don't show abort message as we will get the crashlog window anyway. */
397 _set_abort_behavior(0, _WRITE_ABORT_MSG);
398 #endif
399 SetUnhandledExceptionFilter(ExceptionHandler);
400 AddVectoredExceptionHandler(1, VectoredExceptionHandler);
401 }
402
403 /* static */ void CrashLog::InitThread()
404 {
405 #if defined(_M_AMD64) || defined(_M_ARM64)
406 CONTEXT ctx;
407 RtlCaptureContext(&ctx);
408
409 /* The stack pointer for AMD64 must always be 16-byte aligned inside a
410 * function. As we are simulating a function call with the safe ESP value,
411 * we need to subtract 8 for the imaginary return address otherwise stack
412 * alignment would be wrong in the called function. */
413 # if defined(_M_ARM64)
414 _safe_esp = (void *)(ctx.Sp - 8);
415 # else
416 _safe_esp = (void *)(ctx.Rsp - 8);
417 # endif
418 #else
419 void *safe_esp;
420 # if defined(_MSC_VER)
421 _asm {
422 mov safe_esp, esp
423 }
424 # else
425 asm("movl %%esp, %0" : "=rm" (safe_esp));
426 # endif
427 _safe_esp = safe_esp;
428 #endif
429 }
430
431 /* The crash log GUI */
432
433 static bool _expanded;
434
435 static const wchar_t _crash_desc[] =
436 L"A serious fault condition occurred in the game. The game will shut down.\n"
437 L"Please send crash.json.log, crash.dmp, and crash.sav to the developers.\n"
438 L"This will greatly help debugging.\n\n"
439 L"https://github.com/OpenTTD/OpenTTD/issues\n\n"
440 L"%s\n%s\n%s\n%s\n";
441
442 static const wchar_t * const _expand_texts[] = {L"S&how report >>", L"&Hide report <<" };
443
444 static void SetWndSize(HWND wnd, int mode)
445 {
446 RECT r, r2;
447
448 GetWindowRect(wnd, &r);
449 SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
450
451 if (mode >= 0) {
452 GetWindowRect(GetDlgItem(wnd, 11), &r2);
453 int offs = r2.bottom - r2.top + 10;
454 if (mode == 0) offs = -offs;
455 SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
456 r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
457 } else {
458 SetWindowPos(wnd, HWND_TOPMOST,
459 (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
460 (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
461 0, 0, SWP_NOSIZE);
462 }
463 }
464
465 static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM)
466 {
467 switch (msg) {
468 case WM_INITDIALOG: {
469 std::string crashlog = CrashLogWindows::current->survey.dump(4);
470 size_t crashlog_length = crashlog.size() + 1;
471 /* Reserve extra space for LF to CRLF conversion. */
472 crashlog_length += std::count(crashlog.begin(), crashlog.end(), '\n');
473
474 const size_t filename_count = 4;
475 const size_t filename_buf_length = MAX_PATH + 1;
476 const size_t crash_desc_buf_length = lengthof(_crash_desc) + filename_buf_length * filename_count + 1;
477
478 /* We need to put the crash-log in a separate buffer because the default
479 * buffer in MB_TO_WIDE is not large enough (512 chars).
480 * Use VirtualAlloc to allocate pages for the buffer to avoid overflowing the stack.
481 * Avoid the heap in case the crash is because the heap became corrupted. */
482 const size_t total_length = crash_desc_buf_length * sizeof(wchar_t) +
483 crashlog_length * sizeof(wchar_t) +
484 filename_buf_length * sizeof(wchar_t) * filename_count +
485 crashlog_length;
486 void *raw_buffer = VirtualAlloc(nullptr, total_length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
487
488 wchar_t *crash_desc_buf = reinterpret_cast<wchar_t *>(raw_buffer);
489 wchar_t *crashlog_buf = crash_desc_buf + crash_desc_buf_length;
490 wchar_t *filename_buf = crashlog_buf + crashlog_length;
491 char *crashlog_dos_nl = reinterpret_cast<char *>(filename_buf + filename_buf_length * filename_count);
492
493 /* Convert unix -> dos newlines because the edit box only supports that properly. */
494 const char *crashlog_unix_nl = crashlog.data();
495 char *p = crashlog_dos_nl;
496 char32_t c;
497 while ((c = Utf8Consume(&crashlog_unix_nl))) {
498 if (c == '\n') p += Utf8Encode(p, '\r');
499 p += Utf8Encode(p, c);
500 }
501 *p = '\0';
502
503 _snwprintf(
504 crash_desc_buf,
505 crash_desc_buf_length,
506 _crash_desc,
507 convert_to_fs(CrashLogWindows::current->crashlog_filename, filename_buf + filename_buf_length * 0, filename_buf_length),
508 convert_to_fs(CrashLogWindows::current->crashdump_filename, filename_buf + filename_buf_length * 1, filename_buf_length),
509 convert_to_fs(CrashLogWindows::current->savegame_filename, filename_buf + filename_buf_length * 2, filename_buf_length),
510 convert_to_fs(CrashLogWindows::current->screenshot_filename, filename_buf + filename_buf_length * 3, filename_buf_length)
511 );
512
513 SetDlgItemText(wnd, 10, crash_desc_buf);
514 SetDlgItemText(wnd, 11, convert_to_fs(crashlog_dos_nl, crashlog_buf, crashlog_length));
515 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
516 SetWndSize(wnd, -1);
517 } return TRUE;
518 case WM_COMMAND:
519 switch (wParam) {
520 case 12: // Close
521 CrashLog::AfterCrashLogCleanup();
522 ImmediateExitProcess(2);
523 case 15: // Expand window to show crash-message
524 _expanded = !_expanded;
525 SetWndSize(wnd, _expanded);
526 break;
527 }
528 return TRUE;
529 case WM_CLOSE:
530 CrashLog::AfterCrashLogCleanup();
531 ImmediateExitProcess(2);
532 }
533
534 return FALSE;
535 }
536
537 static void ShowCrashlogWindow()
538 {
539 ShowCursor(TRUE);
540 ShowWindow(GetActiveWindow(), FALSE);
541 DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(100), nullptr, CrashDialogFunc);
542 }
CrashLogWindows::TryExecute
bool TryExecute(std::string_view section_name, std::function< bool()> &&func) override
Execute the func() and return its value.
Definition: crashlog_win.cpp:134
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:953
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:332
win32.h
_gamelog
Gamelog _gamelog
Gamelog instance.
Definition: gamelog.cpp:31
CrashLogWindows
Windows implementation for the crash logger.
Definition: crashlog_win.cpp:84
CrashLog::MakeCrashLog
void MakeCrashLog()
Makes the crash log, writes it to a file and then subsequently tries to make a crash dump and crash s...
Definition: crashlog.cpp:266
CrashLogWindows::CrashLogWindows
CrashLogWindows(EXCEPTION_POINTERS *ep=nullptr)
A crash log is always generated when it's generated.
Definition: crashlog_win.cpp:156
_safe_esp
thread_local void * _safe_esp
Stack pointer for use when 'starting' the crash handler.
Definition: crashlog_win.cpp:304
CrashLogWindows::SurveyCrash
void SurveyCrash(nlohmann::json &survey) const override
Convert system crash reason to JSON.
Definition: crashlog_win.cpp:88
CUSTOM_ABORT_EXCEPTION
static constexpr DWORD CUSTOM_ABORT_EXCEPTION
Exception code used for custom abort.
Definition: crashlog_win.cpp:40
Gamelog::TestEmergency
bool TestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition: gamelog.cpp:364
CrashLogWindows::internal_fault_jmp_buf
jmp_buf internal_fault_jmp_buf
Buffer to track the long jump set setup.
Definition: crashlog_win.cpp:163
LibraryLoader::HasError
bool HasError()
Check whether an error occurred while loading the library or a function.
Definition: library_loader.h:57
FS2OTTD
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition: win32.cpp:462
CrashLogWindows::ep
EXCEPTION_POINTERS * ep
Information about the encountered exception.
Definition: crashlog_win.cpp:86
CrashLog::InitThread
static void InitThread()
Prepare crash log handler for a newly started thread.
Definition: crashlog_osx.cpp:238
CrashLogWindows::current
static CrashLogWindows * current
Points to the current crash log.
Definition: crashlog_win.cpp:170
ImmediateExitProcess
static void ImmediateExitProcess(uint exit_code)
Forcefully try to terminate the application.
Definition: crashlog_win.cpp:74
SaveloadCrashWithMissingNewGRFs
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition: afterload.cpp:353
CrashLog
Helper class for creating crash logs.
Definition: crashlog.h:18
CrashLog::CreateFileName
std::string CreateFileName(const char *ext, bool with_dir=true) const
Create a timestamped filename.
Definition: crashlog.cpp:73
LibraryLoader
Definition: library_loader.h:13
CrashLogWindows::try_execute_active
bool try_execute_active
Whether we are in a TryExecute block.
Definition: crashlog_win.cpp:167
VideoDriver::GetInstance
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
Definition: video_driver.hpp:200
CrashLogWindows::SurveyStacktrace
void SurveyStacktrace(nlohmann::json &survey) const override
Convert stacktrace to JSON.
Definition: crashlog_win.cpp:291
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:233
convert_to_fs
wchar_t * convert_to_fs(const std::string_view name, wchar_t *system_buf, size_t buflen)
Convert from OpenTTD's encoding to that of the environment in UNICODE.
Definition: win32.cpp:518
LibraryLoader::GetFunction
Function GetFunction(const std::string &symbol_name)
Get a function from a loaded library.
Definition: library_loader.h:78
exception_code_to_name
static const std::map< DWORD, std::string > exception_code_to_name
A map between exception code and its name.
Definition: crashlog_win.cpp:43
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:300
Utf8Encode
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:479
CrashLog::message
static std::string message
Error message coming from #FatalError(format, ...).
Definition: crashlog.h:21
CrashLog::WriteCrashDump
virtual bool WriteCrashDump()
Write the (crash) dump to a file.
Definition: crashlog.cpp:206
OTTD2FS
std::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
Definition: win32.cpp:479