# crashlog_unix.cpp

Go to the documentation of this file.

    00001 /* $Id: crashlog_unix.cpp 19858 2010-05-18 21:49:59Z rubidium $ */
    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 #include "../../saveload/saveload.h"
    00017
    00018 #include <errno.h>
    00019 #include <signal.h>
    00020 #include <sys/utsname.h>
    00021
    00022 #if defined(__GLIBC__)
    00023 /* Execinfo (and thus making stacktraces) is a GNU extension */
    00024 # include <execinfo.h>
    00025 #elif defined(SUNOS)
    00026 # include <ucontext.h>
    00027 # include <dlfcn.h>
    00028 #endif
    00029
    00030 #if defined(__NetBSD__)
    00031 #include <unistd.h>
    00032 #endif
    00033
    00037 class CrashLogUnix : public CrashLog {
    00039   int signum;
    00040
    00041   /* virtual */ char *LogOSVersion(char *buffer, const char *last) const
    00042   {
    00043     struct utsname name;
    00044     if (uname(&name) < 0) {
    00045       return buffer + seprintf(buffer, last, "Could not get OS version: %s\n", strerror(errno));
    00046     }
    00047
    00048     return buffer + seprintf(buffer, last,
    00049         "Operating system:\n"
    00050         " Name:     %s\n"
    00051         " Release:  %s\n"
    00052         " Version:  %s\n"
    00053         " Machine:  %s\n",
    00054         name.sysname,
    00055         name.release,
    00056         name.version,
    00057         name.machine
    00058     );
    00059   }
    00060
    00061   /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const
    00062   {
    00063     return buffer + seprintf(buffer, last,
    00064         "Crash reason:\n"
    00065         " Signal:  %s (%d)\n"
    00066         " Message: %s\n\n",
    00067         strsignal(this->signum),
    00068         this->signum,
    00069         message == NULL ? "<none>" : message
    00070     );
    00071   }
    00072
    00073 #if defined(SUNOS)
    00074
    00075   struct StackWalkerParams {
    00076     char **bufptr;
    00077     const char *last;
    00078     int counter;
    00079   };
    00080
    00088   static int SunOSStackWalker(uintptr_t pc, int sig, void *params)
    00089   {
    00090     StackWalkerParams *wp = (StackWalkerParams *)params;
    00091
    00092     /* Resolve program counter to file and nearest symbol (if possible) */
    00093     Dl_info dli;
    00094     if (dladdr((void *)pc, &dli) != 0) {
    00095       *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] %s(%s+0x%x) [0x%x]\n",
    00096           wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc);
    00097     } else {
    00098       *wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] [0x%x]\n", wp->counter, (uint)pc);
    00099     }
    00100     wp->counter++;
    00101
    00102     return 0;
    00103   }
    00104 #endif
    00105
    00106   /* virtual */ char *LogStacktrace(char *buffer, const char *last) const
    00107   {
    00108     buffer += seprintf(buffer, last, "Stacktrace:\n");
    00109 #if defined(__GLIBC__)
    00110     void *trace[64];
    00111     int trace_size = backtrace(trace, lengthof(trace));
    00112
    00113     char **messages = backtrace_symbols(trace, trace_size);
    00114     for (int i = 0; i < trace_size; i++) {
    00115       buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]);
    00116     }
    00117     free(messages);
    00118 #elif defined(SUNOS)
    00119     ucontext_t uc;
    00120     if (getcontext(&uc) != 0) {
    00121       buffer += seprintf(buffer, last, " getcontext() failed\n\n");
    00122       return buffer;
    00123     }
    00124
    00125     StackWalkerParams wp = { &buffer, last, 0 };
    00126     walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp);
    00127 #else
    00128     buffer += seprintf(buffer, last, " Not supported.\n");
    00129 #endif
    00130     return buffer + seprintf(buffer, last, "\n");
    00131   }
    00132 public:
    00137   CrashLogUnix(int signum) :
    00138     signum(signum)
    00139   {
    00140   }
    00141 };
    00142
    00144 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL };
    00145
    00151 static void CDECL HandleCrash(int signum)
    00152 {
    00153   /* Disable all handling of signals by us, so we don't go into infinite loops. */
    00154   for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
    00155     signal(*i, SIG_DFL);
    00156   }
    00157
    00158   if (GamelogTestEmergency()) {
    00159     printf("A serious fault condition occured in the game. The game will shut down.\n");
    00160     printf("As you loaded an emergency savegame no crash information will be generated.\n");
    00161     abort();
    00162   }
    00163
    00164   if (SaveloadCrashWithMissingNewGRFs()) {
    00165     printf("A serious fault condition occured in the game. The game will shut down.\n");
    00166     printf("As you loaded an savegame for which you do not have the required NewGRFs\n");
    00167     printf("no crash information will be generated.\n");
    00168     abort();
    00169   }
    00170
    00171   CrashLogUnix log(signum);
    00172   log.MakeCrashLog();
    00173
    00174   CrashLog::AfterCrashLogCleanup();
    00175   abort();
    00176 }
    00177
    00178 /* static */ void CrashLog::InitialiseCrashLog()
    00179 {
    00180   for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
    00181     signal(*i, HandleCrash);
    00182   }
    00183 }