crashlog_osx.cpp

Go to the documentation of this file.
00001 /* $Id: crashlog_osx.cpp 20266 2010-07-31 21:02:56Z alberth $ */
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 #include "macos.h"
00018 
00019 #include <errno.h>
00020 #include <signal.h>
00021 #include <mach-o/arch.h>
00022 #include <dlfcn.h>
00023 #include <cxxabi.h>
00024 
00025 
00026 /* Macro testing a stack address for valid alignment. */
00027 #if defined(__i386__)
00028 #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 8)
00029 #else
00030 #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0)
00031 #endif
00032 
00033 /* printf format specification for 32/64-bit addresses. */
00034 #if __LP64__
00035 #define PRINTF_PTR "0x%016lx"
00036 #else
00037 #define PRINTF_PTR "0x%08lx"
00038 #endif
00039 
00040 #define MAX_STACK_FRAMES 64
00041 
00045 class CrashLogOSX : public CrashLog {
00047   int signum;
00048 
00049   char filename_log[MAX_PATH];        
00050   char filename_save[MAX_PATH];       
00051   char filename_screenshot[MAX_PATH]; 
00052 
00053   /* virtual */ char *LogOSVersion(char *buffer, const char *last) const
00054   {
00055     int ver_maj, ver_min, ver_bug;
00056     GetMacOSVersion(&ver_maj, &ver_min, &ver_bug);
00057 
00058     const NXArchInfo *arch = NXGetLocalArchInfo();
00059 
00060     return buffer + seprintf(buffer, last,
00061         "Operating system:\n"
00062         " Name:     Mac OS X\n"
00063         " Release:  %d.%d.%d\n"
00064         " Machine:  %s\n"
00065         " Min Ver:  %d\n",
00066         ver_maj, ver_min, ver_bug,
00067         arch != NULL ? arch->description : "unknown",
00068         MAC_OS_X_VERSION_MIN_REQUIRED
00069     );
00070   }
00071 
00072   /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const
00073   {
00074     return buffer + seprintf(buffer, last,
00075         "Crash reason:\n"
00076         " Signal:  %s (%d)\n"
00077         " Message: %s\n\n",
00078         strsignal(this->signum),
00079         this->signum,
00080         message == NULL ? "<none>" : message
00081     );
00082   }
00083 
00084   /* virtual */ char *LogStacktrace(char *buffer, const char *last) const
00085   {
00086     /* As backtrace() is only implemented in 10.5 or later,
00087      * we're rolling our own here. Mostly based on
00088      * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x
00089      * and some details looked up in the Darwin sources. */
00090     buffer += seprintf(buffer, last, "\nStacktrace:\n");
00091 
00092     void **frame;
00093 #if defined(__ppc__) || defined(__ppc64__)
00094     /* Apple says __builtin_frame_address can be broken on PPC. */
00095     __asm__ volatile("mr %0, r1" : "=r" (frame));
00096 #else
00097     frame = (void **)__builtin_frame_address(0);
00098 #endif
00099 
00100     for (int i = 0; frame != NULL && i < MAX_STACK_FRAMES; i++) {
00101       /* Get IP for current stack frame. */
00102 #if defined(__ppc__) || defined(__ppc64__)
00103       void *ip = frame[2];
00104 #else
00105       void *ip = frame[1];
00106 #endif
00107       if (ip == NULL) break;
00108 
00109       /* Print running index. */
00110       buffer += seprintf(buffer, last, " [%02d]", i);
00111 
00112       Dl_info dli;
00113       bool dl_valid = dladdr(ip, &dli) != 0;
00114 
00115       const char *fname = "???";
00116       if (dl_valid && dli.dli_fname) {
00117         /* Valid image name? Extract filename from the complete path. */
00118         const char *s = strrchr(dli.dli_fname, '/');
00119         if (s != NULL) {
00120           fname = s + 1;
00121         } else {
00122           fname = dli.dli_fname;
00123         }
00124       }
00125       /* Print image name and IP. */
00126       buffer += seprintf(buffer, last, " %-20s " PRINTF_PTR, fname, (uintptr_t)ip);
00127 
00128       /* Print function offset if information is available. */
00129       if (dl_valid && dli.dli_sname != NULL && dli.dli_saddr != NULL) {
00130         /* Try to demangle a possible C++ symbol. */
00131         int status = -1;
00132         char *func_name = abi::__cxa_demangle(dli.dli_sname, NULL, 0, &status);
00133 
00134         long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr;
00135         buffer += seprintf(buffer, last, " (%s + %ld)", func_name != NULL ? func_name : dli.dli_sname, offset);
00136 
00137         free(func_name);
00138       }
00139       buffer += seprintf(buffer, last, "\n");
00140 
00141       /* Get address of next stack frame. */
00142       void **next = (void **)frame[0];
00143       /* Frame address not increasing or not aligned? Broken stack, exit! */
00144       if (next <= frame || !IS_ALIGNED(next)) break;
00145       frame = next;
00146     }
00147 
00148     return buffer + seprintf(buffer, last, "\n");
00149   }
00150 
00151 public:
00156   CrashLogOSX(int signum) : signum(signum)
00157   {
00158     filename_log[0] = '\0';
00159     filename_save[0] = '\0';
00160     filename_screenshot[0] = '\0';
00161   }
00162 
00164   bool MakeCrashLog()
00165   {
00166     char buffer[65536];
00167     bool ret = true;
00168 
00169     printf("Crash encountered, generating crash log...\n");
00170     this->FillCrashLog(buffer, lastof(buffer));
00171     printf("%s\n", buffer);
00172     printf("Crash log generated.\n\n");
00173 
00174     printf("Writing crash log to disk...\n");
00175     if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) {
00176       filename_log[0] = '\0';
00177       ret = false;
00178     }
00179 
00180     printf("Writing crash savegame...\n");
00181     if (!this->WriteSavegame(filename_save, lastof(filename_save))) {
00182       filename_save[0] = '\0';
00183       ret = false;
00184     }
00185 
00186     printf("Writing crash savegame...\n");
00187     if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) {
00188       filename_screenshot[0] = '\0';
00189       ret = false;
00190     }
00191 
00192     return ret;
00193   }
00194 
00196   void DisplayCrashDialog() const
00197   {
00198     static const char crash_title[] =
00199       "A serious fault condition occurred in the game. The game will shut down.";
00200 
00201     char message[1024];
00202     seprintf(message, lastof(message),
00203          "Please send the generated crash information and the last (auto)save to the developers. "
00204          "This will greatly help debugging. The correct place to do this is http://bugs.openttd.org.\n\n"
00205          "Generated file(s):\n%s\n%s\n%s",
00206          this->filename_log, this->filename_save, this->filename_screenshot);
00207 
00208     ShowMacDialog(crash_title, message, "Quit");
00209   }
00210 };
00211 
00213 static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGSYS };
00214 
00220 void CDECL HandleCrash(int signum)
00221 {
00222   /* Disable all handling of signals by us, so we don't go into infinite loops. */
00223   for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
00224     signal(*i, SIG_DFL);
00225   }
00226 
00227   if (GamelogTestEmergency()) {
00228     ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
00229         "As you loaded an emergency savegame no crash information will be generated.\n",
00230         "Quit");
00231     abort();
00232   }
00233 
00234   if (SaveloadCrashWithMissingNewGRFs()) {
00235     ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
00236         "As you loaded an savegame for which you do not have the required NewGRFs no crash information will be generated.\n",
00237         "Quit");
00238     abort();
00239   }
00240 
00241   CrashLogOSX log(signum);
00242   log.MakeCrashLog();
00243   log.DisplayCrashDialog();
00244 
00245   CrashLog::AfterCrashLogCleanup();
00246   abort();
00247 }
00248 
00249 /* static */ void CrashLog::InitialiseCrashLog()
00250 {
00251   for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
00252     signal(*i, HandleCrash);
00253   }
00254 }

Generated on Thu Jan 20 22:57:38 2011 for OpenTTD by  doxygen 1.6.1