crashlog_unix.cpp

Go to the documentation of this file.
00001 /* $Id$ */
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 occurred 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 occurred 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 }

Generated on Sun May 8 07:30:16 2011 for OpenTTD by  doxygen 1.6.1