OpenTTD Source  20240919-master-gdf0233f4c2
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 
164 #endif
165 
167  bool try_execute_active = false;
168 
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) {
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);
325  }
326 
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);
334  }
335 
336  CrashLogWindows *log = new CrashLogWindows(ep);
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 
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 {
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
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:
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:869
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:336
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:270
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:337
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:351
CrashLog
Helper class for creating crash logs.
Definition: crashlog.h:18
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:280
CrashLog::CreateFileName
std::string CreateFileName(const char *ext, bool with_dir=true) const
Create a timestamped filename.
Definition: crashlog.cpp:74
LibraryLoader
Definition: library_loader.h:13
convert_to_fs
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
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:201
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
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
Utf8Encode
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:460
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:210
OTTD2FS
std::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
Definition: win32.cpp:354