# crashlog_unix.cpp

Go to the documentation of this file.

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