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

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

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 }