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/>.
8 /** @file crashlog_unix.cpp Unix crash log handler */
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"
19 #include <sys/utsname.h>
21 #if defined(__GLIBC__)
22 /* Execinfo (and thus making stacktraces) is a GNU extension */
23 # include <execinfo.h>
26 #ifdef WITH_UNOFFICIAL_BREAKPAD
27 # include <client/linux/handler/exception_handler.h>
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
38 #include "../../safeguards.h"
40 /** The signals we want our crash handler to handle. */
41 static constexpr int _signals_to_handle
[] = { SIGSEGV
, SIGABRT
, SIGFPE
, SIGBUS
, SIGILL
, SIGQUIT
};
44 * Unix implementation for the crash logger.
46 class CrashLogUnix
: public CrashLog
{
47 /** Signal that has been thrown. */
50 void SurveyCrash(nlohmann::json
&survey
) const override
52 survey
["id"] = signum
;
53 survey
["reason"] = strsignal(signum
);
56 void SurveyStacktrace([[maybe_unused
]] nlohmann::json
&survey
) const override
58 #if defined(__GLIBC__)
60 int trace_size
= backtrace(trace
, lengthof(trace
));
62 survey
= nlohmann::json::array();
64 char **messages
= backtrace_symbols(trace
, trace_size
);
65 for (int i
= 0; i
< trace_size
; i
++) {
66 survey
.push_back(messages
[i
]);
72 #ifdef WITH_UNOFFICIAL_BREAKPAD
73 static bool MinidumpCallback(const google_breakpad::MinidumpDescriptor
&descriptor
, void *context
, bool succeeded
)
75 CrashLogUnix
*crashlog
= reinterpret_cast<CrashLogUnix
*>(context
);
77 crashlog
->crashdump_filename
= crashlog
->CreateFileName(".dmp");
78 std::rename(descriptor
.path(), crashlog
->crashdump_filename
.c_str());
82 bool WriteCrashDump() override
84 return google_breakpad::ExceptionHandler::WriteMinidump(_personal_dir
, MinidumpCallback
, this);
88 /* virtual */ bool TryExecute(std::string_view section_name
, std::function
<bool()> &&func
) override
90 this->try_execute_active
= true;
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
);
96 /* Reset the signals and continue on. The handler is responsible for dealing with the crash. */
99 for (int signum
: _signals_to_handle
) {
100 sigaddset(&sigs
, signum
);
102 sigprocmask(SIG_UNBLOCK
, &sigs
, nullptr);
104 this->try_execute_active
= false;
109 this->try_execute_active
= false;
115 * A crash log is always generated by signal.
116 * @param signum the signal that was caused by the crash.
118 CrashLogUnix(int signum
) :
123 /** Buffer to track the long jump set setup. */
124 jmp_buf internal_fault_jmp_buf
;
126 /** Whether we are in a TryExecute block. */
127 bool try_execute_active
= false;
129 /** Points to the current crash log. */
130 static CrashLogUnix
*current
;
133 /* static */ CrashLogUnix
*CrashLogUnix::current
= nullptr;
136 * Set a signal handler for all signals we want to capture.
138 * @param handler The handler to use.
139 * @return sigset_t A sigset_t containing all signals we want to capture.
141 static sigset_t
SetSignals(void(*handler
)(int))
145 for (int signum
: _signals_to_handle
) {
146 sigaddset(&sigs
, signum
);
150 memset(&sa
, 0, sizeof(sa
));
151 sa
.sa_flags
= SA_RESTART
;
153 sigemptyset(&sa
.sa_mask
);
154 sa
.sa_handler
= handler
;
157 for (int signum
: _signals_to_handle
) {
158 sigaction(signum
, &sa
, nullptr);
165 * Entry point for a crash that happened during the handling of a crash.
167 * @param signum the signal that caused us to crash.
169 static void CDECL
HandleInternalCrash([[maybe_unused
]] int signum
)
171 if (CrashLogUnix::current
== nullptr || !CrashLogUnix::current
->try_execute_active
) {
172 fmt::print("Something went seriously wrong when creating the crash log. Aborting.\n");
176 longjmp(CrashLogUnix::current
->internal_fault_jmp_buf
, 1);
180 * Entry point for the crash handler.
182 * @param signum the signal that caused us to crash.
184 static void CDECL
HandleCrash(int signum
)
186 if (CrashLogUnix::current
!= nullptr) {
187 CrashLog::AfterCrashLogCleanup();
191 /* Capture crashing during the handling of a crash. */
192 sigset_t sigs
= SetSignals(HandleInternalCrash
);
194 sigprocmask(SIG_UNBLOCK
, &sigs
, &old_sigset
);
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");
202 if (SaveloadCrashWithMissingNewGRFs()) {
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");
209 CrashLogUnix
*log
= new CrashLogUnix(signum
);
210 CrashLogUnix::current
= log
;
213 CrashLog::AfterCrashLogCleanup();
217 /* static */ void CrashLog::InitialiseCrashLog()
219 SetSignals(HandleCrash
);
222 /* static */ void CrashLog::InitThread()