1 /* $Id: crashlog_unix.cpp 20266 2010-07-31 21:02:56Z alberth $ */
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/>.
10 /** @file crashlog_unix.cpp Unix crash log handler */
12 #include "../../stdafx.h"
13 #include "../../crashlog.h"
14 #include "../../string_func.h"
15 #include "../../gamelog.h"
16 #include "../../saveload/saveload.h"
20 #include <sys/utsname.h>
22 #if defined(__GLIBC__)
23 /* Execinfo (and thus making stacktraces) is a GNU extension */
24 # include <execinfo.h>
26 # include <ucontext.h>
30 #if defined(__NetBSD__)
34 #include "../../safeguards.h"
37 * Unix implementation for the crash logger.
39 class CrashLogUnix
: public CrashLog
{
40 /** Signal that has been thrown. */
43 /* virtual */ char *LogOSVersion(char *buffer
, const char *last
) const
46 if (uname(&name
) < 0) {
47 return buffer
+ seprintf(buffer
, last
, "Could not get OS version: %s\n", strerror(errno
));
50 return buffer
+ seprintf(buffer
, last
,
63 /* virtual */ char *LogError(char *buffer
, const char *last
, const char *message
) const
65 return buffer
+ seprintf(buffer
, last
,
69 strsignal(this->signum
),
71 message
== NULL
? "<none>" : message
76 /** Data needed while walking up the stack */
77 struct StackWalkerParams
{
78 char **bufptr
; ///< Buffer
79 const char *last
; ///< End of buffer
80 int counter
; ///< We are at counter-th stack level
84 * Callback used while walking up the stack.
85 * @param pc program counter
86 * @param sig 'active' signal (unused)
87 * @param params parameters
88 * @return always 0, continue walking up the stack
90 static int SunOSStackWalker(uintptr_t pc
, int sig
, void *params
)
92 StackWalkerParams
*wp
= (StackWalkerParams
*)params
;
94 /* Resolve program counter to file and nearest symbol (if possible) */
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
);
100 *wp
->bufptr
+= seprintf(*wp
->bufptr
, wp
->last
, " [%02i] [0x%x]\n", wp
->counter
, (uint
)pc
);
108 /* virtual */ char *LogStacktrace(char *buffer
, const char *last
) const
110 buffer
+= seprintf(buffer
, last
, "Stacktrace:\n");
111 #if defined(__GLIBC__)
113 int trace_size
= backtrace(trace
, lengthof(trace
));
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
]);
122 if (getcontext(&uc
) != 0) {
123 buffer
+= seprintf(buffer
, last
, " getcontext() failed\n\n");
127 StackWalkerParams wp
= { &buffer
, last
, 0 };
128 walkcontext(&uc
, &CrashLogUnix::SunOSStackWalker
, &wp
);
130 buffer
+= seprintf(buffer
, last
, " Not supported.\n");
132 return buffer
+ seprintf(buffer
, last
, "\n");
136 * A crash log is always generated by signal.
137 * @param signum the signal that was caused by the crash.
139 CrashLogUnix(int signum
) :
145 /** The signals we want our crash handler to handle. */
146 static const int _signals_to_handle
[] = { SIGSEGV
, SIGABRT
, SIGFPE
, SIGBUS
, SIGILL
};
149 * Entry point for the crash handler.
150 * @note Not static so it shows up in the backtrace.
151 * @param signum the signal that caused us to crash.
153 static void CDECL
HandleCrash(int signum
)
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
++) {
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");
166 if (SaveloadCrashWithMissingNewGRFs()) {
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");
173 CrashLogUnix
log(signum
);
176 CrashLog::AfterCrashLogCleanup();
180 /* static */ void CrashLog::InitialiseCrashLog()
182 for (const int *i
= _signals_to_handle
; i
!= endof(_signals_to_handle
); i
++) {
183 signal(*i
, HandleCrash
);