OpenTTD Source  20241121-master-g67a0fccfad
crashlog_unix.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 "../../fileio_func.h"
13 #include "../../string_func.h"
14 #include "../../gamelog.h"
15 #include "../../saveload/saveload.h"
16 
17 #include <setjmp.h>
18 #include <signal.h>
19 #include <sys/utsname.h>
20 
21 #if defined(__GLIBC__)
22 /* Execinfo (and thus making stacktraces) is a GNU extension */
23 # include <execinfo.h>
24 #endif
25 
26 #ifdef WITH_UNOFFICIAL_BREAKPAD
27 # include <client/linux/handler/exception_handler.h>
28 #endif
29 
30 #if defined(__EMSCRIPTEN__)
31 # include <emscripten.h>
32 /* We avoid abort(), as it is a SIGBART, and use _exit() instead. But emscripten doesn't know _exit(). */
33 # define _exit emscripten_force_exit
34 #else
35 #include <unistd.h>
36 #endif
37 
38 #include "../../safeguards.h"
39 
41 static constexpr int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGQUIT };
42 
46 class CrashLogUnix : public CrashLog {
48  int signum;
49 
50  void SurveyCrash(nlohmann::json &survey) const override
51  {
52  survey["id"] = signum;
53  survey["reason"] = strsignal(signum);
54  }
55 
56  void SurveyStacktrace([[maybe_unused]] nlohmann::json &survey) const override
57  {
58 #if defined(__GLIBC__)
59  void *trace[64];
60  int trace_size = backtrace(trace, lengthof(trace));
61 
62  survey = nlohmann::json::array();
63 
64  char **messages = backtrace_symbols(trace, trace_size);
65  for (int i = 0; i < trace_size; i++) {
66  survey.push_back(messages[i]);
67  }
68  free(messages);
69 #endif
70  }
71 
72 #ifdef WITH_UNOFFICIAL_BREAKPAD
73  static bool MinidumpCallback(const google_breakpad::MinidumpDescriptor &descriptor, void *context, bool succeeded)
74  {
75  CrashLogUnix *crashlog = reinterpret_cast<CrashLogUnix *>(context);
76 
77  crashlog->crashdump_filename = crashlog->CreateFileName(".dmp");
78  std::rename(descriptor.path(), crashlog->crashdump_filename.c_str());
79  return succeeded;
80  }
81 
82  bool WriteCrashDump() override
83  {
84  return google_breakpad::ExceptionHandler::WriteMinidump(_personal_dir, MinidumpCallback, this);
85  }
86 #endif
87 
88  /* virtual */ bool TryExecute(std::string_view section_name, std::function<bool()> &&func) override
89  {
90  this->try_execute_active = true;
91 
92  /* Setup a longjump in case a crash happens. */
93  if (setjmp(this->internal_fault_jmp_buf) != 0) {
94  fmt::print("Something went wrong when attempting to fill {} section of the crash log.\n", section_name);
95 
96  /* Reset the signals and continue on. The handler is responsible for dealing with the crash. */
97  sigset_t sigs;
98  sigemptyset(&sigs);
99  for (int signum : _signals_to_handle) {
100  sigaddset(&sigs, signum);
101  }
102  sigprocmask(SIG_UNBLOCK, &sigs, nullptr);
103 
104  this->try_execute_active = false;
105  return false;
106  }
107 
108  bool res = func();
109  this->try_execute_active = false;
110  return res;
111  }
112 
113 public:
119  signum(signum)
120  {
121  }
122 
125 
127  bool try_execute_active = false;
128 
131 };
132 
133 /* static */ CrashLogUnix *CrashLogUnix::current = nullptr;
134 
141 static sigset_t SetSignals(void(*handler)(int))
142 {
143  sigset_t sigs;
144  sigemptyset(&sigs);
145  for (int signum : _signals_to_handle) {
146  sigaddset(&sigs, signum);
147  }
148 
149  struct sigaction sa;
150  memset(&sa, 0, sizeof(sa));
151  sa.sa_flags = SA_RESTART;
152 
153  sigemptyset(&sa.sa_mask);
154  sa.sa_handler = handler;
155  sa.sa_mask = sigs;
156 
157  for (int signum : _signals_to_handle) {
158  sigaction(signum, &sa, nullptr);
159  }
160 
161  return sigs;
162 }
163 
169 static void CDECL HandleInternalCrash([[maybe_unused]] int signum)
170 {
171  if (CrashLogUnix::current == nullptr || !CrashLogUnix::current->try_execute_active) {
172  fmt::print("Something went seriously wrong when creating the crash log. Aborting.\n");
173  _exit(1);
174  }
175 
176  longjmp(CrashLogUnix::current->internal_fault_jmp_buf, 1);
177 }
178 
184 static void CDECL HandleCrash(int signum)
185 {
186  if (CrashLogUnix::current != nullptr) {
188  _exit(2);
189  }
190 
191  /* Capture crashing during the handling of a crash. */
192  sigset_t sigs = SetSignals(HandleInternalCrash);
193  sigset_t old_sigset;
194  sigprocmask(SIG_UNBLOCK, &sigs, &old_sigset);
195 
196  if (_gamelog.TestEmergency()) {
197  fmt::print("A serious fault condition occurred in the game. The game will shut down.\n");
198  fmt::print("As you loaded an emergency savegame no crash information will be generated.\n");
199  _exit(3);
200  }
201 
203  fmt::print("A serious fault condition occurred in the game. The game will shut down.\n");
204  fmt::print("As you loaded an savegame for which you do not have the required NewGRFs\n");
205  fmt::print("no crash information will be generated.\n");
206  _exit(3);
207  }
208 
209  CrashLogUnix *log = new CrashLogUnix(signum);
210  CrashLogUnix::current = log;
211  log->MakeCrashLog();
212 
214  _exit(2);
215 }
216 
217 /* static */ void CrashLog::InitialiseCrashLog()
218 {
220 }
221 
222 /* static */ void CrashLog::InitThread()
223 {
224 }
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition: afterload.cpp:352
Unix implementation for the crash logger.
CrashLogUnix(int signum)
A crash log is always generated by signal.
void SurveyCrash(nlohmann::json &survey) const override
Convert system crash reason to JSON.
bool try_execute_active
Whether we are in a TryExecute block.
jmp_buf internal_fault_jmp_buf
Buffer to track the long jump set setup.
static CrashLogUnix * current
Points to the current crash log.
bool TryExecute(std::string_view section_name, std::function< bool()> &&func) override
Execute the func() and return its value.
int signum
Signal that has been thrown.
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
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
static void CDECL HandleInternalCrash([[maybe_unused]] int signum)
Entry point for a crash that happened during the handling of a crash.
static constexpr int _signals_to_handle[]
The signals we want our crash handler to handle.
static void CDECL HandleCrash(int signum)
Entry point for the crash handler.
static sigset_t SetSignals(void(*handler)(int))
Set a signal handler for all signals we want to capture.
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:869
Gamelog _gamelog
Gamelog instance.
Definition: gamelog.cpp:31
void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:334
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:280