OpenTTD Source 20250522-master-g467f832c2f
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
40static constexpr DWORD CUSTOM_ABORT_EXCEPTION = 0xE1212012;
41
43static 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
84class 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;
99public:
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)
164#endif
165
167 bool try_execute_active = false;
168
171};
172
173/* static */ CrashLogWindows *CrashLogWindows::current = nullptr;
174
175#if defined(_MSC_VER)
176static const uint MAX_SYMBOL_LEN = 512;
177static 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#ifdef _M_AMD64
217 frame.AddrPC.Offset = ep->ContextRecord->Rip;
218 frame.AddrFrame.Offset = ep->ContextRecord->Rbp;
219 frame.AddrStack.Offset = ep->ContextRecord->Rsp;
220#elif defined(_M_IX86)
221 frame.AddrPC.Offset = ep->ContextRecord->Eip;
222 frame.AddrFrame.Offset = ep->ContextRecord->Ebp;
223 frame.AddrStack.Offset = ep->ContextRecord->Esp;
224#elif defined(_M_ARM64)
225 frame.AddrPC.Offset = ep->ContextRecord->Pc;
226 frame.AddrFrame.Offset = ep->ContextRecord->Fp;
227 frame.AddrStack.Offset = ep->ContextRecord->Sp;
228#endif
229 frame.AddrPC.Mode = AddrModeFlat;
230 frame.AddrFrame.Mode = AddrModeFlat;
231 frame.AddrStack.Mode = AddrModeFlat;
232
233 /* Copy context record as StackWalk64 may modify it. */
234 CONTEXT ctx = *ep->ContextRecord;
235
236 /* Allocate space for symbol info.
237 * The total initialised size must be sufficient for a null-terminating char at sym_info->Name[sym_info->MaxNameLength],
238 * SymGetSymFromAddr64 is not required to write a null-terminating char.
239 * sizeof(IMAGEHLP_SYMBOL64) includes at least one char of the Name buffer. */
240 std::array<char, sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMBOL_LEN> sym_info_raw{};
241 IMAGEHLP_SYMBOL64 *sym_info = reinterpret_cast<IMAGEHLP_SYMBOL64*>(sym_info_raw.data());
242 sym_info->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
243 sym_info->MaxNameLength = MAX_SYMBOL_LEN;
244
245 /* Walk stack at most MAX_FRAMES deep in case the stack is corrupt. */
246 for (uint num = 0; num < MAX_FRAMES; num++) {
247 if (!proc.pStackWalk64(
248#ifdef _M_AMD64
249 IMAGE_FILE_MACHINE_AMD64,
250#else
251 IMAGE_FILE_MACHINE_I386,
252#endif
253 hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
254
255 if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
256 survey.push_back("<infinite loop>");
257 break;
258 }
259
260 /* Get module name. */
261 std::string_view mod_name = "???";
262
263 IMAGEHLP_MODULE64 module;
264 module.SizeOfStruct = sizeof(module);
265 if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) {
266 mod_name = module.ModuleName;
267 }
268
269 /* Print module and instruction pointer. */
270 std::string message = fmt::format("{:20s} {:X}", mod_name, frame.AddrPC.Offset);
271
272 /* Get symbol name and line info if possible. */
273 DWORD64 offset;
274 if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
275 format_append(message, " {} + {}", sym_info->Name, offset);
276
277 DWORD line_offs;
278 IMAGEHLP_LINE64 line;
279 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
280 if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
281 format_append(message, " ({}:{})", line.FileName, line.LineNumber);
282 }
283 }
284
285 survey.push_back(message);
286 }
287
288 proc.pSymCleanup(hCur);
289 }
290}
291#else
292/* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &) const
293{
294 /* Not supported. */
295}
296#endif /* _MSC_VER */
297
298extern bool CloseConsoleLogIfActive();
299static void ShowCrashlogWindow();
300
305thread_local void *_safe_esp = nullptr;
306
307static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
308{
309 /* Restore system timer resolution. */
310 timeEndPeriod(1);
311
312 /* Disable our event loop. */
313 SetWindowLongPtr(GetActiveWindow(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);
314
315 if (CrashLogWindows::current != nullptr) {
318 }
319
320 if (_gamelog.TestEmergency()) {
321 static const wchar_t _emergency_crash[] =
322 L"A serious fault condition occurred in the game. The game will shut down.\n"
323 L"As you loaded an emergency savegame no crash information will be generated.\n";
324 MessageBox(nullptr, _emergency_crash, L"Fatal Application Failure", MB_ICONERROR);
326 }
327
329 static const wchar_t _saveload_crash[] =
330 L"A serious fault condition occurred in the game. The game will shut down.\n"
331 L"As you loaded an savegame for which you do not have the required NewGRFs\n"
332 L"no crash information will be generated.\n";
333 MessageBox(nullptr, _saveload_crash, L"Fatal Application Failure", MB_ICONERROR);
335 }
336
337 CrashLogWindows *log = new CrashLogWindows(ep);
339 log->MakeCrashLog();
340
341 /* Close any possible log files */
342 CloseConsoleLogIfActive();
343
344 if ((VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) && _safe_esp != nullptr) {
345#ifdef _M_AMD64
346 ep->ContextRecord->Rip = (DWORD64)ShowCrashlogWindow;
347 ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
348#elif defined(_M_IX86)
349 ep->ContextRecord->Eip = (DWORD)ShowCrashlogWindow;
350 ep->ContextRecord->Esp = (DWORD)_safe_esp;
351#elif defined(_M_ARM64)
352 ep->ContextRecord->Pc = (DWORD64)ShowCrashlogWindow;
353 ep->ContextRecord->Sp = (DWORD64)_safe_esp;
354#endif
355 return EXCEPTION_CONTINUE_EXECUTION;
356 }
357
359 return EXCEPTION_EXECUTE_HANDLER;
360}
361
362static LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS *ep)
363{
364 if (CrashLogWindows::current != nullptr && CrashLogWindows::current->try_execute_active) {
365#if defined(_MSC_VER)
366 return EXCEPTION_CONTINUE_SEARCH;
367#else
368 longjmp(CrashLogWindows::current->internal_fault_jmp_buf, 1);
369#endif
370 }
371
372 if (ep->ExceptionRecord->ExceptionCode == 0xC0000374 /* heap corruption */) {
373 return ExceptionHandler(ep);
374 }
375 if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
376 return ExceptionHandler(ep);
377 }
378 if (ep->ExceptionRecord->ExceptionCode == CUSTOM_ABORT_EXCEPTION) {
379 return ExceptionHandler(ep);
380 }
381
382 return EXCEPTION_CONTINUE_SEARCH;
383}
384
385static void CDECL CustomAbort(int)
386{
387 RaiseException(CUSTOM_ABORT_EXCEPTION, 0, 0, nullptr);
388}
389
390/* static */ void CrashLog::InitialiseCrashLog()
391{
393
394 /* SIGABRT is not an unhandled exception, so we need to intercept it. */
395 signal(SIGABRT, CustomAbort);
396#if defined(_MSC_VER)
397 /* Don't show abort message as we will get the crashlog window anyway. */
398 _set_abort_behavior(0, _WRITE_ABORT_MSG);
399#endif
400 SetUnhandledExceptionFilter(ExceptionHandler);
401 AddVectoredExceptionHandler(1, VectoredExceptionHandler);
402}
403
404/* static */ void CrashLog::InitThread()
405{
406#if defined(_M_AMD64) || defined(_M_ARM64)
407 CONTEXT ctx;
408 RtlCaptureContext(&ctx);
409
410 /* The stack pointer for AMD64 must always be 16-byte aligned inside a
411 * function. As we are simulating a function call with the safe ESP value,
412 * we need to subtract 8 for the imaginary return address otherwise stack
413 * alignment would be wrong in the called function. */
414# if defined(_M_ARM64)
415 _safe_esp = (void *)(ctx.Sp - 8);
416# else
417 _safe_esp = (void *)(ctx.Rsp - 8);
418# endif
419#else
420 void *safe_esp;
421# if defined(_MSC_VER)
422 _asm {
423 mov safe_esp, esp
424 }
425# else
426 asm("movl %%esp, %0" : "=rm" (safe_esp));
427# endif
428 _safe_esp = safe_esp;
429#endif
430}
431
432/* The crash log GUI */
433
434static bool _expanded;
435
436static const wchar_t _crash_desc[] =
437 L"A serious fault condition occurred in the game. The game will shut down.\n"
438 L"Please send crash.json.log, crash.dmp, and crash.sav to the developers.\n"
439 L"This will greatly help debugging.\n\n"
440 L"https://github.com/OpenTTD/OpenTTD/issues\n\n"
441 L"%s\n%s\n%s\n%s\n";
442
443static const wchar_t * const _expand_texts[] = {L"S&how report >>", L"&Hide report <<" };
444
445static void SetWndSize(HWND wnd, int mode)
446{
447 RECT r, r2;
448
449 GetWindowRect(wnd, &r);
450 SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
451
452 if (mode >= 0) {
453 GetWindowRect(GetDlgItem(wnd, 11), &r2);
454 int offs = r2.bottom - r2.top + 10;
455 if (mode == 0) offs = -offs;
456 SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
457 r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
458 } else {
459 SetWindowPos(wnd, HWND_TOPMOST,
460 (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
461 (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
462 0, 0, SWP_NOSIZE);
463 }
464}
465
466static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM)
467{
468 switch (msg) {
469 case WM_INITDIALOG: {
470 std::string crashlog = CrashLogWindows::current->survey.dump(4);
471 size_t crashlog_length = crashlog.size() + 1;
472 /* Reserve extra space for LF to CRLF conversion. */
473 crashlog_length += std::ranges::count(crashlog, '\n');
474
475 const size_t filename_count = 4;
476 const size_t filename_buf_length = MAX_PATH + 1;
477 const size_t crash_desc_buf_length = lengthof(_crash_desc) + filename_buf_length * filename_count + 1;
478
479 /* We need to put the crash-log in a separate buffer because the default
480 * buffer in MB_TO_WIDE is not large enough (512 chars).
481 * Use VirtualAlloc to allocate pages for the buffer to avoid overflowing the stack.
482 * Avoid the heap in case the crash is because the heap became corrupted. */
483 const size_t total_length = crash_desc_buf_length * sizeof(wchar_t) +
484 crashlog_length * sizeof(wchar_t) +
485 filename_buf_length * sizeof(wchar_t) * filename_count +
486 crashlog_length;
487 void *raw_buffer = VirtualAlloc(nullptr, total_length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
488
489 wchar_t *crash_desc_buf = reinterpret_cast<wchar_t *>(raw_buffer);
490 wchar_t *crashlog_buf = crash_desc_buf + crash_desc_buf_length;
491 wchar_t *filename_buf = crashlog_buf + crashlog_length;
492 char *crashlog_dos_nl = reinterpret_cast<char *>(filename_buf + filename_buf_length * filename_count);
493
494 /* Convert unix -> dos newlines because the edit box only supports that properly. */
495 {
496 char *p = crashlog_dos_nl;
497 for (char c : crashlog) {
498 if (c == '\n') *(p++) = '\r';
499 *(p++) = c;
500 }
501 *p = '\0';
502 }
503
504 _snwprintf(
505 crash_desc_buf,
506 crash_desc_buf_length,
507 _crash_desc,
508 convert_to_fs(CrashLogWindows::current->crashlog_filename, {filename_buf + filename_buf_length * 0, filename_buf_length}),
509 convert_to_fs(CrashLogWindows::current->crashdump_filename, {filename_buf + filename_buf_length * 1, filename_buf_length}),
510 convert_to_fs(CrashLogWindows::current->savegame_filename, {filename_buf + filename_buf_length * 2, filename_buf_length}),
511 convert_to_fs(CrashLogWindows::current->screenshot_filename, {filename_buf + filename_buf_length * 3, filename_buf_length})
512 );
513
514 SetDlgItemText(wnd, 10, crash_desc_buf);
515 SetDlgItemText(wnd, 11, convert_to_fs(crashlog_dos_nl, {crashlog_buf, crashlog_length}));
516 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
517 SetWndSize(wnd, -1);
518 } return TRUE;
519 case WM_COMMAND:
520 switch (wParam) {
521 case 12: // Close
524 case 15: // Expand window to show crash-message
525 _expanded = !_expanded;
526 SetWndSize(wnd, _expanded);
527 break;
528 }
529 return TRUE;
530 case WM_CLOSE:
533 }
534
535 return FALSE;
536}
537
538static void ShowCrashlogWindow()
539{
540 ShowCursor(TRUE);
541 ShowWindow(GetActiveWindow(), FALSE);
542 DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(100), nullptr, CrashDialogFunc);
543}
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Windows implementation for the crash logger.
static CrashLogWindows * current
Points to the current crash log.
void SurveyStacktrace(nlohmann::json &survey) const override
Convert stacktrace to JSON.
bool TryExecute(std::string_view section_name, std::function< bool()> &&func) override
Execute the func() and return its value.
bool try_execute_active
Whether we are in a TryExecute block.
EXCEPTION_POINTERS * ep
Information about the encountered exception.
jmp_buf internal_fault_jmp_buf
Buffer to track the long jump set setup.
CrashLogWindows(EXCEPTION_POINTERS *ep=nullptr)
A crash log is always generated when it's generated.
void SurveyCrash(nlohmann::json &survey) const override
Convert system crash reason to JSON.
Helper class for creating crash logs.
Definition crashlog.h:18
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:272
static std::string message
Error message coming from FatalError(format, ...).
Definition crashlog.h:33
virtual bool WriteCrashDump()
Write the (crash) dump to a file.
Definition crashlog.cpp:212
static void InitThread()
Prepare crash log handler for a newly started thread.
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:338
std::string CreateFileName(std::string_view ext, bool with_dir=true) const
Create a timestamped filename.
Definition crashlog.cpp:76
static void InitialiseCrashLog()
Initialiser for crash logs; do the appropriate things so crashes are handled by our crash handler ins...
bool TestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition gamelog.cpp:364
bool HasError()
Check whether an error occurred while loading the library or a function.
Function GetFunction(const std::string &symbol_name)
Get a function from a loaded library.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
static const std::map< DWORD, std::string > exception_code_to_name
A map between exception code and its name.
static void ImmediateExitProcess(uint exit_code)
Forcefully try to terminate the application.
static constexpr DWORD CUSTOM_ABORT_EXCEPTION
Exception code used for custom abort.
thread_local void * _safe_esp
Stack pointer for use when 'starting' the crash handler.
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition fileio.cpp:868
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
std::wstring OTTD2FS(std::string_view name)
Convert from OpenTTD's encoding to a wide string.
Definition win32.cpp:354
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.
Definition win32.cpp:337
wchar_t * convert_to_fs(std::string_view src, std::span< wchar_t > dst_buf)
Convert from OpenTTD's encoding to that of the environment in UNICODE.
Definition win32.cpp:389
declarations of functions for MS windows systems