OpenTTD

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