// 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 <stdio.h>
#include <unistd.h>     // for getpid()
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "mac_backtrace.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 MacBTPPCFrame *frameArray, unsigned long frameCount, unsigned char lookupSymbolNames);


void PrintBacktrace(void) {
        int                 err;
        MacBTPPCFrame       frames[kFrameCount];
        unsigned long       frameCount;
        unsigned long       validFrames;
        char                OSMinorVersion;

        PrintNameOfThisApp();
        PrintOSVersion(&OSMinorVersion);        

        frameCount = sizeof(frames) / sizeof(*frames);
        err = MacBacktracePPCMachSelf(0, 0, frames, frameCount, &validFrames, OSMinorVersion);
        if (err == 0) {
                if (validFrames > frameCount) {
                        validFrames = frameCount;
                }
                err = OutputFrames(frames, validFrames, true);
        }
}


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


// 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 void ReplaceSymbolIfBetter(MacAToSSymInfo *existingSymbol, MacAToSSymbolType symbolType, const char * symbolName, unsigned long symbolOffset) {
	unsigned char replace;
		
	if (existingSymbol->symbolType == kMacAToSNoSymbol) {
		replace = true;
	} else {
		replace = (symbolOffset < existingSymbol->symbolOffset);
	}
	
	if (replace) {
		existingSymbol->symbolType   = symbolType;
                strncpy(existingSymbol->symbolName, symbolName, sizeof(existingSymbol->symbolName)-1);
                existingSymbol->symbolName[sizeof(existingSymbol->symbolName)-1] = '\0';
		existingSymbol->symbolOffset = symbolOffset;
	}
}


// FindOwnerOfPC and GetFunctionName countesy of Ed Wynne.

static const struct mach_header *FindOwnerOfPC(unsigned int pc) {
        unsigned int			count,index,offset,cmdex;
        struct segment_command          *seg;
        struct load_command		*cmd;
        struct mach_header		*header;
        
        count = _dyld_image_count();
        for (index = 0;index < count;index += 1)
        {
                header = (struct mach_header*)_dyld_get_image_header(index);
                offset = _dyld_get_image_vmaddr_slide(index);
                cmd = (struct load_command*)((char*)header + sizeof(struct mach_header));
                for (cmdex = 0;cmdex < header->ncmds;cmdex += 1,cmd = (struct load_command*)((char*)cmd + cmd->cmdsize))
                {
                        switch(cmd->cmd)
                        {
                                case LC_SEGMENT:
                                        seg = (struct segment_command*)cmd;
                                        if ((pc >= (seg->vmaddr + offset)) && (pc < (seg->vmaddr + offset + seg->vmsize)))
                                                return header;
                                        break;
                        }
                }
        }
        
        return NULL;
}

static const char *GetFunctionName(unsigned int pc,unsigned int *offset, unsigned char *publicSymbol) {
        struct segment_command	*seg_linkedit = NULL;
        struct segment_command	*seg_text = NULL;
        struct symtab_command	*symtab = NULL;
        struct load_command		*cmd;
        const struct mach_header*header;
        unsigned int			vm_slide,file_slide;
        struct nlist			*sym,*symbase;
        char					*strings,*name;
        unsigned int			base,index;
        
        header = FindOwnerOfPC(pc);
        if (header != NULL)
        {
                cmd = (struct load_command*)((char*)header + sizeof(struct mach_header));
                for (index = 0;index < header->ncmds;index += 1,cmd = (struct load_command*)((char*)cmd + cmd->cmdsize))
                {
                        switch(cmd->cmd)
                        {
                                case LC_SEGMENT:
                                        if (!strcmp(((struct segment_command*)cmd)->segname,SEG_TEXT))
                                                seg_text = (struct segment_command*)cmd;
                                        else if (!strcmp(((struct segment_command*)cmd)->segname,SEG_LINKEDIT))
                                                seg_linkedit = (struct segment_command*)cmd;
                                        break;
                                
                                case LC_SYMTAB:
                                        symtab = (struct symtab_command*)cmd;
                                        break;
                        }
                }
                
                if ((seg_text == NULL) || (seg_linkedit == NULL) || (symtab == NULL))
                {
                        *offset = 0;
                        return NULL;
                }
                
                vm_slide = (unsigned long)header - (unsigned long)seg_text->vmaddr;
                file_slide = ((unsigned long)seg_linkedit->vmaddr - (unsigned long)seg_text->vmaddr) - seg_linkedit->fileoff;
                symbase = (struct nlist*)((unsigned long)header + (symtab->symoff + file_slide));
                strings = (char*)((unsigned long)header + (symtab->stroff + file_slide));
                
                // Look for a global symbol.
                for (index = 0,sym = symbase;index < symtab->nsyms;index += 1,sym += 1)
                {
                        if (sym->n_type != N_FUN)
                                continue;
                        
                        name = sym->n_un.n_strx ? (strings + sym->n_un.n_strx) : NULL;
                        base = sym->n_value + vm_slide;
                        
                        for (index += 1,sym += 1;index < symtab->nsyms;index += 1,sym += 1)
                                if (sym->n_type == N_FUN)
                                        break;
                        
                        if ((pc >= base) && (pc <= (base + sym->n_value)) && (name != NULL) && (strlen(name) > 0))
                        {
                                *offset = pc - base;
                                *publicSymbol = true;
                                return strdup(name);
                        }
                }
                
                // Look for a reasonably close private symbol.
                for (name = NULL,base = 0xFFFFFFFF,index = 0,sym = symbase;index < symtab->nsyms;index += 1,sym += 1)
                {
                        if ((sym->n_type & 0x0E) != 0x0E)
                                continue;
                        
                        if ((sym->n_value + vm_slide) > pc)
                                continue;
                        
                        if ((base != 0xFFFFFFFF) && ((pc - (sym->n_value + vm_slide)) >= (pc - base)))
                                continue;
                        
                        name = sym->n_un.n_strx ? (strings + sym->n_un.n_strx) : NULL;
                        base = sym->n_value + vm_slide;
                }
                
                *offset = pc - base;
                *publicSymbol = false;
                return (name != NULL) ? strdup(name) : NULL;
        }
        
        *offset = 0;
        return NULL;
}

static int AToSCopySymbolNameUsingDyld(MacAToSAddr address, MacAToSSymInfo *symbol) {
        const char * 		thisSymbol;
        char *                  colonPtr;
        unsigned int 		thisSymbolOffset;
        unsigned char      	thisSymbolPublic;
        MacAToSSymbolType	thisSymbolType;
        int                     err = 0;
        
        thisSymbol = NULL;
        if (address != NULL) {		// NULL is never a useful symbol
                thisSymbol = GetFunctionName( (unsigned int) address, &thisSymbolOffset, &thisSymbolPublic);
        }
        if (thisSymbol != NULL) {
                // Symbols are sometimes followed by a colon and other characters.
                // If there is a colon, replace it with a null terminator
                colonPtr = strchr(thisSymbol, ':');
                if (colonPtr)
                    *colonPtr = '\0';
                
                if (thisSymbolPublic) {
                        thisSymbolType = kMacAToSDyldPubliSymbol;
                } else {
                        thisSymbolType = kMacAToSDyldPrivateSymbol;
                }
                
                if (err == 0) {	
                        ReplaceSymbolIfBetter(symbol, thisSymbolType, thisSymbol, (unsigned long) thisSymbolOffset);
                }
        } else {
            symbol->symbolName[0] = '\0';
        }
        
        free( (void *) thisSymbol);
        
        return err;
}



static int OutputFrames(const MacBTPPCFrame *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.

        int                     err;
        unsigned long		frameIndex, skipframe = 0;
        MacAToSSymInfo          symbol;
        MacAToSAddr             address;

        err = 0;
        
        if ((frameCount >= SKIPFRAME) && frameArray[SKIPFRAME-1].flags & kMacBTSignalHandlerMask)
            skipframe = SKIPFRAME;
        
        fputs("Stack Frame backtrace:\n #  Flags Frame Addr  Caller PC   Symbol\n"
                        "===  ===  ==========  ==========  ==========\n", stderr);
        
        for (frameIndex = skipframe; frameIndex < frameCount; frameIndex++) {
                
                fprintf(stderr, "%3ld  %c%c%c  0x%08lx  0x%08lx  ", 
                                 frameIndex - skipframe + 1,
                                 (frameArray[frameIndex].flags & kMacBTFrameBadMask)      ? 'F' : '-',
                                 (frameArray[frameIndex].flags & kMacBTPCBadMask)         ? 'P' : '-',
                                 (frameArray[frameIndex].flags & kMacBTSignalHandlerMask) ? 'S' : '-',
                                 frameArray[frameIndex].sp, 
                                 frameArray[frameIndex].pc);
                                                         
                if (frameArray[frameIndex].flags & kMacBTPCBadMask) {
                        address = NULL;
                } else {
                        address = (MacAToSAddr) frameArray[frameIndex].pc;
                }

                symbol.symbolName[0] = '\0';
                symbol.symbolType = kMacAToSNoSymbol;
                symbol.symbolOffset = 0;
                
                err = AToSCopySymbolNameUsingDyld(address, &symbol);
                
                if (symbol.symbolName[0]) {
                    fprintf(stderr, "%s + 0x%lx", 
                                    symbol.symbolName, symbol.symbolOffset);
                }

                fputs("\n", stderr);
        }
        
        return err;
}

#pragma mark ***** PowerPC Backtrace Core

/*	PowerPC Stack Frame Basics
	--------------------------
	
					Size	Purpose
					----	-------
	low memory ->	0x004	pointer to next frame
					0x004	place to save CR
					0x004	place to save LR
					0x008	reserved
	high memory ->	0x004	place to save TOC (CFM only)
					
					To get from one frame to the next, you have to indirect 
					through an offset of 0 (kMacBTPPCOffsetToSP).  To 
					extract the return address from a frame, you have to 
					indirect an offset of 8 (kMacBTPPCOffsetToLR).
*/

enum {
	kMacBTPPCOffsetToSP	= 0,
	kMacBTPPCOffsetToLR 	= 8
};

/*	PowerPC Signal Stack Frames
	---------------------------
	In the current Mac OS X architecture, there is no guaranteed reliable 
	way to backtrace a signal stack frame.  The problem is that the kernel 
	pushes a variable amount of data on to the stack when it invokes the 
	user space signal trampoline (_sigtramp), and the only handle to the 
	information about how much data was pushed is passed in a register 
	parameter to _sigtramp.  _sigtramp stashes that value away in a 
	non-volatile register.  So, when _sigtramp calls the user-supplied 
	signal handler, there's no way to work out where that register 
	ends up being saved.
	
	Thus, we devolve into guesswork.  It turns out that the offset from 
	the stack of the kernel data to the information we need (the place 
	where the interrupted thread's SP was stored) is a constant for any 
	given system release.  So, we can just simply add the appropriate 
	offset to the frame pointer and grab the data we need.
	
	The problem is that this constant varies from release to release. 
	This code handles Mac OS X 10.2.x through 10.4.x.  There's no 
	guarantee that this offset won't change again in the future.

	When the kernel invokes the user space signal trampoline, it pushes 
	the following items on to the stack.
	
	Mac OS X 10.2.x
	---------------
					Size	Purpose
					----	-------
	low memory ->	0x030   bytes for C linkage
					0x040 	bytes for saving PowerPC parameters
					0x008	alignment padding
					0x408   struct mcontext, comprised of:
								 0x020 ppc_exception_state_t
								 0x0A0 ppc_thread_state_t
								 0x108 ppc_float_state_t
								 0x240 ppc_vector_state_t
					0x040	siginfo_t
					0x020	ucontext
	high memory ->	0x0e0	red zone
	
					The previous frame's SP is at offset 0x00C within 
					ppc_thread_state_t, which makes 
					kMacBTPPCOffsetToSignalSPTenTwo equal to 
					0x030 + 0x040 + 0x008 + 0x020 + 0x00C, or 0x0A4.
*/

enum {
	kMacBTPPCOffsetToSignalSPTenOne = 0x07C,
	kMacBTPPCOffsetToSignalSPTenTwo = 0x0A4, 
        // We determined the offsets for OS 10.3 and 10.4 heuristically by 
        // examining the contents of the stack
        kMacBTPPCOffsetToSignalSPTenThree = 0x09C,
        // The same offset seems to work for OS 10.4 as for OS 10.3.  Other 
        // possible values for the offset in 10.4 are 0x0a8 and 0x0c8.  (In 
        // our tests on OS 10.4.2, the same correct frame pointer appeared 
        // at all three of these offsets.)
        kMacBTPPCOffsetToSignalSPTenFour = 0x09C
};

/*	PowerPC Signal Stack Frames (cont)
	----------------------------------
	The only remotely reliable way to detect a signal stack frame is to 
	look at the return address to see whether it points within the 
	_sigtramp routine.  I can find the address of this routine via 
	the dynamic linker, but I don't have an easy way to determine it's 
	length.  So I just guess!  Fortunately, this is rarely a problem.
        And here's the number I chose.  
*/

enum {
	kMacBTPPCSigTrampSize	= 256
};

typedef struct MacBTPPCContext MacBTPPCContext;

typedef int (*MacBTReadBytesProc)(MacBTPPCContext *context, MacBTPPCAddr src, void *dst, unsigned long size);
	// This function pointer is called by the core backtrace code 
	// when it needs to read memory.  The callback should do a safe 
	// read of size bytes from src into the buffer specified by 
	// dst.  By "safe" we mean that the routine should return an error 
	// if the read can't be done (typically because src is a pointer to 
	// unmapped memory).

// The MacBTPPCContext structure is used by the core backtrace code 
// to maintain its state.

struct MacBTPPCContext {

	// Internal parameters that are set up by the caller 
	// of the core backtrace code.
	
	unsigned long			offsetToSignalSP;
	MacBTPPCAddr			sigTrampLowerBound;
	MacBTPPCAddr			sigTrampUpperBound;
	MacBTReadBytesProc              readBytes;
	void *				refCon;
	
	// Parameters from client.
	
	MacBTPPCAddr	pc;
	MacBTPPCAddr	r0;			// see MacBTPPCCheckLeaf
	MacBTPPCAddr	sp;
	MacBTPPCAddr	lr;
	MacBTPPCAddr	stackBottom;
	MacBTPPCAddr	stackTop;
	MacBTPPCFrame   *frameArray;		// array contents filled out by core
	unsigned long 	frameArrayCount;
	unsigned long 	frameCountOut;		// returned by core
};

static int ReadPPCAddr(MacBTPPCContext *context, MacBTPPCAddr addr, MacBTPPCAddr *value) {
	// Reads a PowerPC address (ie a pointer) from the target task, 
	// returning an error if the memory is unmapped.

	return context->readBytes(context, addr, value, sizeof(*value));
}

static int ReadPPCInst(MacBTPPCContext *context, MacBTPPCAddr addr, MacBTPPCInst *value) {
	// Reads a PowerPC instruction from the target task, 
	// returning an error if the memory is unmapped.

	return context->readBytes(context, addr, value, sizeof(*value));
}

static int MacBTPPCCheckLeaf(MacBTPPCContext *context) {
	// The top most frame may be in a weird state because of the 
	// possible variations in the routine prologue.  There are a 
	// variety of combinations, such as:
	//
	// 1. a normal routine, with its return address stored in 
	//    its caller's stack frame
	//
	// 2. a system call routine, which is a leaf routine with 
	//    no frame and the return address is in LR
	//
	// 3. a leaf routine with no frame, where the return address 
	//    is in LR
	//
	// 4. a leaf routine with no frame that accesses a global, where 
	//    the return address is in R0
	//
	// 5. a normal routine that was stopped midway through 
	//    constructing its prolog, where the return address is 
	//    typically in R0
	//
	// Of these, 1 and 2 are most common, and they're the cases I 
	// handle.  General support for all of the cases requires the 
	// ability to accurately determine the start of the routine 
	// which is not something that I can do with my current 
	// infrastructure.
	//
	// Note that don't handle any cases where the return address is 
	// in R0, although I do have a variable for R0 in the context 
	// in case I add that handling in the future.

	int                     err;
	unsigned char           isSystemCall;
	MacBTPPCInst            inst;
	MacBTPPCInst            pc;
	int			count;

	// Using the PC from the top frame (frame[0]), walk back through 
	// the code stream for 3 instructions looking for a "sc" instruction. 
	// If we find one, it's almost certain that we're in a system call 
	// frameless leaf routine.

	isSystemCall = false;
	count = 0;
	pc = context->pc;
	do {
		err = ReadPPCInst(context, pc, &inst);
		if (err == 0) {
			isSystemCall = (inst == 0x44000002);			// PPC "sc" instruction
		}
		if ( (err == 0) && ! isSystemCall ) {
			count += 1;
			pc -= sizeof(MacBTPPCInst);
		}
	} while ( (err == 0) && ! isSystemCall && (count < 3) );
	err = 0;
	
	// If we find that we're in a system call frameless leaf routine, 
	// te add a dummy stack frame (with no frame, because the frame actually 
	// belows to frameArray[1]).

	if (isSystemCall) {
		if ( (context->frameArray != NULL) && (context->frameCountOut < context->frameArrayCount) ) {
			MacBTPPCFrame *	frameOutPtr;

			frameOutPtr = &context->frameArray[context->frameCountOut];
			frameOutPtr->pc    = context->pc;
			frameOutPtr->sp    = 0;
			frameOutPtr->flags = kMacBTFrameBadMask;
		}
		context->frameCountOut += 1;

		context->pc = context->lr;
	}

	return err;
}

static int MacBacktracePPCCore(MacBTPPCContext *context) {
	// The core backtrace code.  This routine is called by all of the various 
	// exported routines.  It implements the core backtrace functionality. 
	// All of the parameters to this routine are contained within 
	// the context.  This routine traces back through the stack (using the 
	// readBytes callback in the context to actually read memory) creating 
	// a backtrace.

	int                     err;
	MacBTPPCAddr            thisPC;
	MacBTPPCAddr            thisFrame;
	MacBTPPCAddr            lowerBound;
	MacBTPPCAddr            upperBound;
	unsigned char           stopNow;
	
	lowerBound = context->stackBottom;
	upperBound = context->stackTop;
	if (upperBound == 0) {
		upperBound = (MacBTPPCAddr) -1;
	}
	
	// Check the current PC and add a dummy frame if it points to 
	// a frameless leaf routine.
	
	context->frameCountOut = 0;
	err = MacBTPPCCheckLeaf(context);
	
	// Handle the normal frames.
	
	if (err == 0) {
		thisPC     = context->pc;
		thisFrame  = context->sp;
		
		stopNow = false;
		do {
			MacBTPPCFrame           * frameOutPtr;
			MacBTPPCFrame     	tmpFrameOut;
			MacBTPPCAddr 		nextFrame;
			MacBTPPCAddr 		nextPC;
			MacBTPPCInst		junkInst;
			
			// Output to a tmpFrameOut unless the client has supplied 
			// a buffer and there's sufficient space left in it.
			
			if ( (context->frameArray != NULL) && (context->frameCountOut < context->frameArrayCount) ) {
				frameOutPtr = &context->frameArray[context->frameCountOut];
			} else {
				frameOutPtr = &tmpFrameOut;
			}
			context->frameCountOut += 1;

			// Record this entry.
			
			frameOutPtr->pc    = thisPC;
			frameOutPtr->sp    = thisFrame;
			frameOutPtr->flags = 0;
			
			// Now set the flags to indicate the validity of specific information. 
			
			// Check the validity of the PC.  Don't set the err here; a bad PC value 
			// does not cause us to quit the backtrace.
			
			if ( (((int) thisPC) & 0x03) || (ReadPPCInst(context, thisPC, &junkInst) != 0) ) {
				frameOutPtr->flags |= kMacBTPCBadMask;
			}
                        			
			// Check the validity of the frame pointer.  A bad frame pointer *does* 
			// cause us to stop tracing.
			
			if ( (thisFrame == 0L) || (((int) thisFrame) & 0x03) || (thisFrame < lowerBound) || (thisFrame >= upperBound) ) {
				frameOutPtr->flags |= kMacBTFrameBadMask;
				stopNow = true;
			}

			if (err == 0 && ! stopNow) {
			
				// Read the next frame pointer.  Again, a failure here causes us to quit 
				// backtracing.  Note that we set kMacBTFrameBadMask in frameOutPtr 
				// because, if we can't read the contents of the frame pointer, the 
				// frame pointer itself must be bad.
				
				err = ReadPPCAddr(context, thisFrame + kMacBTPPCOffsetToSP, &nextFrame);
				if (err != 0) {
					frameOutPtr->flags |= kMacBTFrameBadMask;
					// No need to set stopNow because err != 0 will 
					// terminate loop.
				}

				// If the next frame pointer indicates that this frame was called 
				// as a signal handler, handle the discontinuity in the stack.
				
				if (err == 0) {
					// Extract the LR from the stack frame.    Note that we have to do 
					// this before we check for a signal frame because the PC of 
					// the frame that was interrupted by the signal is stored 
					// in this nextFrame, not in the one we'll get by delving 
					// into the signal handler stack block.
					
					if ( ReadPPCAddr(context, nextFrame + kMacBTPPCOffsetToLR, &nextPC) != 0 ) {
						nextPC = (MacBTPPCAddr) -1;		// an odd value, to trigger above check on next iteration
					}
					
					// delving into the signal handler stack block.
					
					if (      !(frameOutPtr->flags & kMacBTPCBadMask) 
							&& ( frameOutPtr->pc >= context->sigTrampLowerBound ) 
							&& ( frameOutPtr->pc <  context->sigTrampUpperBound ) ) {
						frameOutPtr->flags |= kMacBTSignalHandlerMask;
#if 0
                                                // This code allows us to examine the stack to find the correct 
                                                // value for offsetToSignalSP for new releases of OS X.  We 
                                                // don't use it in production
                                                for (long index=0;index<256;index+=4) {
                                                    MacBTPPCAddr value;
                                                    ReadPPCAddr(context, nextFrame + index, &value);
                                                    fprintf(stderr, "offset %lx: value = %lx\n", index, value);
                                                }
#endif
						err = ReadPPCAddr(context, nextFrame + context->offsetToSignalSP, &nextFrame);
					}
				}

				// Set up for the next iteration.
				
				if (err == 0) {
					lowerBound = thisFrame;
					thisPC     = nextPC;
					thisFrame  = nextFrame;
				}
			}
		} while ( (err == 0) && ! stopNow );
	}
	
	return err;
}

static int InitMacBTPPCContext(MacBTPPCContext *context,
                                    MacBTPPCAddr stackBottom, MacBTPPCAddr stackTop,
                                    MacBTPPCFrame *frameArray, unsigned long frameArrayCount, 
                                    char OSMinorVersion) {
	// Initialises a MacBTPPCContext to appropriate default values.
	int err;
		
	memset(context, 0, sizeof(context));
	
	// We don't check the input parameters here.  Instead the 
	// check is done by the backtrace core.
	
	context->stackBottom     = stackBottom;
	context->stackTop        = stackTop;
	context->frameArray      = frameArray;
	context->frameArrayCount = frameArrayCount;
	
	// Some system version specific parameters:
	//
	// o _sigtramp is irrelevant on traditional Mac OS.
	// o We don't support Mac OS X 10.0.x.
	// o offsetToSignalSP changed between 10.1.x and 10.2.x.
	
	err = 0;

        if (OSMinorVersion == '3')
            context->offsetToSignalSP = kMacBTPPCOffsetToSignalSPTenThree;
        else
            context->offsetToSignalSP = kMacBTPPCOffsetToSignalSPTenFour;
        
        context->sigTrampLowerBound = (MacBTPPCAddr) & _sigtramp;

        // We can't actually determine the size of _sigtramp with our current 
        // technology, so we just guess at the upper bound.
        context->sigTrampUpperBound = context->sigTrampLowerBound + kMacBTPPCSigTrampSize;
	return err;
}

/////////////////////////////////////////////////////////////////
#pragma mark ***** Mach Interface

// The Mach interface works accesses all backtrace memory via 
// Mach VM calls, and thus there's the potential for it to execute 
// on a instruction set architecture other than the one being 
// backtraced.  Hence, there's no requirement for TARGET_CPU_PPC here.

static int MacBTReadBytesMach(MacBTPPCContext *context, MacBTPPCAddr src, void *dst, unsigned long size) {
        // A memory read callback for Mach (see MacBTReadBytesProc). 
        // This simply calls through to the Mach vm_read_overwrite 
        // primitive, which does exactly what we want.
        int             err;
        vm_size_t 	sizeRead;
        
        sizeRead = size;
        err = vm_read_overwrite( (thread_t) context->refCon, (vm_address_t) src, size, (vm_address_t) dst, &sizeRead);
        if ( (err == 0) && (sizeRead != size) ) {
                err = -1;
        }
        return err;
}

int MacBacktracePPCMach(task_t task, MacBTPPCAddr pc, MacBTPPCAddr sp,
                            MacBTPPCAddr stackBottom, MacBTPPCAddr stackTop,
                            MacBTPPCFrame *frameArray, unsigned long frameArrayCount, 
                            unsigned long *frameCount, char OSMinorVersion) {
        // See comments in header.
        int 			err;
        MacBTPPCContext 	context;
        
        err = InitMacBTPPCContext(&context, stackBottom, stackTop, frameArray, frameArrayCount, OSMinorVersion);
        if (err == 0) {
                context.pc = pc;
                context.sp = sp;
                context.readBytes = MacBTReadBytesMach;
                context.refCon    = (void *) task;
                
                err = MacBacktracePPCCore(&context);
        }
        if (frameCount != NULL) {
                *frameCount = context.frameCountOut;
        }
        return err;
}

/////////////////////////////////////////////////////////////////
#pragma mark ***** Assembly Stuff

// The following is an inline assembly abstraction layer that isolates 
// the rest of the code from the specific compiler's flavour of assembly. 
// Note that this is not as clean as I'd like it to be.  Specifically, 
// MacBTPPCGetProgramCounter is a nice function definition, but 
// MacBTPPCGetStackPointer is a macro.  If I define MacBTPPCGetStackPointer 
// as a function, I have problems because GCC insists on building a 
// stack from in non-optimised builds but no stack frame in optimised 
// builds.  That makes things tricky.  So, instead, I use a macro that 
// expands inside the function itself.  This causes other issues.  
// For example, in MacBacktracePPCCarbonSelf I had to name the local 
// variable "mySP" and not "sp", because otherwise the CodeWarrior 
// inline assembler can't distinguish between the variable and the 
// register.  Also, mySP has to qualified as "register" for the sake 
// of the CodeWarrior inline assembler.  Fortunately, these changes 
// do not cause problems for GCC.

// Obviously the "Self" calls require TARGET_CPU_PPC.


#define MacBTPPCGetStackPointer(result)						\
        __asm__ volatile("mr		%0,r1" : "=r" (result));

static MacBTPPCAddr MacBTPPCGetProgramCounter(void) {
    MacBTPPCAddr result;
    __asm__ volatile("mflr		%0" : "=r" (result));
    return result;
}

int MacBacktracePPCMachSelf(MacBTPPCAddr stackBottom, MacBTPPCAddr stackTop,
                                MacBTPPCFrame *frameArray, unsigned long frameArrayCount, 
                                unsigned long *frameCount, char OSMinorVersion) {
        // See comments in header.
        register MacBTPPCAddr   mySP;
        MacBTPPCAddr            myPC;
        
        // For Mac information about these inline assembly routines, 
        // see the "***** Assembly Stuff" comment above.

        MacBTPPCGetStackPointer(mySP);
        myPC = MacBTPPCGetProgramCounter();
        
        return MacBacktracePPCMach(mach_task_self(), myPC, mySP, stackBottom, stackTop, frameArray, frameArrayCount, frameCount, OSMinorVersion);
}
