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

OpenTTD Source 12.2

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 "../../string_func.h"
13 #include "../../gamelog.h"
14 #include "../../saveload/saveload.h"
15 #include "../../video/video_driver.hpp"
16 #include "macos.h"
17
18 #include <errno.h>
19 #include <signal.h>
20 #include <mach-o/arch.h>
21 #include <dlfcn.h>
22 #include <cxxabi.h>
23
24 #include "../../safeguards.h"
25
26
27 /* Macro testing a stack address for valid alignment. */
28 #if defined(__i386__)
29 #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 8)
30 #else
31 #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0)
32 #endif
33
34 /* printf format specification for 32/64-bit addresses. */
35 #ifdef __LP64__
36 #define PRINTF_PTR "0x%016lx"
37 #else
38 #define PRINTF_PTR "0x%08lx"
39 #endif
40
41 #define MAX_STACK_FRAMES 64
42
46 class CrashLogOSX : public CrashLog {
48 int signum;
49
50 char filename_log[MAX_PATH];
51 char filename_save[MAX_PATH];
52 char filename_screenshot[MAX_PATH];
53
54 char *LogOSVersion(char *buffer, const char *last) const override
55 {
56 int ver_maj, ver_min, ver_bug;
57 GetMacOSVersion(&ver_maj, &ver_min, &ver_bug);
58
59 const NXArchInfo *arch = NXGetLocalArchInfo();
60
61 return buffer + seprintf(buffer, last,
62 "Operating system:\n"
63 " Name: Mac OS X\n"
64 " Release: %d.%d.%d\n"
65 " Machine: %s\n"
66 " Min Ver: %d\n"
67 " Max Ver: %d\n",
68 ver_maj, ver_min, ver_bug,
69 arch != nullptr ? arch->description : "unknown",
70 MAC_OS_X_VERSION_MIN_REQUIRED,
71 MAC_OS_X_VERSION_MAX_ALLOWED
72 );
73 }
74
75 char *LogError(char *buffer, const char *last, const char *message) const override
76 {
77 return buffer + seprintf(buffer, last,
78 "Crash reason:\n"
79 " Signal: %s (%d)\n"
80 " Message: %s\n\n",
81 strsignal(this->signum),
82 this->signum,
83 message == nullptr ? "<none>" : message
84 );
85 }
86
87 char *LogStacktrace(char *buffer, const char *last) const override
88 {
89 /* As backtrace() is only implemented in 10.5 or later,
90 * we're rolling our own here. Mostly based on
91 * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x
92 * and some details looked up in the Darwin sources. */
93 buffer += seprintf(buffer, last, "\nStacktrace:\n");
94
95 void **frame;
96 #if defined(__ppc__) || defined(__ppc64__)
97 /* Apple says __builtin_frame_address can be broken on PPC. */
98 __asm__ volatile("mr %0, r1" : "=r" (frame));
99 #else
100 frame = (void **)__builtin_frame_address(0);
101 #endif
102
103 for (int i = 0; frame != nullptr && i < MAX_STACK_FRAMES; i++) {
104 /* Get IP for current stack frame. */
105 #if defined(__ppc__) || defined(__ppc64__)
106 void *ip = frame[2];
107 #else
108 void *ip = frame[1];
109 #endif
110 if (ip == nullptr) break;
111
112 /* Print running index. */
113 buffer += seprintf(buffer, last, " [%02d]", i);
114
115 Dl_info dli;
116 bool dl_valid = dladdr(ip, &dli) != 0;
117
118 const char *fname = "???";
119 if (dl_valid && dli.dli_fname) {
120 /* Valid image name? Extract filename from the complete path. */
121 const char *s = strrchr(dli.dli_fname, '/');
122 if (s != nullptr) {
123 fname = s + 1;
124 } else {
125 fname = dli.dli_fname;
126 }
127 }
128 /* Print image name and IP. */
129 buffer += seprintf(buffer, last, " %-20s " PRINTF_PTR, fname, (uintptr_t)ip);
130
131 /* Print function offset if information is available. */
132 if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) {
133 /* Try to demangle a possible C++ symbol. */
134 int status = -1;
135 char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status);
136
137 long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr;
138 buffer += seprintf(buffer, last, " (%s + %ld)", func_name != nullptr ? func_name : dli.dli_sname, offset);
139
140 free(func_name);
141 }
142 buffer += seprintf(buffer, last, "\n");
143
144 /* Get address of next stack frame. */
145 void **next = (void **)frame[0];
146 /* Frame address not increasing or not aligned? Broken stack, exit! */
147 if (next <= frame || !IS_ALIGNED(next)) break;
148 frame = next;
149 }
150
151 return buffer + seprintf(buffer, last, "\n");
152 }
153
154 public:
159 CrashLogOSX(int signum) : signum(signum)
160 {
161 filename_log[0] = '\0';
162 filename_save[0] = '\0';
163 filename_screenshot[0] = '\0';
164 }
165
167 bool MakeCrashLog()
168 {
169 char buffer[65536];
170 bool ret = true;
171
172 printf("Crash encountered, generating crash log...\n");
173 this->FillCrashLog(buffer, lastof(buffer));
174 printf("%s\n", buffer);
175 printf("Crash log generated.\n\n");
176
177 printf("Writing crash log to disk...\n");
178 if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) {
179 filename_log[0] = '\0';
180 ret = false;
181 }
182
183 printf("Writing crash savegame...\n");
184 if (!this->WriteSavegame(filename_save, lastof(filename_save))) {
185 filename_save[0] = '\0';
186 ret = false;
187 }
188
189 printf("Writing crash screenshot...\n");
190 if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) {
191 filename_screenshot[0] = '\0';
192 ret = false;
193 }
194
195 return ret;
196 }
197
199 void DisplayCrashDialog() const
200 {
201 static const char crash_title[] =
202 "A serious fault condition occurred in the game. The game will shut down.";
203
204 char message[1024];
205 seprintf(message, lastof(message),
206 "Please send the generated crash information and the last (auto)save to the developers. "
207 "This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues.\n\n"
208 "Generated file(s):\n%s\n%s\n%s",
209 this->filename_log, this->filename_save, this->filename_screenshot);
210
211 ShowMacDialog(crash_title, message, "Quit");
212 }
213 };
214
216 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGSYS };
217
223 void CDECL HandleCrash(int signum)
224 {
225 /* Disable all handling of signals by us, so we don't go into infinite loops. */
226 for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
227 signal(*i, SIG_DFL);
228 }
229
230 if (GamelogTestEmergency()) {
231 ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
232 "As you loaded an emergency savegame no crash information will be generated.\n",
233 "Quit");
234 abort();
235 }
236
237 if (SaveloadCrashWithMissingNewGRFs()) {
238 ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
239 "As you loaded an savegame for which you do not have the required NewGRFs no crash information will be generated.\n",
240 "Quit");
241 abort();
242 }
243
244 CrashLogOSX log(signum);
245 log.MakeCrashLog();
246 if (VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) {
247 log.DisplayCrashDialog();
248 }
249
250 CrashLog::AfterCrashLogCleanup();
251 abort();
252 }
253
254 /* static */ void CrashLog::InitialiseCrashLog()
255 {
256 for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
257 signal(*i, HandleCrash);
258 }
259 }
260
261 /* static */ void CrashLog::InitThread()
262 {
263 }
CrashLogOSX::filename_save
char filename_save[MAX_PATH]
Path of crash.sav.
Definition: crashlog_osx.cpp:51
CrashLogOSX
OSX implementation for the crash logger.
Definition: crashlog_osx.cpp:46
CrashLogOSX::filename_log
char filename_log[MAX_PATH]
Path of crash.log.
Definition: crashlog_osx.cpp:50
CrashLogOSX::MakeCrashLog
bool MakeCrashLog()
Generate the crash log.
Definition: crashlog_osx.cpp:167
HandleCrash
void CDECL HandleCrash(int signum)
Entry point for the crash handler.
Definition: crashlog_osx.cpp:223
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:526
CrashLog::WriteScreenshot
bool WriteScreenshot(char *filename, const char *filename_last) const
Write the (crash) screenshot to a file.
Definition: crashlog.cpp:440
CrashLogOSX::LogStacktrace
char * LogStacktrace(char *buffer, const char *last) const override
Writes the stack trace to the buffer, if there is information about it available.
Definition: crashlog_osx.cpp:87
CrashLogOSX::filename_screenshot
char filename_screenshot[MAX_PATH]
Path of crash.(png|bmp|pcx)
Definition: crashlog_osx.cpp:52
_signals_to_handle
static const int _signals_to_handle[]
The signals we want our crash handler to handle.
Definition: crashlog_osx.cpp:216
CrashLog::InitThread
static void InitThread()
Prepare crash log handler for a newly started thread.
Definition: crashlog_osx.cpp:261
GamelogTestEmergency
bool GamelogTestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition: gamelog.cpp:420
CrashLogOSX::signum
int signum
Signal that has been thrown.
Definition: crashlog_osx.cpp:48
SaveloadCrashWithMissingNewGRFs
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition: afterload.cpp:365
CrashLog::message
static const char * message
Pointer to the error message.
Definition: crashlog.h:19
CrashLog
Helper class for creating crash logs.
Definition: crashlog.h:16
CrashLogOSX::LogOSVersion
char * LogOSVersion(char *buffer, const char *last) const override
Writes OS' version to the buffer.
Definition: crashlog_osx.cpp:54
ShowMacDialog
void ShowMacDialog(const char *title, const char *message, const char *button_label)
Helper function displaying a message the best possible way.
VideoDriver::GetInstance
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
Definition: video_driver.hpp:199
CrashLogOSX::LogError
char * LogError(char *buffer, const char *last, const char *message) const override
Writes actually encountered error to the buffer.
Definition: crashlog_osx.cpp:75
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:254
CrashLog::WriteSavegame
bool WriteSavegame(char *filename, const char *filename_last) const
Write the (crash) savegame to a file.
Definition: crashlog.cpp:414
endof
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:386
CrashLogOSX::CrashLogOSX
CrashLogOSX(int signum)
A crash log is always generated by signal.
Definition: crashlog_osx.cpp:159
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:535
CrashLogOSX::DisplayCrashDialog
void DisplayCrashDialog() const
Show a dialog with the crash information.
Definition: crashlog_osx.cpp:199
macos.h
CrashLog::FillCrashLog
char * FillCrashLog(char *buffer, const char *last) const
Fill the crash log buffer with all data of a crash log.
Definition: crashlog.cpp:352
CrashLog::WriteCrashLog
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
Write the crash log to a file.
Definition: crashlog.cpp:386
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:460
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:394