OpenTTD Source 20241224-master-gf74b0cf984
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 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 * The total initialised size must be sufficient for a null-terminating char at sym_info->Name[sym_info->MaxNameLength],
240 * SymGetSymFromAddr64 is not required to write a null-terminating char.
241 * sizeof(IMAGEHLP_SYMBOL64) includes at least one char of the Name buffer. */
242 std::array<char, sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMBOL_LEN> sym_info_raw{};
243 IMAGEHLP_SYMBOL64 *sym_info = reinterpret_cast<IMAGEHLP_SYMBOL64*>(sym_info_raw.data());
244 sym_info->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
245 sym_info->MaxNameLength = MAX_SYMBOL_LEN;
246
247 /* Walk stack at most MAX_FRAMES deep in case the stack is corrupt. */
248 for (uint num = 0; num < MAX_FRAMES; num++) {
249 if (!proc.pStackWalk64(
250#ifdef _M_AMD64
251 IMAGE_FILE_MACHINE_AMD64,
252#else
253 IMAGE_FILE_MACHINE_I386,
254#endif
255 hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
256
257 if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
258 survey.push_back("<infinite loop>");
259 break;
260 }
261
262 /* Get module name. */
263 const char *mod_name = "???";
264
265 IMAGEHLP_MODULE64 module;
266 module.SizeOfStruct = sizeof(module);
267 if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) {
268 mod_name = module.ModuleName;
269 }
270
271 /* Print module and instruction pointer. */
272 std::string message = fmt::format("{:20s} {:X}", mod_name, frame.AddrPC.Offset);
273
274 /* Get symbol name and line info if possible. */
275 DWORD64 offset;
276 if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
277 message += fmt::format(" {} + {}", sym_info->Name, offset);
278
279 DWORD line_offs;
280 IMAGEHLP_LINE64 line;
281 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
282 if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
283 message += fmt::format(" ({}:{})", line.FileName, line.LineNumber);
284 }
285 }
286
287 survey.push_back(message);
288 }
289
290 proc.pSymCleanup(hCur);
291 }
292}
293#else
294/* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &) const
295{
296 /* Not supported. */
297}
298#endif /* _MSC_VER */
299
300extern bool CloseConsoleLogIfActive();
301static void ShowCrashlogWindow();
302
307thread_local void *_safe_esp = nullptr;
308
309static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
310{
311 /* Restore system timer resolution. */
312 timeEndPeriod(1);
313
314 /* Disable our event loop. */
315 SetWindowLongPtr(GetActiveWindow(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);
316
317 if (CrashLogWindows::current != nullptr) {
320 }
321
322 if (_gamelog.TestEmergency()) {
323 static const wchar_t _emergency_crash[] =
324 L"A serious fault condition occurred in the game. The game will shut down.\n"
325 L"As you loaded an emergency savegame no crash information will be generated.\n";
326 MessageBox(nullptr, _emergency_crash, L"Fatal Application Failure", MB_ICONERROR);
328 }
329
331 static const wchar_t _saveload_crash[] =
332 L"A serious fault condition occurred in the game. The game will shut down.\n"
333 L"As you loaded an savegame for which you do not have the required NewGRFs\n"
334 L"no crash information will be generated.\n";
335 MessageBox(nullptr, _saveload_crash, L"Fatal Application Failure", MB_ICONERROR);
337 }
338
339 CrashLogWindows *log = new CrashLogWindows(ep);
341 log->MakeCrashLog();
342
343 /* Close any possible log files */
344 CloseConsoleLogIfActive();
345
346 if ((VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) && _safe_esp != nullptr) {
347#ifdef _M_AMD64
348 ep->ContextRecord->Rip = (DWORD64)ShowCrashlogWindow;
349 ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
350#elif defined(_M_IX86)
351 ep->ContextRecord->Eip = (DWORD)ShowCrashlogWindow;
352 ep->ContextRecord->Esp = (DWORD)_safe_esp;
353#elif defined(_M_ARM64)
354 ep->ContextRecord->Pc = (DWORD64)ShowCrashlogWindow;
355 ep->ContextRecord->Sp = (DWORD64)_safe_esp;
356#endif
357 return EXCEPTION_CONTINUE_EXECUTION;
358 }
359
361 return EXCEPTION_EXECUTE_HANDLER;
362}
363
364static LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS *ep)
365{
366 if (CrashLogWindows::current != nullptr && CrashLogWindows::current->try_execute_active) {
367#if defined(_MSC_VER)
368 return EXCEPTION_CONTINUE_SEARCH;
369#else
370 longjmp(CrashLogWindows::current->internal_fault_jmp_buf, 1);
371#endif
372 }
373
374 if (ep->ExceptionRecord->ExceptionCode == 0xC0000374 /* heap corruption */) {
375 return ExceptionHandler(ep);
376 }
377 if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
378 return ExceptionHandler(ep);
379 }
380 if (ep->ExceptionRecord->ExceptionCode == CUSTOM_ABORT_EXCEPTION) {
381 return ExceptionHandler(ep);
382 }
383
384 return EXCEPTION_CONTINUE_SEARCH;
385}
386
387static void CDECL CustomAbort(int)
388{
389 RaiseException(CUSTOM_ABORT_EXCEPTION, 0, 0, nullptr);
390}
391
392/* static */ void CrashLog::InitialiseCrashLog()
393{
395
396 /* SIGABRT is not an unhandled exception, so we need to intercept it. */
397 signal(SIGABRT, CustomAbort);
398#if defined(_MSC_VER)
399 /* Don't show abort message as we will get the crashlog window anyway. */
400 _set_abort_behavior(0, _WRITE_ABORT_MSG);
401#endif
402 SetUnhandledExceptionFilter(ExceptionHandler);
403 AddVectoredExceptionHandler(1, VectoredExceptionHandler);
404}
405
406/* static */ void CrashLog::InitThread()
407{
408#if defined(_M_AMD64) || defined(_M_ARM64)
409 CONTEXT ctx;
410 RtlCaptureContext(&ctx);
411
412 /* The stack pointer for AMD64 must always be 16-byte aligned inside a
413 * function. As we are simulating a function call with the safe ESP value,
414 * we need to subtract 8 for the imaginary return address otherwise stack
415 * alignment would be wrong in the called function. */
416# if defined(_M_ARM64)
417 _safe_esp = (void *)(ctx.Sp - 8);
418# else
419 _safe_esp = (void *)(ctx.Rsp - 8);
420# endif
421#else
422 void *safe_esp;
423# if defined(_MSC_VER)
424 _asm {
425 mov safe_esp, esp
426 }
427# else
428 asm("movl %%esp, %0" : "=rm" (safe_esp));
429# endif
430 _safe_esp = safe_esp;
431#endif
432}
433
434/* The crash log GUI */
435
436static bool _expanded;
437
438static const wchar_t _crash_desc[] =
439 L"A serious fault condition occurred in the game. The game will shut down.\n"
440 L"Please send crash.json.log, crash.dmp, and crash.sav to the developers.\n"
441 L"This will greatly help debugging.\n\n"
442 L"https://github.com/OpenTTD/OpenTTD/issues\n\n"
443 L"%s\n%s\n%s\n%s\n";
444
445static const wchar_t * const _expand_texts[] = {L"S&how report >>", L"&Hide report <<" };
446
447static void SetWndSize(HWND wnd, int mode)
448{
449 RECT r, r2;
450
451 GetWindowRect(wnd, &r);
452 SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
453
454 if (mode >= 0) {
455 GetWindowRect(GetDlgItem(wnd, 11), &r2);
456 int offs = r2.bottom - r2.top + 10;
457 if (mode == 0) offs = -offs;
458 SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
459 r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
460 } else {
461 SetWindowPos(wnd, HWND_TOPMOST,
462 (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
463 (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
464 0, 0, SWP_NOSIZE);
465 }
466}
467
468static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM)
469{
470 switch (msg) {
471 case WM_INITDIALOG: {
472 std::string crashlog = CrashLogWindows::current->survey.dump(4);
473 size_t crashlog_length = crashlog.size() + 1;
474 /* Reserve extra space for LF to CRLF conversion. */
475 crashlog_length += std::ranges::count(crashlog, '\n');
476
477 const size_t filename_count = 4;
478 const size_t filename_buf_length = MAX_PATH + 1;
479 const size_t crash_desc_buf_length = lengthof(_crash_desc) + filename_buf_length * filename_count + 1;
480
481 /* We need to put the crash-log in a separate buffer because the default
482 * buffer in MB_TO_WIDE is not large enough (512 chars).
483 * Use VirtualAlloc to allocate pages for the buffer to avoid overflowing the stack.
484 * Avoid the heap in case the crash is because the heap became corrupted. */
485 const size_t total_length = crash_desc_buf_length * sizeof(wchar_t) +
486 crashlog_length * sizeof(wchar_t) +
487 filename_buf_length * sizeof(wchar_t) * filename_count +
488 crashlog_length;
489 void *raw_buffer = VirtualAlloc(nullptr, total_length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
490
491 wchar_t *crash_desc_buf = reinterpret_cast<wchar_t *>(raw_buffer);
492 wchar_t *crashlog_buf = crash_desc_buf + crash_desc_buf_length;
493 wchar_t *filename_buf = crashlog_buf + crashlog_length;
494 char *crashlog_dos_nl = reinterpret_cast<char *>(filename_buf + filename_buf_length * filename_count);
495
496 /* Convert unix -> dos newlines because the edit box only supports that properly. */
497 const char *crashlog_unix_nl = crashlog.data();
498 char *p = crashlog_dos_nl;
499 char32_t c;
500 while ((c = Utf8Consume(&crashlog_unix_nl))) {
501 if (c == '\n') p += Utf8Encode(p, '\r');
502 p += Utf8Encode(p, c);
503 }
504 *p = '\0';
505
506 _snwprintf(
507 crash_desc_buf,
508 crash_desc_buf_length,
509 _crash_desc,
510 convert_to_fs(CrashLogWindows::current->crashlog_filename, {filename_buf + filename_buf_length * 0, filename_buf_length}),
511 convert_to_fs(CrashLogWindows::current->crashdump_filename, {filename_buf + filename_buf_length * 1, filename_buf_length}),
512 convert_to_fs(CrashLogWindows::current->savegame_filename, {filename_buf + filename_buf_length * 2, filename_buf_length}),
513 convert_to_fs(CrashLogWindows::current->screenshot_filename, {filename_buf + filename_buf_length * 3, filename_buf_length})
514 );
515
516 SetDlgItemText(wnd, 10, crash_desc_buf);
517 SetDlgItemText(wnd, 11, convert_to_fs(crashlog_dos_nl, {crashlog_buf, crashlog_length}));
518 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
519 SetWndSize(wnd, -1);
520 } return TRUE;
521 case WM_COMMAND:
522 switch (wParam) {
523 case 12: // Close
526 case 15: // Expand window to show crash-message
527 _expanded = !_expanded;
528 SetWndSize(wnd, _expanded);
529 break;
530 }
531 return TRUE;
532 case WM_CLOSE:
535 }
536
537 return FALSE;
538}
539
540static void ShowCrashlogWindow()
541{
542 ShowCursor(TRUE);
543 ShowWindow(GetActiveWindow(), FALSE);
544 DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(100), nullptr, CrashDialogFunc);
545}
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:270
std::string CreateFileName(const char *ext, bool with_dir=true) const
Create a timestamped filename.
Definition crashlog.cpp:74
static std::string message
Error message coming from FatalError(format, ...).
Definition crashlog.h:32
virtual bool WriteCrashDump()
Write the (crash) dump to a file.
Definition crashlog.cpp:210
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:336
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:869
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:280
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition string.cpp:460
std::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
Definition win32.cpp:354
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition win32.cpp:337
wchar_t * convert_to_fs(const 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