// 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);
}