OpenTTD Source: src/os/unix/crashlog_unix.cpp Source File

OpenTTD Source 15.0-RC2

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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
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
113public:
118 CrashLogUnix(int signum) :
119 signum(signum)
120 {
121 }
122
124 jmp_buf internal_fault_jmp_buf{};
125
127 bool try_execute_active = false;
128
130 static CrashLogUnix *current;
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 sa.sa_flags = SA_RESTART;
151
152 sigemptyset(&sa.sa_mask);
153 sa.sa_handler = handler;
154 sa.sa_mask = sigs;
155
156 for (int signum : _signals_to_handle) {
157 sigaction(signum, &sa, nullptr);
158 }
159
160 return sigs;
161}
162
168 static void CDECL HandleInternalCrash([[maybe_unused]] int signum)
169{
170 if (CrashLogUnix::current == nullptr || !CrashLogUnix::current->try_execute_active) {
171 fmt::print("Something went seriously wrong when creating the crash log. Aborting.\n");
172 _exit(1);
173 }
174
175 longjmp(CrashLogUnix::current->internal_fault_jmp_buf, 1);
176}
177
183 static void CDECL HandleCrash(int signum)
184{
185 if (CrashLogUnix::current != nullptr) {
186 CrashLog::AfterCrashLogCleanup();
187 _exit(2);
188 }
189
190 /* Capture crashing during the handling of a crash. */
191 sigset_t sigs = SetSignals(HandleInternalCrash);
192 sigset_t old_sigset;
193 sigprocmask(SIG_UNBLOCK, &sigs, &old_sigset);
194
195 if (_gamelog.TestEmergency()) {
196 fmt::print("A serious fault condition occurred in the game. The game will shut down.\n");
197 fmt::print("As you loaded an emergency savegame no crash information will be generated.\n");
198 _exit(3);
199 }
200
201 if (SaveloadCrashWithMissingNewGRFs()) {
202 fmt::print("A serious fault condition occurred in the game. The game will shut down.\n");
203 fmt::print("As you loaded an savegame for which you do not have the required NewGRFs\n");
204 fmt::print("no crash information will be generated.\n");
205 _exit(3);
206 }
207
208 CrashLogUnix *log = new CrashLogUnix(signum);
209 CrashLogUnix::current = log;
210 log->MakeCrashLog();
211
212 CrashLog::AfterCrashLogCleanup();
213 _exit(2);
214}
215
216/* static */ void CrashLog::InitialiseCrashLog()
217{
218 SetSignals(HandleCrash);
219}
220
221/* static */ void CrashLog::InitThread()
222{
223}
SaveloadCrashWithMissingNewGRFs
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition afterload.cpp:349
CrashLogUnix
Unix implementation for the crash logger.
Definition crashlog_unix.cpp:46
CrashLogUnix::CrashLogUnix
CrashLogUnix(int signum)
A crash log is always generated by signal.
Definition crashlog_unix.cpp:118
CrashLogUnix::SurveyCrash
void SurveyCrash(nlohmann::json &survey) const override
Convert system crash reason to JSON.
Definition crashlog_unix.cpp:50
CrashLogUnix::try_execute_active
bool try_execute_active
Whether we are in a TryExecute block.
Definition crashlog_unix.cpp:127
CrashLogUnix::internal_fault_jmp_buf
jmp_buf internal_fault_jmp_buf
Buffer to track the long jump set setup.
Definition crashlog_unix.cpp:124
CrashLogUnix::current
static CrashLogUnix * current
Points to the current crash log.
Definition crashlog_unix.cpp:130
CrashLogUnix::TryExecute
bool TryExecute(std::string_view section_name, std::function< bool()> &&func) override
Execute the func() and return its value.
Definition crashlog_unix.cpp:88
CrashLogUnix::SurveyStacktrace
void SurveyStacktrace(nlohmann::json &survey) const override
Convert stacktrace to JSON.
Definition crashlog_unix.cpp:56
CrashLogUnix::signum
int signum
Signal that has been thrown.
Definition crashlog_unix.cpp:48
CrashLog
Helper class for creating crash logs.
Definition crashlog.h:18
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:272
CrashLog::WriteCrashDump
virtual bool WriteCrashDump()
Write the (crash) dump to a file.
Definition crashlog.cpp:212
CrashLog::InitThread
static void InitThread()
Prepare crash log handler for a newly started thread.
Definition crashlog_osx.cpp:237
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:338
CrashLog::CreateFileName
std::string CreateFileName(std::string_view ext, bool with_dir=true) const
Create a timestamped filename.
Definition crashlog.cpp:76
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:232
Gamelog::TestEmergency
bool TestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition gamelog.cpp:364
HandleCrash
static void CDECL HandleCrash(int signum)
Entry point for the crash handler.
Definition crashlog_osx.cpp:195
SetSignals
static sigset_t SetSignals(void(*handler)(int))
Set a signal handler for all signals we want to capture.
Definition crashlog_osx.cpp:154
_signals_to_handle
static constexpr int _signals_to_handle[]
The signals we want our crash handler to handle.
Definition crashlog_unix.cpp:41
HandleCrash
static void CDECL HandleCrash(int signum)
Entry point for the crash handler.
Definition crashlog_unix.cpp:183
HandleInternalCrash
static void CDECL HandleInternalCrash(int signum)
Entry point for a crash that happened during the handling of a crash.
Definition crashlog_unix.cpp:168
SetSignals
static sigset_t SetSignals(void(*handler)(int))
Set a signal handler for all signals we want to capture.
Definition crashlog_unix.cpp:141
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition fileio.cpp:871
_gamelog
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271