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 
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 
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 
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 }
bool GamelogTestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition: gamelog.cpp:392
Helper class for creating crash logs.
Definition: crashlog.h:18
char filename_save[MAX_PATH]
Path of crash.sav.
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
Write the crash log to a file.
Definition: crashlog.cpp:351
bool MakeCrashLog()
Generate the crash log.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:398
char * LogStacktrace(char *buffer, const char *last) const
Writes the stack trace to the buffer, if there is information about it available. ...
void CDECL HandleCrash(int signum)
Entry point for the crash handler.
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
static void InitialiseCrashLog()
Initialiser for crash logs; do the appropriate things so crashes are handled by our crash handler ins...
char filename_screenshot[MAX_PATH]
Path of crash.(png|bmp|pcx)
int signum
Signal that has been thrown.
static const char * message
Pointer to the error message.
Definition: crashlog.h:21
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
bool WriteScreenshot(char *filename, const char *filename_last) const
Write the (crash) screenshot to a file.
Definition: crashlog.cpp:405
static const int _signals_to_handle[]
The signals we want our crash handler to handle.
bool WriteSavegame(char *filename, const char *filename_last) const
Write the (crash) savegame to a file.
Definition: crashlog.cpp:379
OSX implementation for the crash logger.
void DisplayCrashDialog() const
Show a dialog with the crash information.
CrashLogOSX(int signum)
A crash log is always generated by signal.
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:427
void ShowMacDialog(const char *title, const char *message, const char *button_label)
Helper function displaying a message the best possible way.
Functions related to MacOS support.
char * LogOSVersion(char *buffer, const char *last) const
Writes OS&#39; version to the buffer.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:114
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition: afterload.cpp:359
char * FillCrashLog(char *buffer, const char *last) const
Fill the crash log buffer with all data of a crash log.
Definition: crashlog.cpp:317
char * LogError(char *buffer, const char *last, const char *message) const
Writes actually encountered error to the buffer.
char filename_log[MAX_PATH]
Path of crash.log.