OpenTTD Source  1.11.0-beta2
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  " Max Ver: %d\n",
68  ver_maj, ver_min, ver_bug,
69  arch != nullptr ? arch->description : "unknown",
70  MAC_OS_X_VERSION_MIN_REQUIRED,
71  MAC_OS_X_VERSION_MAX_ALLOWED
72  );
73  }
74 
75  char *LogError(char *buffer, const char *last, const char *message) const override
76  {
77  return buffer + seprintf(buffer, last,
78  "Crash reason:\n"
79  " Signal: %s (%d)\n"
80  " Message: %s\n\n",
81  strsignal(this->signum),
82  this->signum,
83  message == nullptr ? "<none>" : message
84  );
85  }
86 
87  char *LogStacktrace(char *buffer, const char *last) const override
88  {
89  /* As backtrace() is only implemented in 10.5 or later,
90  * we're rolling our own here. Mostly based on
91  * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x
92  * and some details looked up in the Darwin sources. */
93  buffer += seprintf(buffer, last, "\nStacktrace:\n");
94 
95  void **frame;
96 #if defined(__ppc__) || defined(__ppc64__)
97  /* Apple says __builtin_frame_address can be broken on PPC. */
98  __asm__ volatile("mr %0, r1" : "=r" (frame));
99 #else
100  frame = (void **)__builtin_frame_address(0);
101 #endif
102 
103  for (int i = 0; frame != nullptr && i < MAX_STACK_FRAMES; i++) {
104  /* Get IP for current stack frame. */
105 #if defined(__ppc__) || defined(__ppc64__)
106  void *ip = frame[2];
107 #else
108  void *ip = frame[1];
109 #endif
110  if (ip == nullptr) break;
111 
112  /* Print running index. */
113  buffer += seprintf(buffer, last, " [%02d]", i);
114 
115  Dl_info dli;
116  bool dl_valid = dladdr(ip, &dli) != 0;
117 
118  const char *fname = "???";
119  if (dl_valid && dli.dli_fname) {
120  /* Valid image name? Extract filename from the complete path. */
121  const char *s = strrchr(dli.dli_fname, '/');
122  if (s != nullptr) {
123  fname = s + 1;
124  } else {
125  fname = dli.dli_fname;
126  }
127  }
128  /* Print image name and IP. */
129  buffer += seprintf(buffer, last, " %-20s " PRINTF_PTR, fname, (uintptr_t)ip);
130 
131  /* Print function offset if information is available. */
132  if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) {
133  /* Try to demangle a possible C++ symbol. */
134  int status = -1;
135  char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status);
136 
137  long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr;
138  buffer += seprintf(buffer, last, " (%s + %ld)", func_name != nullptr ? func_name : dli.dli_sname, offset);
139 
140  free(func_name);
141  }
142  buffer += seprintf(buffer, last, "\n");
143 
144  /* Get address of next stack frame. */
145  void **next = (void **)frame[0];
146  /* Frame address not increasing or not aligned? Broken stack, exit! */
147  if (next <= frame || !IS_ALIGNED(next)) break;
148  frame = next;
149  }
150 
151  return buffer + seprintf(buffer, last, "\n");
152  }
153 
154 public:
160  {
161  filename_log[0] = '\0';
162  filename_save[0] = '\0';
163  filename_screenshot[0] = '\0';
164  }
165 
168  {
169  char buffer[65536];
170  bool ret = true;
171 
172  printf("Crash encountered, generating crash log...\n");
173  this->FillCrashLog(buffer, lastof(buffer));
174  printf("%s\n", buffer);
175  printf("Crash log generated.\n\n");
176 
177  printf("Writing crash log to disk...\n");
178  if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) {
179  filename_log[0] = '\0';
180  ret = false;
181  }
182 
183  printf("Writing crash savegame...\n");
184  if (!this->WriteSavegame(filename_save, lastof(filename_save))) {
185  filename_save[0] = '\0';
186  ret = false;
187  }
188 
189  printf("Writing crash savegame...\n");
190  if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) {
191  filename_screenshot[0] = '\0';
192  ret = false;
193  }
194 
195  return ret;
196  }
197 
199  void DisplayCrashDialog() const
200  {
201  static const char crash_title[] =
202  "A serious fault condition occurred in the game. The game will shut down.";
203 
204  char message[1024];
206  "Please send the generated crash information and the last (auto)save to the developers. "
207  "This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues.\n\n"
208  "Generated file(s):\n%s\n%s\n%s",
209  this->filename_log, this->filename_save, this->filename_screenshot);
210 
211  ShowMacDialog(crash_title, message, "Quit");
212  }
213 };
214 
216 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGSYS };
217 
223 void CDECL HandleCrash(int signum)
224 {
225  /* Disable all handling of signals by us, so we don't go into infinite loops. */
226  for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
227  signal(*i, SIG_DFL);
228  }
229 
230  if (GamelogTestEmergency()) {
231  ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
232  "As you loaded an emergency savegame no crash information will be generated.\n",
233  "Quit");
234  abort();
235  }
236 
238  ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
239  "As you loaded an savegame for which you do not have the required NewGRFs no crash information will be generated.\n",
240  "Quit");
241  abort();
242  }
243 
244  CrashLogOSX log(signum);
245  log.MakeCrashLog();
246  if (VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) {
247  log.DisplayCrashDialog();
248  }
249 
251  abort();
252 }
253 
254 /* static */ void CrashLog::InitialiseCrashLog()
255 {
256  for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
257  signal(*i, HandleCrash);
258  }
259 }
CrashLogOSX::filename_save
char filename_save[MAX_PATH]
Path of crash.sav.
Definition: crashlog_osx.cpp:51
CrashLogOSX
OSX implementation for the crash logger.
Definition: crashlog_osx.cpp:46
CrashLogOSX::filename_log
char filename_log[MAX_PATH]
Path of crash.log.
Definition: crashlog_osx.cpp:50
CrashLogOSX::MakeCrashLog
bool MakeCrashLog()
Generate the crash log.
Definition: crashlog_osx.cpp:167
HandleCrash
void CDECL HandleCrash(int signum)
Entry point for the crash handler.
Definition: crashlog_osx.cpp:223
CrashLog::AfterCrashLogCleanup
static void AfterCrashLogCleanup()
Try to close the sound/video stuff so it doesn't keep lingering around incorrect video states or so,...
Definition: crashlog.cpp:507
CrashLog::WriteScreenshot
bool WriteScreenshot(char *filename, const char *filename_last) const
Write the (crash) screenshot to a file.
Definition: crashlog.cpp:423
CrashLogOSX::LogStacktrace
char * LogStacktrace(char *buffer, const char *last) const override
Writes the stack trace to the buffer, if there is information about it available.
Definition: crashlog_osx.cpp:87
CrashLogOSX::filename_screenshot
char filename_screenshot[MAX_PATH]
Path of crash.(png|bmp|pcx)
Definition: crashlog_osx.cpp:52
_signals_to_handle
static const int _signals_to_handle[]
The signals we want our crash handler to handle.
Definition: crashlog_osx.cpp:216
GamelogTestEmergency
bool GamelogTestEmergency()
Finds out if current game is a loaded emergency savegame.
Definition: gamelog.cpp:420
CrashLogOSX::signum
int signum
Signal that has been thrown.
Definition: crashlog_osx.cpp:48
SaveloadCrashWithMissingNewGRFs
bool SaveloadCrashWithMissingNewGRFs()
Did loading the savegame cause a crash? If so, were NewGRFs missing?
Definition: afterload.cpp:364
CrashLog::message
static const char * message
Pointer to the error message.
Definition: crashlog.h:19
CrashLog
Helper class for creating crash logs.
Definition: crashlog.h:16
CrashLogOSX::LogOSVersion
char * LogOSVersion(char *buffer, const char *last) const override
Writes OS' version to the buffer.
Definition: crashlog_osx.cpp:54
ShowMacDialog
void ShowMacDialog(const char *title, const char *message, const char *button_label)
Helper function displaying a message the best possible way.
VideoDriver::GetInstance
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
Definition: video_driver.hpp:168
CrashLogOSX::LogError
char * LogError(char *buffer, const char *last, const char *message) const override
Writes actually encountered error to the buffer.
Definition: crashlog_osx.cpp:75
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:254
CrashLog::WriteSavegame
bool WriteSavegame(char *filename, const char *filename_last) const
Write the (crash) savegame to a file.
Definition: crashlog.cpp:397
endof
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:375
CrashLogOSX::CrashLogOSX
CrashLogOSX(int signum)
A crash log is always generated by signal.
Definition: crashlog_osx.cpp:159
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:442
CrashLogOSX::DisplayCrashDialog
void DisplayCrashDialog() const
Show a dialog with the crash information.
Definition: crashlog_osx.cpp:199
macos.h
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:334
CrashLog::WriteCrashLog
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
Write the crash log to a file.
Definition: crashlog.cpp:369
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:454
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:383