OpenTTD Source: src/os/macosx/crashlog_osx.cpp Source File

OpenTTD Source 14.1

crashlog_osx.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 #include "../../video/video_driver.hpp"
17 #include "macos.h"
18
19 #include <setjmp.h>
20 #include <signal.h>
21 #include <mach-o/arch.h>
22 #include <dlfcn.h>
23 #include <cxxabi.h>
24 #include <execinfo.h>
25
26 #ifdef WITH_UNOFFICIAL_BREAKPAD
27 # include <client/mac/handler/exception_handler.h>
28 #endif
29
30 #include "../../safeguards.h"
31
32
33 /* Macro testing a stack address for valid alignment. */
34 #if defined(__i386__)
35 #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 8)
36 #else
37 #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0)
38 #endif
39
40 #define MAX_STACK_FRAMES 64
41
43 static constexpr int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGSYS, SIGQUIT };
44
48 class CrashLogOSX : public CrashLog {
50 int signum;
51
52 void SurveyCrash(nlohmann::json &survey) const override
53 {
54 survey["id"] = signum;
55 survey["reason"] = strsignal(signum);
56 }
57
58 void SurveyStacktrace(nlohmann::json &survey) const override
59 {
60 void *trace[64];
61 int trace_size = backtrace(trace, lengthof(trace));
62
63 survey = nlohmann::json::array();
64
65 char **messages = backtrace_symbols(trace, trace_size);
66 for (int i = 0; i < trace_size; i++) {
67 survey.push_back(messages[i]);
68 }
69 free(messages);
70 }
71
72 #ifdef WITH_UNOFFICIAL_BREAKPAD
73 static bool MinidumpCallback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded)
74 {
75 CrashLogOSX *crashlog = reinterpret_cast<CrashLogOSX *>(context);
76
77 crashlog->crashdump_filename = crashlog->CreateFileName(".dmp");
78 std::rename(fmt::format("{}/{}.dmp", dump_dir, minidump_id).c_str(), 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:
118 CrashLogOSX(int signum) : signum(signum) {}
119
121 void DisplayCrashDialog() const
122 {
123 static const char crash_title[] =
124 "A serious fault condition occurred in the game. The game will shut down.";
125
126 std::string message = fmt::format(
127 "Please send crash.json.log, crash.dmp, and crash.sav to the developers. "
128 "This will greatly help debugging.\n\n"
129 "https://github.com/OpenTTD/OpenTTD/issues.\n\n"
130 "{}\n{}\n{}\n{}",
131 this->crashlog_filename, this->crashdump_filename, this->savegame_filename, this->screenshot_filename);
132
133 ShowMacDialog(crash_title, message.c_str(), "Quit");
134 }
135
137 jmp_buf internal_fault_jmp_buf;
138
140 bool try_execute_active = false;
141
143 static CrashLogOSX *current;
144 };
145
146 /* static */ CrashLogOSX *CrashLogOSX::current = nullptr;
147
154 static sigset_t SetSignals(void(*handler)(int))
155 {
156 sigset_t sigs;
157 sigemptyset(&sigs);
158 for (int signum : _signals_to_handle) {
159 sigaddset(&sigs, signum);
160 }
161
162 struct sigaction sa;
163 memset(&sa, 0, sizeof(sa));
164 sa.sa_flags = SA_RESTART;
165
166 sigemptyset(&sa.sa_mask);
167 sa.sa_handler = handler;
168 sa.sa_mask = sigs;
169
170 for (int signum : _signals_to_handle) {
171 sigaction(signum, &sa, nullptr);
172 }
173
174 return sigs;
175 }
176
181 static void CDECL HandleInternalCrash(int)
182 {
183 if (CrashLogOSX::current == nullptr || !CrashLogOSX::current->try_execute_active) {
184 fmt::print("Something went seriously wrong when creating the crash log. Aborting.\n");
185 _exit(1);
186 }
187
188 longjmp(CrashLogOSX::current->internal_fault_jmp_buf, 1);
189 }
190
196 static void CDECL HandleCrash(int signum)
197 {
198 if (CrashLogOSX::current != nullptr) {
199 CrashLog::AfterCrashLogCleanup();
200 _exit(2);
201 }
202
203 /* Capture crashing during the handling of a crash. */
204 sigset_t sigs = SetSignals(HandleInternalCrash);
205 sigset_t old_sigset;
206 sigprocmask(SIG_UNBLOCK, &sigs, &old_sigset);
207
208 if (_gamelog.TestEmergency()) {
209 ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
210 "As you loaded an emergency savegame no crash information will be generated.\n",
211 "Quit");
212 _exit(3);
213 }
214
215 if (SaveloadCrashWithMissingNewGRFs()) {
216 ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
217 "As you loaded an savegame for which you do not have the required NewGRFs no crash information will be generated.\n",
218 "Quit");
219 _exit(3);
220 }
221
222 CrashLogOSX *log = new CrashLogOSX(signum);
223 CrashLogOSX::current = log;
224 log->MakeCrashLog();
225 if (VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) {
226 log->DisplayCrashDialog();
227 }
228
229 CrashLog::AfterCrashLogCleanup();
230 _exit(2);
231 }
232
233 /* static */ void CrashLog::InitialiseCrashLog()
234 {
235 SetSignals(HandleCrash);
236 }
237
238 /* static */ void CrashLog::InitThread()
239 {
240 }
CrashLogOSX
OSX implementation for the crash logger.
Definition: crashlog_osx.cpp:48
HandleCrash
static void CDECL HandleCrash(int signum)
Entry point for the crash handler.
Definition: crashlog_osx.cpp:196
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:966
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
_gamelog
Gamelog _gamelog
Gamelog instance.
Definition: gamelog.cpp:31
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
HandleInternalCrash
static void CDECL HandleInternalCrash(int)
Entry point for a crash that happened during the handling of a crash.
Definition: crashlog_osx.cpp:181
CrashLogOSX::SurveyStacktrace
void SurveyStacktrace(nlohmann::json &survey) const override
Convert stacktrace to JSON.
Definition: crashlog_osx.cpp:58
SetSignals
static sigset_t SetSignals(void(*handler)(int))
Set a signal handler for all signals we want to capture.
Definition: crashlog_osx.cpp:154
Gamelog::TestEmergency
bool TestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition: gamelog.cpp:364
CrashLogOSX::current
static CrashLogOSX * current
Points to the current crash log.
Definition: crashlog_osx.cpp:143
free
void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:382
CrashLog::InitThread
static void InitThread()
Prepare crash log handler for a newly started thread.
Definition: crashlog_osx.cpp:238
CrashLogOSX::signum
int signum
Signal that has been thrown.
Definition: crashlog_osx.cpp:50
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:303
CrashLogOSX::SurveyCrash
void SurveyCrash(nlohmann::json &survey) const override
Convert system crash reason to JSON.
Definition: crashlog_osx.cpp:52
CrashLog::CreateFileName
std::string CreateFileName(const char *ext, bool with_dir=true) const
Create a timestamped filename.
Definition: crashlog.cpp:73
ShowMacDialog
void ShowMacDialog(const char *title, const char *message, const char *button_label)
Helper function displaying a message the best possible way.
_signals_to_handle
static constexpr int _signals_to_handle[]
The signals we want our crash handler to handle.
Definition: crashlog_osx.cpp:43
VideoDriver::GetInstance
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
Definition: video_driver.hpp:201
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
CrashLogOSX::internal_fault_jmp_buf
jmp_buf internal_fault_jmp_buf
Buffer to track the long jump set setup.
Definition: crashlog_osx.cpp:137
CrashLogOSX::CrashLogOSX
CrashLogOSX(int signum)
A crash log is always generated by signal.
Definition: crashlog_osx.cpp:118
CrashLogOSX::TryExecute
bool TryExecute(std::string_view section_name, std::function< bool()> &&func) override
Execute the func() and return its value.
Definition: crashlog_osx.cpp:88
CrashLogOSX::DisplayCrashDialog
void DisplayCrashDialog() const
Show a dialog with the crash information.
Definition: crashlog_osx.cpp:121
macos.h
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
CrashLogOSX::try_execute_active
bool try_execute_active
Whether we are in a TryExecute block.
Definition: crashlog_osx.cpp:140