OpenTTD
crashlog_unix.cpp
Go to the documentation of this file.
1 /* $Id: crashlog_unix.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
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 
18 #include <errno.h>
19 #include <signal.h>
20 #include <sys/utsname.h>
21 
22 #if defined(__GLIBC__)
23 /* Execinfo (and thus making stacktraces) is a GNU extension */
24 # include <execinfo.h>
25 #elif defined(SUNOS)
26 # include <ucontext.h>
27 # include <dlfcn.h>
28 #endif
29 
30 #if defined(__NetBSD__)
31 #include <unistd.h>
32 #endif
33 
34 #include "../../safeguards.h"
35 
39 class CrashLogUnix : public CrashLog {
41  int signum;
42 
43  /* virtual */ char *LogOSVersion(char *buffer, const char *last) const
44  {
45  struct utsname name;
46  if (uname(&name) < 0) {
47  return buffer + seprintf(buffer, last, "Could not get OS version: %s\n", strerror(errno));
48  }
49 
50  return buffer + seprintf(buffer, last,
51  "Operating system:\n"
52  " Name: %s\n"
53  " Release: %s\n"
54  " Version: %s\n"
55  " Machine: %s\n",
56  name.sysname,
57  name.release,
58  name.version,
59  name.machine
60  );
61  }
62 
63  /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const
64  {
65  return buffer + seprintf(buffer, last,
66  "Crash reason:\n"
67  " Signal: %s (%d)\n"
68  " Message: %s\n\n",
69  strsignal(this->signum),
70  this->signum,
71  message == NULL ? "<none>" : message
72  );
73  }
74 
75 #if defined(SUNOS)
76 
77  struct StackWalkerParams {
78  char **bufptr;
79  const char *last;
80  int counter;
81  };
82 
90  static int SunOSStackWalker(uintptr_t pc, int sig, void *params)
91  {
92  StackWalkerParams *wp = (StackWalkerParams *)params;
93 
94  /* Resolve program counter to file and nearest symbol (if possible) */
95  Dl_info dli;
96  if (dladdr((void *)pc, &dli) != 0) {
97  *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] %s(%s+0x%x) [0x%x]\n",
98  wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc);
99  } else {
100  *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] [0x%x]\n", wp->counter, (uint)pc);
101  }
102  wp->counter++;
103 
104  return 0;
105  }
106 #endif
107 
108  /* virtual */ char *LogStacktrace(char *buffer, const char *last) const
109  {
110  buffer += seprintf(buffer, last, "Stacktrace:\n");
111 #if defined(__GLIBC__)
112  void *trace[64];
113  int trace_size = backtrace(trace, lengthof(trace));
114 
115  char **messages = backtrace_symbols(trace, trace_size);
116  for (int i = 0; i < trace_size; i++) {
117  buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]);
118  }
119  free(messages);
120 #elif defined(SUNOS)
121  ucontext_t uc;
122  if (getcontext(&uc) != 0) {
123  buffer += seprintf(buffer, last, " getcontext() failed\n\n");
124  return buffer;
125  }
126 
127  StackWalkerParams wp = { &buffer, last, 0 };
128  walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp);
129 #else
130  buffer += seprintf(buffer, last, " Not supported.\n");
131 #endif
132  return buffer + seprintf(buffer, last, "\n");
133  }
134 public:
140  signum(signum)
141  {
142  }
143 };
144 
146 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL };
147 
153 static void CDECL HandleCrash(int signum)
154 {
155  /* Disable all handling of signals by us, so we don't go into infinite loops. */
156  for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
157  signal(*i, SIG_DFL);
158  }
159 
160  if (GamelogTestEmergency()) {
161  printf("A serious fault condition occurred in the game. The game will shut down.\n");
162  printf("As you loaded an emergency savegame no crash information will be generated.\n");
163  abort();
164  }
165 
167  printf("A serious fault condition occurred in the game. The game will shut down.\n");
168  printf("As you loaded an savegame for which you do not have the required NewGRFs\n");
169  printf("no crash information will be generated.\n");
170  abort();
171  }
172 
173  CrashLogUnix log(signum);
174  log.MakeCrashLog();
175 
177  abort();
178 }
179 
180 /* static */ void CrashLog::InitialiseCrashLog()
181 {
182  for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
183  signal(*i, HandleCrash);
184  }
185 }