OpenTTD Source  20241108-master-g80f628063a
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)
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  * 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 
300 extern bool CloseConsoleLogIfActive();
301 static void ShowCrashlogWindow();
302 
307 thread_local void *_safe_esp = nullptr;
308 
309 static 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 
364 static 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 
387 static 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 
436 static bool _expanded;
437 
438 static 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 
445 static const wchar_t * const _expand_texts[] = {L"S&how report >>", L"&Hide report <<" };
446 
447 static 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 
468 static 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::count(crashlog.begin(), crashlog.end(), '\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 
540 static 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?
Definition: afterload.cpp:352
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:21
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