📄 morebacktrace.c
字号:
/* File: MoreBacktrace.c Contains: Code for generating backtraces. Written by: DTS Copyright: Copyright (c) 2006 by Apple Computer, Inc., All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple誷 copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Change History (most recent first):$Log: MoreBacktrace.c,v $Revision 1.6 2006/01/23 18:32:52 eskimo1Correct uninitialised variable pointed out by the compiler.Revision 1.5 2006/01/23 00:59:36 eskimo1Added lots of comments describing the stack structure for each architecture. Also filled in one of the blank with regards stack offsets on older systems.Revision 1.4 2006/01/22 22:46:46 eskimo1Complete rewrite to account for architecture independence.Revision 1.3 2003/04/09 22:48:11 eskimo1Added comments.Revision 1.2 2003/04/09 22:30:14 eskimo1Lots of changes. Rewrote the core to work properly. We now handle leaf routines correctly in the common cases, and document the cases that we don't handle. Also added lots of comments.Revision 1.1 2003/04/04 15:03:04 eskimo1First checked in. This code still has bugs, but I've written enough code that checking in is a good idea.*//////////////////////////////////////////////////////////////////// Our Prototypes#include "opal_config.h"#include "MoreBacktrace.h"// Mac OS Interfaces#include <string.h>#include <stdbool.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <sys/utsname.h>#include <mach-o/arch.h>#if defined(__cplusplus) extern "C" {#endif// Some extra Mach interfaces that we don't need in the public header.// Again, we need C++ guards.#ifdef HAVE_MACH_MACH_VM_H#include <mach/mach_vm.h>#endif#include <mach/vm_region.h>// We want both PowerPC and Intel thread state information.// By default, the system only gives us the one that's appropriate // for our machine. So we include both here.#include <mach/ppc/thread_status.h>#include <mach/i386/thread_status.h>#if defined(__cplusplus) }#endif/* OMPI CHANGE: This is a total hack, but in OS X 10.5 (Leopard), they * renamed all the registers in the thread state structure if * __DARWIN_UNIX03 is defined. So adapt. */#if !defined(HAVE_PPC_THREAD_STATE_T_SRR0)#define srr0 __srr0#define lr __lr#define r1 __r1#define eip __eip#define ebp __ebp#define esp __esp#endif/////////////////////////////////////////////////////////////////// A new architecture will require substantial changes to this file.#if ! (TARGET_CPU_PPC || TARGET_CPU_PPC64 || TARGET_CPU_X86 ) #error MoreBacktrace: What architecture?#endif/////////////////////////////////////////////////////////////////#pragma mark ***** Generic Utilitiesstruct OSRelease { int major; int minor; int bug;};typedef struct OSRelease OSRelease;static int GetOSRelease(OSRelease *releasePtr) // Get the Darwin OS release using uname. I can't use gestaltSystemVersion // because it's part of CoreServices, and CoreServices is not available // to 64-bit programs on Mac OS X 10.4.x.{ int err; struct utsname names; int scanResult; assert(releasePtr != NULL); err = uname(&names); if (err < 0) { err = errno; } if (err == 0) { // Parse the three dot separated components of the release string. // If we don't get exactly three, we've confused and we error. scanResult = sscanf(names.release, "%d.%d.%d", &releasePtr->major, &releasePtr->minor, &releasePtr->bug); if (scanResult != 3) { err = EINVAL; } } assert( (err == 0) == (releasePtr->major != 0) ); return err;}/////////////////////////////////////////////////////////////////#pragma mark ***** Architecture Specificationtypedef struct MoreBTContext MoreBTContext; // forward declaration// Architecture Callbacks -- Called by the core to do architecture-specific tasks.typedef int (*MoreBTHandleLeafProc)(MoreBTContext *context, MoreBTAddr *pcPtr, MoreBTAddr *fpPtr); // This callback is called by the core to start a backtrace. // It should extract the first PC and frame from the thread state // in the context and return them to the core. Also, if the // routine detects a frameless leaf routine, it should add a // dummy frame for that routine (by calling AddFrame). // // On entry, context will be a valid context (as determined by ValidateContext). // On entry, pcPtr will not be NULL. // Returns an errno-style error code, // On entry, fpPtr will not be NULL. // On success, *pcPtr must be the PC of the first non-leaf frame. // On success, *fpPtr must be the frame pointer of the first non-leaf frame.typedef bool (*MoreBTValidPCProc)(MoreBTContext *context, MoreBTAddr pc); // This callback is called by the core to check whether a PC address // is valid. This is architecture-specific; for example, on PowerPC a // PC value must be a multiple of 4, whereas an Intel an instruction // can start at any address. // // At a minimum, an implementation is expected to check the PC's alignment // and read the instruction at the PC. // // IMPORTANT: // The core code assumes that (MoreBTAddr) -1 is never a valid PC. // If that isn't true for your architecture, you'll need to eliminate // that assumption from the core. // // On entry, context will be a valid context (as determined by ValidateContext). // On entry, pc can be any value. // Returns true if the PC looks reasonably valid, false otherwise.typedef int (*MoreBTGetFrameNextPCProc)(MoreBTContext *context, MoreBTAddr thisFrame, MoreBTAddr nextFrame, MoreBTAddr *nextPCPtr); // This callback is called by the core to get the PC associated with // the next frame. This is necessary because different architectures // store the PC in different places. Specifically, PowerPC stores // the PC of a frame in that frame, whereas Intel stores the PC of // a frame in the previous frame (that is, the PC value in the frame // is actually a return address). // // On entry, context will be a valid context (as determined by ValidateContext). // On entry, thisFrame will be a valid frame. // On entry, nextFrame will be the valid frame following thisFrame. // On entry, nextPCPtr will not be NULL. // Returns an errno-style error code. // On success, *nextPCPtr must be the PC associated with nextFrame.typedef int (*MoreBTCrossSignalFrameProc)(MoreBTContext *context, MoreBTAddr thisFrame, MoreBTAddr *nextPCPtr, MoreBTAddr *nextFramePtr); // This callback is called by the core when it detects a cross signal // frame and wants to cross that frame in an architecture-specific // manner. The code gets a pointer to the cross-signal handler frame // and is expected to return the PC and frame pointer of the first // non-leaf frame on the other side. Furthermore, it must detect if // the first frame on the other side is a leaf frame and add a // dummy frame for that routine (by calling AddFrame) before returning. // // An implementation does not have to check the validity of the // returned PC and frame. The core will do that for you. // // On entry, context will be a valid context (as determined by ValidateContext). // On entry, thisFrame will be a valid cross-signal handler frame. // On entry, nextPCPtr will not be NULL. // On entry, nextFramePtr will not be NULL. // Returns an errno-style error code. // On success, *nextPCPtr must be the PC of the next non-leaf frame. // On success, *nextFramePtr must be the PC of the next non-leaf frame.// Architecture Specification Structure -- Aggregates all of the information // associated with a particular architecture.struct MoreBTArchInfo { // Information to identify the architecture cpu_type_t cputype; // per NXGetLocalArchInfo in <mach-o/arch.h> cpu_subtype_t cpusubtype; // per NXGetLocalArchInfo in <mach-o/arch.h> bool is64Bit; // Misc information about the architecture enum NXByteOrder byteOrder; // per <architecture/byte_order.h> MoreBTAddr frameAlignMask; // mask to detect frame misalignment // FP & frameAlignMask must be 0 for a valid frame // Architecture-specific backtrace callbacks MoreBTHandleLeafProc handleLeaf; // described in detail above MoreBTValidPCProc validPC; // described in detail above MoreBTGetFrameNextPCProc getFrameNextPC; // described in detail above MoreBTCrossSignalFrameProc crossSignalFrame; // described in detail above // Specification of how to call thread_get_state thread_state_flavor_t stateFlavor; mach_msg_type_number_t stateCount;};typedef struct MoreBTArchInfo MoreBTArchInfo;static bool TaskIs64Bits(task_t task) // Returns true if the specified task if a 64-bit task. // The current implementation works by looking to see // if the task uses any address space above the 4 GB boundary. // This is less than ideal, but it's the best I can think of // right now.{ bool result; assert(task != MACH_PORT_NULL); // If mach_vm_region is NULL, we're running prior to 10.4, where no // task can be 64-bit. result = false;#ifdef HAVE_MACH_VM_REGION if ( mach_vm_region != NULL ) { kern_return_t err; mach_vm_address_t addr; mach_vm_size_t size; vm_region_basic_info_data_64_t info; mach_msg_type_number_t count; mach_port_t junkPort; // Look for a VM region above 4 GB. In practice this generally // picks up the stack. However, at a minimum, this should always // pick up the comm page. If such a region exists, the task must // be 64-bit. addr = 0x0000000100000000LL; count = VM_REGION_BASIC_INFO_COUNT_64; junkPort = MACH_PORT_NULL; err = mach_vm_region( task, &addr, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t) &info, &count, &junkPort ); if (err == KERN_SUCCESS) { assert(addr >= 0x0000000100000000LL); assert(count == VM_REGION_BASIC_INFO_COUNT_64); result = true; } assert(junkPort == MACH_PORT_NULL); }#endif /* HAVE_MACH_VM_REGION */ return result;}static const MoreBTArchInfo kArchitectures[4]; // forward declarationstatic const MoreBTArchInfo * GetTaskArch(task_t task) // Returns a pointer to the architecture associated with the specific // task, or NULL if it's an architecture we don't know about. // // This is one place that trips up cross-architecture backtraces. // For this to work properly, we have to work out the architecture // of the remote task. There's no API to do this. The standard // approach is to find dyld in the remote task and see what // architecture it is (from its Mach image header). // // However, as I've not yet written code to find dyld (what an // ugly kludge that is), I've installed a less than ideal solution, // which is to assume that the remote task is the same architecture // as the local task, modulo 32- vs 64-bit differences. This is // fine for most uses.{ const NXArchInfo * localArch; bool targetIs64Bits; const MoreBTArchInfo * result; assert(task != MACH_PORT_NULL); localArch = NXGetLocalArchInfo(); assert(localArch != NULL); // in debug builds, we want to know if this fails targetIs64Bits = TaskIs64Bits(task); result = NULL; if (localArch != NULL) { const MoreBTArchInfo * thisArch; // Look through the architecture array for an architecture // that matches the local architecture, but with the correct // address space size (as determined by TaskIs64Bits, above). // Also, we prefer architectures with an exact CPU subtype // match, but we'll accept those with a 0 CPU subtype. thisArch = &kArchitectures[0]; while ( (thisArch->cputype != 0) && (result == NULL) ) { if ( (thisArch->cputype == localArch->cputype) && ((thisArch->cpusubtype == 0) || (thisArch->cpusubtype == localArch->cpusubtype)) && (thisArch->is64Bit == targetIs64Bits) ) { result = thisArch; } else { thisArch += 1; } } } return result;}/////////////////////////////////////////////////////////////////#pragma mark ***** Backtrace Core// Memory Read Callbacktypedef int (*MoreBTReadBytesProc)(MoreBTContext *context, MoreBTAddr src, void *dst, size_t 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). // // On entry, context will be a valid context (as determined by ValidateContext). // On entry, src can be any value. // On entry, dst will not be NULL. // On entry, size will be greater than 0. // Returns an errno-style error code. // On success, the routine has copied size bytes of data from the address src // in the remote task to the address dst in the local task. // On error, the value at dst is unspecified. // // Note: // In previous versions of MoreBacktrace, I supported alternative ways to // read bytes from the target. For example, I could use the Carbon // exception handler mechanism <CoreServices/MachineExceptions.h> to read // data from the current task in a safe fashion. This was useful because // it would work on both Mac OS X and traditional Mac OS. Now that I // require Mac OS X and Mach-O, I can just use the Mach routines to do // my reading. However, I've left the abstraction layer in place, // Just In Case (tm).struct MoreBTContext { // Internal parameters that are set up by the caller // of the core backtrace code. const MoreBTArchInfo * arch; task_t task; const void * threadState; // architecture-specific current thread state // for example, on Intel this is i386_thread_state_t MoreBTReadBytesProc readBytes; // described in detail above void * readBytesRefCon; // currently unused // Stuff worked out internally by InitContext. bool swapBytes; // true if target and current task have different byte orders MoreBTAddr sigTrampLowerBound; // address of _sigtramp in target MoreBTAddr sigTrampUpperBound; // Parameters from client. MoreBTAddr stackBottom; MoreBTAddr stackTop; MoreBTFrame * frameArray; // array contents filled out by core size_t frameArrayCount; size_t frameCountOut; // returned by core};static bool ValidateContext(const MoreBTContext *context){ return (context != NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -