// 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. */ #ifdef __ppc__ #include #endif #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" #include "filesys.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); typedef char ** (*backtrace_symbolsProc)(void* const*,int); #define CALL_STACK_SIZE 128 extern void * _sigtramp; enum { kFrameCount = 200 }; #define SKIPFRAME 4 /* Number frames overhead for signal handler and backtrace */ static void PrintOSVersion(char *minorVersion); void PrintBacktrace(void) { int err; QCrashReportRef crRef = NULL; char nameBuf[256], pathToThisProcess[1024]; const NXArchInfo *localArch; char OSMinorVersion = '?'; time_t t; char atosPipeBuf[1024], cppfiltPipeBuf[1024]; char outBuf[1024], offsetBuf[32]; char *sourceSymbol, *symbolEnd; char **symbols = NULL; void *callstack[CALL_STACK_SIZE]; int frames, i; void *systemlib = NULL; FILE *atosPipe = NULL; FILE *cppfiltPipe = NULL; backtraceProc myBacktraceProc = NULL; backtrace_symbolsProc myBacktrace_symbolsProc = NULL; char saved_env[128], *env = NULL; bool atosExists = false, cppfiltExists = false; #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); if (OSMinorVersion == '5') { #ifdef __ppc__ fputs("BOINC backtrace under OS 10.5.x only shows exported (global) symbols\n", stderr); fputs("and may work poorly on a PowerPC Mac after a crash. For a better\n", stderr); fputs("backtrace, run under OS 10.4.x.\n\n", stderr); #else fputs("BOINC backtrace under OS 10.5.x only shows exported (global) symbols\n", stderr); fputs("and may not show the final location which caused a crash. For a better\n", stderr); fputs("backtrace, either run under OS 10.4.x or run under OS 10.6.x or later.\n\n", stderr); #endif } if (OSMinorVersion >= '5') { // 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) { goto skipBackTrace; // Should never happen } frames = myBacktraceProc(callstack, CALL_STACK_SIZE); myBacktrace_symbolsProc = (backtrace_symbolsProc)dlsym(systemlib, "backtrace_symbols"); if (myBacktrace_symbolsProc) { symbols = myBacktrace_symbolsProc(callstack, frames); } else { goto skipBackTrace; // Should never happen } atosExists = boinc_file_exists("/usr/bin/atos"); cppfiltExists = boinc_file_exists("/usr/bin/atos"); if (atosExists || cppfiltExists) { // 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); } if (atosExists) { // The backtrace_symbols() and backtrace_symbols() APIs are limited to // external symbols only, so we also use the atos command-line utility // which gives us debugging symbols when available. // // 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(atosPipeBuf, sizeof(atosPipeBuf), "/usr/bin/atos -o \"%s\" -arch x86_64", pathToThisProcess); #elif defined (__i386__) snprintf(atosPipeBuf, sizeof(atosPipeBuf), "/usr/bin/atos -o \"%s\" -arch i386", pathToThisProcess); #else snprintf(atosPipeBuf, sizeof(atosPipeBuf), "/usr/bin/atos -o \"%s\" -arch ppc", pathToThisProcess); #endif atosPipe = popen(atosPipeBuf, "r+"); if (atosPipe) { setbuf(atosPipe, 0); } } if (cppfiltExists) { cppfiltPipe = popen("/usr/bin/c++filt -s gnu-v3 -n", "r+"); if (cppfiltPipe) { setbuf(cppfiltPipe, 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); }