OpenTTD Source  1.10.0-RC1
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  ver_maj, ver_min, ver_bug,
68  arch != nullptr ? arch->description : "unknown",
69  MAC_OS_X_VERSION_MIN_REQUIRED
70  );
71  }
72 
73  char *LogError(char *buffer, const char *last, const char *message) const override
74  {
75  return buffer + seprintf(buffer, last,
76  "Crash reason:\n"
77  " Signal: %s (%d)\n"
78  " Message: %s\n\n",
79  strsignal(this->signum),
80  this->signum,
81  message == nullptr ? "<none>" : message
82  );
83  }
84 
85  char *LogStacktrace(char *buffer, const char *last) const override
86  {
87  /* As backtrace() is only implemented in 10.5 or later,
88  * we're rolling our own here. Mostly based on
89  * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x
90  * and some details looked up in the Darwin sources. */
91  buffer += seprintf(buffer, last, "\nStacktrace:\n");
92 
93  void **frame;
94 #if defined(__ppc__) || defined(__ppc64__)
95  /* Apple says __builtin_frame_address can be broken on PPC. */
96  __asm__ volatile("mr %0, r1" : "=r" (frame));
97 #else
98  frame = (void **)__builtin_frame_address(0);
99 #endif
100 
101  for (int i = 0; frame != nullptr && i < MAX_STACK_FRAMES; i++) {
102  /* Get IP for current stack frame. */
103 #if defined(__ppc__) || defined(__ppc64__)
104  void *ip = frame[2];
105 #else
106  void *ip = frame[1];
107 #endif
108  if (ip == nullptr) break;
109 
110  /* Print running index. */
111  buffer += seprintf(buffer, last, " [%02d]", i);
112 
113  Dl_info dli;
114  bool dl_valid = dladdr(ip, &dli) != 0;
115 
116  const char *fname = "???";
117  if (dl_valid && dli.dli_fname) {
118  /* Valid image name? Extract filename from the complete path. */
119  const char *s = strrchr(dli.dli_fname, '/');
120  if (s != nullptr) {
121  fname = s + 1;
122  } else {
123  fname = dli.dli_fname;
124  }
125  }
126  /* Print image name and IP. */
127  buffer += seprintf(buffer, last, " %-20s " PRINTF_PTR, fname, (uintptr_t)ip);
128 
129  /* Print function offset if information is available. */
130  if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) {
131  /* Try to demangle a possible C++ symbol. */
132  int status = -1;
133  char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status);
134 
135  long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr;
136  buffer += seprintf(buffer, last, " (%s + %ld)", func_name != nullptr ? func_name : dli.dli_sname, offset);
137 
138  free(func_name);
139  }
140  buffer += seprintf(buffer, last, "\n");
141 
142  /* Get address of next stack frame. */
143  void **next = (void **)frame[0];
144  /* Frame address not increasing or not aligned? Broken stack, exit! */
145  if (next <= frame || !IS_ALIGNED(next)) break;
146  frame = next;
147  }
148 
149  return buffer + seprintf(buffer, last, "\n");
150  }
151 
152 public:
157  CrashLogOSX(int signum) : signum(signum)
158  {
159  filename_log[0] = '\0';
160  filename_save[0] = '\0';
161  filename_screenshot[0] = '\0';
162  }
163 
166  {
167  char buffer[65536];
168  bool ret = true;
169 
170  printf("Crash encountered, generating crash log...\n");
171  this->FillCrashLog(buffer, lastof(buffer));
172  printf("%s\n", buffer);
173  printf("Crash log generated.\n\n");
174 
175  printf("Writing crash log to disk...\n");
176  if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) {
177  filename_log[0] = '\0';
178  ret = false;
179  }
180 
181  printf("Writing crash savegame...\n");
182  if (!this->WriteSavegame(filename_save, lastof(filename_save))) {
183  filename_save[0] = '\0';
184  ret = false;
185  }
186 
187  printf("Writing crash savegame...\n");
188  if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) {
189  filename_screenshot[0] = '\0';
190  ret = false;
191  }
192 
193  return ret;
194  }
195 
197  void DisplayCrashDialog() const
198  {
199  static const char crash_title[] =
200  "A serious fault condition occurred in the game. The game will shut down.";
201 
202  char message[1024];
203  seprintf(message, lastof(message),
204  "Please send the generated crash information and the last (auto)save to the developers. "
205  "This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues.\n\n"
206  "Generated file(s):\n%s\n%s\n%s",
207  this->filename_log, this->filename_save, this->filename_screenshot);
208 
209  ShowMacDialog(crash_title, message, "Quit");
210  }
211 };
212 
214 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGSYS };
215 
221 void CDECL HandleCrash(int signum)
222 {
223  /* Disable all handling of signals by us, so we don't go into infinite loops. */
224  for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
225  signal(*i, SIG_DFL);
226  }
227 
228  if (GamelogTestEmergency()) {
229  ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
230  "As you loaded an emergency savegame no crash information will be generated.\n",
231  "Quit");
232  abort();
233  }
234 
236  ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
237  "As you loaded an savegame for which you do not have the required NewGRFs no crash information will be generated.\n",
238  "Quit");
239  abort();
240  }
241 
242  CrashLogOSX log(signum);
243  log.MakeCrashLog();
244  if (VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) {
245  log.DisplayCrashDialog();
246  }
247 
249  abort();
250 }
251 
252 /* static */ void CrashLog::InitialiseCrashLog()
253 {
254  for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
255  signal(*i, HandleCrash);
256  }
257 }
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.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
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:364
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.