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