summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.lib17
-rw-r--r--source.list1
-rw-r--r--src/os/unix/crashlog_unix.cpp171
-rw-r--r--src/os/unix/unix.cpp2
-rw-r--r--src/sdl.cpp43
-rw-r--r--src/stdafx.h15
6 files changed, 199 insertions, 50 deletions
diff --git a/config.lib b/config.lib
index 3ae524dfb..bada28c73 100644
--- a/config.lib
+++ b/config.lib
@@ -1038,13 +1038,18 @@ make_compiler_cflags() {
# $1 - compiler
# $2 - the current cflags
# $3 - variable to finally write to
+ # $4 - the current ldflags
+ # $5 - variable to finally write to
flags="$2"
+ ldflags="$4"
if [ `echo $1 | cut -c 1-3` = "icc" ]; then
# Enable some things only for certain ICC versions
cc_version=`$1 -dumpversion | cut -c 1-4`
+ flags="$flags -rdynamic"
+ ldflags="$ldflags -rdynamic"
if [ "$cc_version" = "10.1" ]; then
flags="$flags -Wno-multichar"
@@ -1094,9 +1099,17 @@ make_compiler_cflags() {
# about it's own optimized code in some places.
flags="$flags -fno-strict-overflow"
fi
+
+ has_rdynamic=`$1 -dumpspecs | grep rdynamic`
+ if [ -n "$has_rdynamic" ]; then
+ # rdynamic is used to get useful stack traces from crash reports.
+ flags="$flags -rdynamic"
+ ldflags="$ldflags -rdynamic"
+ fi
fi
eval "$3=\"$flags\""
+ eval "$5=\"$ldflags\""
}
make_cflags_and_ldflags() {
@@ -1154,8 +1167,8 @@ make_cflags_and_ldflags() {
CFLAGS="$CFLAGS -DNO_THREADS"
fi
- make_compiler_cflags "$cc_build" "$CFLAGS_BUILD" "CFLAGS_BUILD"
- make_compiler_cflags "$cc_host" "$CFLAGS" "CFLAGS"
+ make_compiler_cflags "$cc_build" "$CFLAGS_BUILD" "CFLAGS_BUILD" "$LDFLAGS_BUILD" "LDFLAGS_BUILD"
+ make_compiler_cflags "$cc_host" "$CFLAGS" "CFLAGS" "$LDFLAGS" "LDFLAGS"
if [ "`echo $1 | cut -c 1-3`" != "icc" ]; then
if [ "$os" = "CYGWIN" ]; then
diff --git a/source.list b/source.list
index c782ccfcf..c1bdb6a30 100644
--- a/source.list
+++ b/source.list
@@ -79,6 +79,7 @@ tile_map.cpp
#if OS2
os/os2/os2.cpp
#else
+ os/unix/crashlog_unix.cpp
os/unix/unix.cpp
#end
#end
diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp
new file mode 100644
index 000000000..73644ba8d
--- /dev/null
+++ b/src/os/unix/crashlog_unix.cpp
@@ -0,0 +1,171 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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/>.
+ */
+
+/** @file crashlog_unix.cpp Unix crash log handler */
+
+#include "../../stdafx.h"
+#include "../../crashlog.h"
+#include "../../string_func.h"
+#include "../../gamelog.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/utsname.h>
+
+#if defined(__GLIBC__)
+/* Execinfo (and thus making stacktraces) is a GNU extension */
+# include <execinfo.h>
+#elif defined(SUNOS)
+# include <ucontext.h>
+# include <dlfcn.h>
+#endif
+
+/**
+ * Unix implementation for the crash logger.
+ */
+class CrashLogUnix : public CrashLog {
+ /** Signal that has been thrown. */
+ int signum;
+
+ /* virtual */ char *LogOSVersion(char *buffer, const char *last) const
+ {
+ struct utsname name;
+ if (uname(&name) < 0) {
+ return buffer + seprintf(buffer, last, "Could not get OS version: %s\n", strerror(errno));
+ }
+
+ return buffer + seprintf(buffer, last,
+ "Operating system:\n"
+ " Name: %s\n"
+ " Release: %s\n"
+ " Version: %s\n"
+ " Machine: %s\n\n",
+ name.sysname,
+ name.release,
+ name.version,
+ name.machine
+ );
+ }
+
+ /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const
+ {
+ return buffer + seprintf(buffer, last,
+ "Crash reason:\n"
+ " Signal: %s (%d)\n"
+ " Message: %s\n\n",
+ strsignal(this->signum),
+ this->signum,
+ message == NULL ? "<none>" : message
+ );
+ }
+
+#if defined(SUNOS)
+ /** Data needed while walking up the stack */
+ struct StackWalkerParams {
+ char **bufptr; ///< Buffer
+ const char *last; ///< End of buffer
+ int counter; ///< We are at counter-th stack level
+ };
+
+ /**
+ * Callback used while walking up the stack.
+ * @param pc program counter
+ * @param sig 'active' signal (unused)
+ * @param params parameters
+ * @return always 0, continue walking up the stack
+ */
+ static int SunOSStackWalker(uintptr_t pc, int sig, void *params)
+ {
+ StackWalkerParams *wp = (StackWalkerParams *)params;
+
+ /* Resolve program counter to file and nearest symbol (if possible) */
+ Dl_info dli;
+ if (dladdr((void *)pc, &dli) != 0) {
+ *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] %s(%s+0x%x) [0x%x]\n",
+ wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc);
+ } else {
+ *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] [0x%x]\n", wp->counter, (uint)pc);
+ }
+ wp->counter++;
+
+ return 0;
+ }
+#endif
+
+ /* virtual */ char *LogStacktrace(char *buffer, const char *last) const
+ {
+ buffer += seprintf(buffer, last, "Stacktrace:\n");
+#if defined(__GLIBC__)
+ void *trace[64];
+ int trace_size = backtrace(trace, lengthof(trace));
+
+ char **messages = backtrace_symbols(trace, trace_size);
+ for (int i = 0; i < trace_size; i++) {
+ buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]);
+ }
+ free(messages);
+#elif defined(SUNOS)
+ ucontext_t uc;
+ if (getcontext(&uc) != 0) {
+ buffer += seprintf(buffer, last, " getcontext() failed\n\n");
+ return buffer;
+ }
+
+ StackWalkerParams wp = { &buffer, last, 0 };
+ walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp);
+#else
+ buffer += seprintf(buffer, last, " Not supported.\n");
+#endif
+ return buffer + seprintf(buffer, last, "\n");
+ }
+public:
+ /**
+ * A crash log is always generated by signal.
+ * @param signum the signal that was caused by the crash.
+ */
+ CrashLogUnix(int signum) :
+ signum(signum)
+ {
+ }
+};
+
+/** The signals we want our crash handler to handle. */
+static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS };
+
+/**
+ * Entry point for the crash handler.
+ * @note Not static so it shows up in the backtrace.
+ * @param signum the signal that caused us to crash.
+ */
+void CDECL HandleCrash(int signum)
+{
+ /* Disable all handling of signals by us, so we don't go into infinite loops. */
+ for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
+ signal(*i, SIG_DFL);
+ }
+
+ if (GamelogTestEmergency()) {
+ printf("A serious fault condition occured in the game. The game will shut down.\n");
+ printf("As you loaded an emergency savegame no crash information will be generated.\n");
+ exit(3);
+ }
+
+ CrashLogUnix log(signum);
+ log.MakeCrashLog();
+
+ CrashLog::AfterCrashLogCleanup();
+ exit(2);
+}
+
+/* static */ void CrashLog::InitialiseCrashLog()
+{
+ for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
+ signal(*i, HandleCrash);
+ }
+}
diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp
index bbe916c57..ee4388aa5 100644
--- a/src/os/unix/unix.cpp
+++ b/src/os/unix/unix.cpp
@@ -15,6 +15,7 @@
#include "../../textbuf_gui.h"
#include "../../functions.h"
#include "../../core/random_func.hpp"
+#include "../../crashlog.h"
#include "table/strings.h"
@@ -254,6 +255,7 @@ int CDECL main(int argc, char *argv[])
argc = 1;
}
#endif
+ CrashLog::InitialiseCrashLog();
SetRandomSeed(time(NULL));
diff --git a/src/sdl.cpp b/src/sdl.cpp
index 0650f8e29..389659e56 100644
--- a/src/sdl.cpp
+++ b/src/sdl.cpp
@@ -17,16 +17,6 @@
#include "sdl.h"
#include <SDL.h>
-#ifdef UNIX
-#include <signal.h>
-
-#ifdef __MORPHOS__
- /* The system supplied definition of SIG_DFL is wrong on MorphOS */
- #undef SIG_DFL
- #define SIG_DFL (void (*)(int))0
-#endif
-#endif
-
static int _sdl_usage;
#ifdef DYNAMICALLY_LOADED_SDL
@@ -86,26 +76,6 @@ static const char *LoadSdlDLL()
#endif /* DYNAMICALLY_LOADED_SDL */
-#ifdef UNIX
-static void SdlAbort(int sig)
-{
- /* Own hand-made parachute for the cases of failed assertions. */
- SDL_CALL SDL_Quit();
-
- switch (sig) {
- case SIGSEGV:
- case SIGFPE:
- signal(sig, SIG_DFL);
- raise(sig);
- break;
-
- default:
- break;
- }
-}
-#endif
-
-
const char *SdlOpen(uint32 x)
{
#ifdef DYNAMICALLY_LOADED_SDL
@@ -115,19 +85,13 @@ const char *SdlOpen(uint32 x)
}
#endif
if (_sdl_usage++ == 0) {
- if (SDL_CALL SDL_Init(x) == -1)
+ if (SDL_CALL SDL_Init(x | SDL_INIT_NOPARACHUTE) == -1)
return SDL_CALL SDL_GetError();
} else if (x != 0) {
if (SDL_CALL SDL_InitSubSystem(x) == -1)
return SDL_CALL SDL_GetError();
}
-#ifdef UNIX
- signal(SIGABRT, SdlAbort);
- signal(SIGSEGV, SdlAbort);
- signal(SIGFPE, SdlAbort);
-#endif
-
return NULL;
}
@@ -137,11 +101,6 @@ void SdlClose(uint32 x)
SDL_CALL SDL_QuitSubSystem(x);
if (--_sdl_usage == 0) {
SDL_CALL SDL_Quit();
- #ifdef UNIX
- signal(SIGABRT, SIG_DFL);
- signal(SIGSEGV, SIG_DFL);
- signal(SIGFPE, SIG_DFL);
- #endif
}
}
diff --git a/src/stdafx.h b/src/stdafx.h
index 35cbc8b1b..f21b28ee9 100644
--- a/src/stdafx.h
+++ b/src/stdafx.h
@@ -229,12 +229,6 @@
#define strncasecmp strnicmp
#endif
-
- #if defined(NDEBUG) && defined(WITH_ASSERT)
- #undef assert
- #define assert(expression) if (!(expression)) error("Assertion failed at line %i of %s: %s", __LINE__, __FILE__, #expression);
- #endif
-
/* MSVC doesn't have these :( */
#define S_ISDIR(mode) (mode & S_IFDIR)
#define S_ISREG(mode) (mode & S_IFREG)
@@ -363,6 +357,15 @@ void NORETURN CDECL usererror(const char *str, ...) WARN_FORMAT(1, 2);
void NORETURN CDECL error(const char *str, ...) WARN_FORMAT(1, 2);
#define NOT_REACHED() error("NOT_REACHED triggered at line %i of %s", __LINE__, __FILE__)
+/* For non-debug builds with assertions enabled use the special assertion handler:
+ * - For MSVC: NDEBUG is set for all release builds and WITH_ASSERT overrides the disabling of asserts.
+ * - For non MSVC: NDEBUG is set when assertions are disables, _DEBUG is set for non-release builds.
+ */
+#if (defined(_MSC_VER) && defined(NDEBUG) && defined(WITH_ASSERT)) || (!defined(_MSC_VER) && !defined(NDEBUG) && !defined(_DEBUG))
+ #undef assert
+ #define assert(expression) if (!(expression)) error("Assertion failed at line %i of %s: %s", __LINE__, __FILE__, #expression);
+#endif
+
#if defined(MORPHOS) || defined(__NDS__) || defined(__DJGPP__)
/* MorphOS and NDS don't have C++ conformant _stricmp... */
#define _stricmp stricmp