mirror of https://github.com/BOINC/boinc.git
299 lines
9.9 KiB
C
299 lines
9.9 KiB
C
// Berkeley Open Infrastructure for Network Computing
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2005 University of California
|
|
//
|
|
// This 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 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
// This software 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.
|
|
//
|
|
// To view the GNU Lesser General Public License visit
|
|
// http://www.gnu.org/copyleft/lesser.html
|
|
// or write to the Free Software Foundation, Inc.,
|
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
/*
|
|
* mac_backtrace.C
|
|
*
|
|
*/
|
|
|
|
/* This is a rudimentary backtrace generator for boinc project applications.
|
|
*
|
|
* It is adapted from Apple Developer Technical Support Sample Code
|
|
* MoreisBetter / MoreDebugging / MoreBacktraceTest
|
|
* The symbols it displays are not always clean.
|
|
*
|
|
* This code handles Mac OS X 10.3.x through 10.4.2. 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).
|
|
*
|
|
* Flags in backtrace:
|
|
* F this frame pointer is bad
|
|
* P this PC is bad
|
|
* S this frame is a signal handler
|
|
*
|
|
*/
|
|
|
|
|
|
#include <mach-o/dyld.h>
|
|
#include <mach-o/loader.h>
|
|
#include <mach-o/nlist.h>
|
|
#include <mach-o/stab.h>
|
|
#include <mach-o/arch.h>
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h> // for getpid()
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
|
|
#include "mac_backtrace.h"
|
|
#include "MoreBacktrace.h"
|
|
#include "MoreAddrToSym.h"
|
|
|
|
extern void * _sigtramp;
|
|
|
|
enum {
|
|
kFrameCount = 200
|
|
};
|
|
|
|
#define SKIPFRAME 4 /* Number frames overhead for signal handler and backtrace */
|
|
|
|
static void PrintNameOfThisApp(void);
|
|
static void PrintOSVersion(char *minorVersion);
|
|
static int OutputFrames(const MoreBTFrame *frameArray, unsigned long frameCount, unsigned char lookupSymbolNames);
|
|
|
|
void PrintBacktrace(void) {
|
|
int err;
|
|
MoreBTFrame frames[kFrameCount];
|
|
unsigned long frameCount;
|
|
unsigned long validFrames;
|
|
char OSMinorVersion;
|
|
time_t t;
|
|
int didSuspend;
|
|
task_t targetTask;
|
|
thread_array_t threadList;
|
|
thread_t currentThread;
|
|
mach_msg_type_number_t threadCount, thisThread;
|
|
|
|
|
|
PrintNameOfThisApp();
|
|
PrintOSVersion(&OSMinorVersion);
|
|
|
|
time(&t);
|
|
fputs(asctime(localtime(&t)), stderr);
|
|
|
|
frameCount = sizeof(frames) / sizeof(*frames);
|
|
err = MoreBacktraceMachSelf(0, 0, frames, frameCount, &validFrames); // Calling task first
|
|
if (err == 0) {
|
|
if (validFrames > frameCount) {
|
|
validFrames = frameCount;
|
|
}
|
|
err = OutputFrames(frames, validFrames, true);
|
|
}
|
|
fflush(stderr);
|
|
|
|
|
|
targetTask = mach_task_self();
|
|
currentThread = mach_thread_self();
|
|
err = task_threads(targetTask, &threadList, &threadCount);
|
|
if (threadList != NULL) {
|
|
for (thisThread = 0; thisThread < threadCount; thisThread++) {
|
|
|
|
if (threadList[thisThread] != currentThread) { // Calling task cannot call thread_get_state on itself
|
|
err = thread_suspend(threadList[thisThread]);
|
|
didSuspend = (err == 0);
|
|
err = MoreBacktraceMachThread(targetTask, threadList[thisThread], 0, 0, frames, frameCount, &validFrames);
|
|
if (didSuspend)
|
|
thread_resume(threadList[thisThread]);
|
|
if (err == 0) {
|
|
if (validFrames > frameCount) {
|
|
validFrames = frameCount;
|
|
}
|
|
fprintf(stderr, "\nThread number %d: ", thisThread);
|
|
err = OutputFrames(frames, validFrames, true);
|
|
}
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static char * PersistentFGets(char *buf, size_t buflen, FILE *f) {
|
|
char *p = buf;
|
|
size_t len = buflen;
|
|
size_t datalen = 0;
|
|
|
|
*buf = '\0';
|
|
while (datalen < (buflen - 1)) {
|
|
fgets(p, len, f);
|
|
if (feof(f)) break;
|
|
if (ferror(f) && (errno != EINTR)) break;
|
|
if (strchr(buf, '\n')) break;
|
|
datalen = strlen(buf);
|
|
p = buf + datalen;
|
|
len -= datalen;
|
|
}
|
|
return (buf[0] ? buf : NULL);
|
|
}
|
|
|
|
|
|
static void PrintNameOfThisApp() {
|
|
FILE *f;
|
|
char buf[64], nameBuf[1024];
|
|
pid_t myPID = getpid();
|
|
int i;
|
|
const NXArchInfo *localArch;
|
|
|
|
nameBuf[0] = 0; // in case of failure
|
|
|
|
sprintf(buf, "ps -p %d -c -o command", myPID);
|
|
f = popen(buf, "r");
|
|
if (!f)
|
|
return;
|
|
PersistentFGets(nameBuf, sizeof(nameBuf), f); // Skip over line of column headings
|
|
nameBuf[0] = 0;
|
|
PersistentFGets(nameBuf, sizeof(nameBuf), f); // Get the UNIX command which ran us
|
|
fclose(f);
|
|
|
|
for (i=strlen(nameBuf)-1; i>=0; --i) {
|
|
if (nameBuf[i] <= ' ')
|
|
nameBuf[i] = 0; // Strip off trailing spaces, newlines, etc.
|
|
else
|
|
break;
|
|
}
|
|
|
|
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\n", localArch->description);
|
|
|
|
}
|
|
|
|
|
|
// This is an alternative to using Gestalt(gestaltSystemVersion,..) so
|
|
// we don't need the Carbon Framework
|
|
static void PrintOSVersion(char *OSMinorVersion) {
|
|
char buf[1024], *p1 = NULL, *p2 = NULL, *p3;
|
|
FILE *f;
|
|
int n;
|
|
|
|
f = fopen("/System/Library/CoreServices/SystemVersion.plist", "r");
|
|
if (!f)
|
|
return;
|
|
|
|
n = fread(buf, 1, sizeof(buf)-1, f);
|
|
buf[n] = '\0';
|
|
p1 = strstr(buf, "<key>ProductUserVisibleVersion</key>");
|
|
if (p1) {
|
|
p1 = strstr(p1, "<string>") + 8;
|
|
p2 = strstr(p1, "</string>");
|
|
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, "<key>ProductBuildVersion</key>");
|
|
if (p1) {
|
|
p1 = strstr(p1, "<string>") + 8;
|
|
p2 = strstr(p1, "</string>");
|
|
if (p2) {
|
|
fputs(" build ", stderr);
|
|
while (p1 < p2) {
|
|
fputc(*p1++, stderr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fputc('\n', stderr);
|
|
|
|
fclose(f);
|
|
}
|
|
|
|
|
|
static int OutputFrames(const MoreBTFrame *frameArray, unsigned long frameCount, unsigned char lookupSymbolNames) {
|
|
// Output a textual description of frameCount frames from frameArray.
|
|
// we look up the symbol names of the PCs of each of the frames.
|
|
// This assumes we are not running a 64-bit application
|
|
|
|
int err;
|
|
unsigned long frameIndex, skipframe = 0;
|
|
MoreAToSSymInfo symbol;
|
|
MoreAToSAddr address;
|
|
|
|
err = 0;
|
|
|
|
if ((frameCount >= SKIPFRAME) && (frameArray[SKIPFRAME-1].flags & kMoreBTSignalHandlerMask))
|
|
skipframe = SKIPFRAME;
|
|
|
|
fputs("Stack frame backtrace:\n # Flags Frame Addr Caller PC Return Address Symbol\n"
|
|
"=== === ========== ========== =====================\n", stderr);
|
|
|
|
for (frameIndex = skipframe; frameIndex < frameCount; frameIndex++) {
|
|
|
|
fprintf(stderr, "%3ld %c%c%c 0x%08llx 0x%08llx ",
|
|
frameIndex - skipframe + 1,
|
|
(frameArray[frameIndex].flags & kMoreBTFrameBadMask) ? 'F' : '-',
|
|
(frameArray[frameIndex].flags & kMoreBTPCBadMask) ? 'P' : '-',
|
|
(frameArray[frameIndex].flags & kMoreBTSignalHandlerMask) ? 'S' : '-',
|
|
frameArray[frameIndex].fp,
|
|
frameArray[frameIndex].pc);
|
|
|
|
if (frameArray[frameIndex].flags & kMoreBTPCBadMask) {
|
|
address = 0;
|
|
} else {
|
|
address = (MoreAToSAddr) frameArray[frameIndex].pc;
|
|
}
|
|
|
|
symbol.symbolName = NULL;
|
|
symbol.symbolType = kMoreAToSNoSymbol;
|
|
symbol.symbolOffset = 0;
|
|
|
|
err = MoreAToSCopySymbolNamesUsingDyld(1, &address, &symbol);
|
|
|
|
if (symbol.symbolName) {
|
|
if (symbol.symbolName[0]) {
|
|
fprintf(stderr, "%s + 0x%llx",
|
|
symbol.symbolName, symbol.symbolOffset);
|
|
free( (void *) symbol.symbolName);
|
|
}
|
|
}
|
|
|
|
fputs("\n", stderr);
|
|
}
|
|
|
|
return err;
|
|
}
|