# 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 }