# crashlog_unix.cpp

Go to the documentation of this file.

    00001 /* $Id: crashlog_unix.cpp 18350 2009-11-29 22:18:04Z smatz $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "../../stdafx.h"
    00013 #include "../../crashlog.h"
    00014 #include "../../string_func.h"
    00015 #include "../../gamelog.h"
    00016
    00017 #include <errno.h>
    00018 #include <signal.h>
    00019 #include <sys/utsname.h>
    00020
    00021 #if defined(__GLIBC__)
    00022 /* Execinfo (and thus making stacktraces) is a GNU extension */
    00023 # include <execinfo.h>
    00024 #elif defined(SUNOS)
    00025 # include <ucontext.h>
    00026 # include <dlfcn.h>
    00027 #endif
    00028
    00032 class CrashLogUnix : public CrashLog {
    00034   int signum;
    00035
    00036   /* virtual */ char *LogOSVersion(char *buffer, const char *last) const
    00037   {
    00038     struct utsname name;
    00039     if (uname(&name) < 0) {
    00040       return buffer + seprintf(buffer, last, "Could not get OS version: %s\n", strerror(errno));
    00041     }
    00042
    00043     return buffer + seprintf(buffer, last,
    00044         "Operating system:\n"
    00045         " Name:    %s\n"
    00046         " Release: %s\n"
    00047         " Version: %s\n"
    00048         " Machine: %s\n\n",
    00049         name.sysname,
    00050         name.release,
    00051         name.version,
    00052         name.machine
    00053     );
    00054   }
    00055
    00056   /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const
    00057   {
    00058     return buffer + seprintf(buffer, last,
    00059         "Crash reason:\n"
    00060         " Signal:  %s (%d)\n"
    00061         " Message: %s\n\n",
    00062         strsignal(this->signum),
    00063         this->signum,
    00064         message == NULL ? "<none>" : message
    00065     );
    00066   }
    00067
    00068 #if defined(SUNOS)
    00069
    00070   struct StackWalkerParams {
    00071     char **bufptr;
    00072     const char *last;
    00073     int counter;
    00074   };
    00075
    00083   static int SunOSStackWalker(uintptr_t pc, int sig, void *params)
    00084   {
    00085     StackWalkerParams *wp = (StackWalkerParams *)params;
    00086
    00087     /* Resolve program counter to file and nearest symbol (if possible) */
    00088     Dl_info dli;
    00089     if (dladdr((void *)pc, &dli) != 0) {
    00090       *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] %s(%s+0x%x) [0x%x]\n",
    00091           wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc);
    00092     } else {
    00093       *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] [0x%x]\n", wp->counter, (uint)pc);
    00094     }
    00095     wp->counter++;
    00096
    00097     return 0;
    00098   }
    00099 #endif
    00100
    00101   /* virtual */ char *LogStacktrace(char *buffer, const char *last) const
    00102   {
    00103     buffer += seprintf(buffer, last, "Stacktrace:\n");
    00104 #if defined(__GLIBC__)
    00105     void *trace[64];
    00106     int trace_size = backtrace(trace, lengthof(trace));
    00107
    00108     char **messages = backtrace_symbols(trace, trace_size);
    00109     for (int i = 0; i < trace_size; i++) {
    00110       buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]);
    00111     }
    00112     free(messages);
    00113 #elif defined(SUNOS)
    00114     ucontext_t uc;
    00115     if (getcontext(&uc) != 0) {
    00116       buffer += seprintf(buffer, last, " getcontext() failed\n\n");
    00117       return buffer;
    00118     }
    00119
    00120     StackWalkerParams wp = { &buffer, last, 0 };
    00121     walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp);
    00122 #else
    00123     buffer += seprintf(buffer, last, " Not supported.\n");
    00124 #endif
    00125     return buffer + seprintf(buffer, last, "\n");
    00126   }
    00127 public:
    00132   CrashLogUnix(int signum) :
    00133     signum(signum)
    00134   {
    00135   }
    00136 };
    00137
    00139 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL };
    00140
    00146 static void CDECL HandleCrash(int signum)
    00147 {
    00148   /* Disable all handling of signals by us, so we don't go into infinite loops. */
    00149   for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
    00150     signal(*i, SIG_DFL);
    00151   }
    00152
    00153   if (GamelogTestEmergency()) {
    00154     printf("A serious fault condition occured in the game. The game will shut down.\n");
    00155     printf("As you loaded an emergency savegame no crash information will be generated.\n");
    00156     abort();
    00157   }
    00158
    00159   CrashLogUnix log(signum);
    00160   log.MakeCrashLog();
    00161
    00162   CrashLog::AfterCrashLogCleanup();
    00163   abort();
    00164 }
    00165
    00166 /* static */ void CrashLog::InitialiseCrashLog()
    00167 {
    00168   for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
    00169     signal(*i, HandleCrash);
    00170   }
    00171 }