// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2008 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC 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. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . /* * mac_backtrace.C * */ /* This is part of a backtrace generator for boinc project applications. * * Adapted from Apple Developer Technical Support Sample Code QCrashReport * * This code handles Mac OS X 10.3.x through 10.4.9. It may require some * adjustment for future OS versions; see the discussion of _sigtramp and * PowerPC Signal Stack Frames below. * * For useful tips on using backtrace information, see Apple Tech Note 2123: * http://developer.apple.com/technotes/tn2004/tn2123.html#SECNOSYMBOLS * * To convert addresses to correct symbols, use the atos command-line tool: * atos -o path/to/executable/with/symbols address * Note: if address 1a23 is hex, use 0x1a23. * * To demangle mangled C++ symbols, use the c++filt command-line tool. * You may need to prefix C++ symbols with an additonal underscore before * passing them to c++filt (so they begin with two underscore characters). * * A very useful shell script to add symbols to a crash dump can be found at: * http://developer.apple.com/tools/xcode/symbolizingcrashdumps.html * Pipe the output of the shell script through c++filt to demangle C++ symbols. */ #include #include #include #include #include #include #include // for getpid() #include #include #include #include #include "QSymbols.h" #include "QMachOImageList.h" #include "QBacktrace.h" #include "QCrashReport.h" #include "mac_backtrace.h" // Suppress obsolete warning when building for OS 10.3.9 #define DLOPEN_NO_WARN #include // Functions available only in OS 10.5 and later typedef int (*backtraceProc)(void**,int); #define CALL_STACK_SIZE 128 extern void * _sigtramp; enum { kFrameCount = 200 }; #define SKIPFRAME 4 /* Number frames overhead for signal handler and backtrace */ static char * PersistentFGets(char *buf, size_t buflen, FILE *f); static void GetNameOfAndPathToThisProcess(char *nameBuf, size_t nameBufLen, char* outbuf, size_t outBufLen); static void PrintOSVersion(char *minorVersion); void PrintBacktrace(void) { int err; QCrashReportRef crRef = NULL; char nameBuf[256], pathToThisProcess[1024], pipeBuf[1024]; const NXArchInfo *localArch; char OSMinorVersion; time_t t; void *callstack[CALL_STACK_SIZE]; int frames, i; void *systemlib = NULL; FILE *f; backtraceProc myBacktraceProc = NULL; char saved_env[128], *env; #if 0 // To debug backtrace logic: // * Enable this block of code. // * Set a breakpoint at sleep(1) call, and wherever else you wish. // * Launch built development application from Finder. // * Get this application's pid from Activity Monitor. // * Attach Debugger to this application. // * Continue until you reach this breakpoint. // * Change wait variable to 0 (false). // This is necessary because GDB intercepts signals even if you tell it // not to, so you must attach GDB after the signal handler is invoked. bool wait = true; while (wait) { fprintf(stderr, "waiting\n"); sleep(1); } #endif GetNameOfAndPathToThisProcess(nameBuf, sizeof(nameBuf), pathToThisProcess, sizeof(pathToThisProcess)); if (nameBuf[0]) { fprintf(stderr, "\nCrashed executable name: %s\n", nameBuf); } #ifdef BOINC_VERSION_STRING fprintf(stderr, "built using BOINC library version %s\n", BOINC_VERSION_STRING); #endif localArch = NXGetLocalArchInfo(); fprintf(stderr, "Machine type %s", localArch->description); #ifdef __LP64__ fprintf(stderr, " (64-bit executable)\n"); #else fprintf(stderr, " (32-bit executable)\n"); #endif PrintOSVersion(&OSMinorVersion); time(&t); fputs(asctime(localtime(&t)), stderr); fputc('\n', stderr); err = QCRCreateFromSelf(&crRef); // Use new backtrace functions if available (only in OS 10.5 and later) systemlib = dlopen ("/usr/lib/libSystem.dylib", RTLD_NOW ); if (systemlib) { myBacktraceProc = (backtraceProc)dlsym(systemlib, "backtrace"); } if (myBacktraceProc) { frames = myBacktraceProc(callstack, CALL_STACK_SIZE); // The backtrace_symbols() and backtrace_symbols_fd() APIs are limited to // external symbols only, so we use the atos command-line utility which // checks all symbols and gives us debugging symbols when available. // // The bidirectional popen only works if the NSUnbufferedIO environment // variable is set, so we save and restore its current value. env = getenv("NSUnbufferedIO"); if (env) { strlcpy(saved_env, env, sizeof(saved_env)); } setenv("NSUnbufferedIO", "YES", 1); // For some reason, using the -p option with the value from getpid() // fails here but the -o option with a path does work. #ifdef __x86_64__ snprintf(pipeBuf, sizeof(pipeBuf), "/usr/bin/atos -o \"%s\" -arch x86_64", pathToThisProcess); #elif defined (__i386__) snprintf(pipeBuf, sizeof(pipeBuf), "/usr/bin/atos -o \"%s\" -arch i386", pathToThisProcess); #else snprintf(pipeBuf, sizeof(pipeBuf), "/usr/bin/atos -o \"%s\" -arch ppc", pathToThisProcess); #endif f = popen(pipeBuf, "r+"); if (f) { setbuf(f, 0); for (i=0; iProductUserVisibleVersion"); if (p1) { p1 = strstr(p1, "") + 8; p2 = strstr(p1, ""); if (p2) { // Extract the minor system version number character p3 = strchr(p2, '.'); *OSMinorVersion = *(p3+1); // Pass minor version number back to caller // Now print the full OS version string fputs("System version: Macintosh OS ", stderr); while (p1 < p2) { fputc(*p1++, stderr); } } } if (p2) { p2 = NULL; p1 = strstr(buf, "ProductBuildVersion"); if (p1) { p1 = strstr(p1, "") + 8; p2 = strstr(p1, ""); if (p2) { fputs(" build ", stderr); while (p1 < p2) { fputc(*p1++, stderr); } } } } fputc('\n', stderr); fclose(f); }