📄 morebacktrace.c
字号:
&& (context->arch != NULL) && (context->task != MACH_PORT_NULL) && (context->readBytes != NULL) && (context->sigTrampLowerBound != 0) && (context->sigTrampLowerBound < context->sigTrampUpperBound) && (context->stackBottom <= context->stackTop) && ((context->frameArrayCount == 0) || (context->frameArray != NULL)) && (context->threadState != NULL);}static int ReadAddr(MoreBTContext *context, MoreBTAddr addr, MoreBTAddr *valuePtr) // Reads an address (that is, a pointer) from the target task, // returning an error if the memory is unmapped. // // On entry, context will be a valid context (as determined by ValidateContext). // On entry, addr can be any value. // On entry, valuePtr must not be NULL. // Returns an errno-style error code. // On success, *valuePtr will be the value of the pointer stored at addr in // the target task.{ int err; MoreBTAddr value; assert(ValidateContext(context)); assert(valuePtr != NULL); if (context->arch->is64Bit) { // Read directly into value, and then swap all 8 bytes. err = context->readBytes(context, addr, &value, sizeof(value)); if (err == 0) { if (context->swapBytes) { value = OSSwapInt64(value); } } } else { uint32_t tmpAddr; // Read into a temporary address, swap 4 bytes, then copy that // into value. tmpAddr is unsigned, so we zero fill the top // 32 bits. err = context->readBytes(context, addr, &tmpAddr, sizeof(tmpAddr)); if (err == 0) { if (context->swapBytes) { tmpAddr = OSSwapInt32(tmpAddr); } value = tmpAddr; } } if (err == 0) { *valuePtr = value; } return err;}static void AddFrame(MoreBTContext *context, MoreBTAddr pc, MoreBTAddr fp, MoreBTFlags flags) // Adds a frame to the end of the output array with the // value specified by pc, fp, and flags. // // On entry, context will be a valid context (as determined by ValidateContext).{ // Only actually output the frame if the client supplied an array // and we we haven't filled it up yet. assert(ValidateContext(context)); if ( (context->frameArray != NULL) && (context->frameCountOut < context->frameArrayCount) ) { MoreBTFrame * frameOutPtr; frameOutPtr = &context->frameArray[context->frameCountOut]; frameOutPtr->pc = pc; frameOutPtr->fp = fp; frameOutPtr->flags = flags; } // Always increment the frame count. context->frameCountOut += 1; }static int InitSigTrampAddress(MoreBTContext *context) // Fills out the sigTrampLowerBound and sigTrampUpperBound fields of // the context to be the address and bound of the _sigtramp routine. // I need this information to be able to detect and cross signal // frames correctly. // // Doing this correctly requires the ability to map symbols to addresses // in the remote task, something for which I haven't yet written the code. // So, we just make some risky assumptions: // // o _sigtramp is at the same address in the target task as it is in // our task. This would fail if the target task was a different // architecture, so we outlaw that. It can also fail if System // frameworks was relocated in the target task, something that's // possible and, with my current code base, hard to detect. // // o I assume a fixed length of 0x100 for the _sigtramp code. Needless to // say, this is completely bogus. // // On entry, context isn't yet a fully valid context because the // sigtramp fields haven't been set up yet. // Returns an errno-style error code. // On success, context is a valid context.{ int err; extern void _sigtramp(void); assert(context != NULL); if ( context->arch != GetTaskArch(mach_task_self()) ) { fprintf(stderr, "MoreBacktrace: Cross architecture backtrace not supported because of problems finding _sigtramp.\n"); err = ENOTSUP; } else { context->sigTrampLowerBound = (uintptr_t) &_sigtramp; context->sigTrampUpperBound = context->sigTrampLowerBound + 0x100; err = 0; } assert( (err != 0) || ValidateContext(context) ); return err;}static OSRelease gOSRelease;static int InitContext( MoreBTContext * context, const MoreBTArchInfo * arch, task_t task, const void * threadState, MoreBTReadBytesProc readBytes, void * readBytesRefCon, MoreBTAddr stackBottom, MoreBTAddr stackTop, MoreBTFrame * frameArray, size_t frameArrayCount) // Initialises a MoreBTContext to appropriate values based on // the input parameters and various default values and values // derived from the input parameters. // // On entry, context must not be NULL (but it's not yet a valid context). // On entry, arch must not be NULL. // On entry, task must not be MACH_PORT_NULL. // On entry, threadState must not be NULL. // On entry, readBytes must not be NULL. // Returns an errno-style error code. // On success, context will be a valid context.{ int err; assert(context != NULL); assert(arch != NULL); assert(task != MACH_PORT_NULL); assert(threadState != NULL); assert(readBytes != NULL); memset(context, 0, sizeof(*context)); // We don't check these input parameters here. Instead the // check is done by the ValidateContext call below. context->stackBottom = stackBottom; context->stackTop = stackTop; context->frameArray = frameArray; context->frameArrayCount = frameArrayCount; context->arch = arch; context->task = task; context->threadState = threadState; context->readBytes = readBytes; context->readBytesRefCon = readBytesRefCon; context->swapBytes = (arch->byteOrder != NXGetLocalArchInfo()->byteorder); err = 0; if (gOSRelease.major == 0) { err = GetOSRelease(&gOSRelease); } if (err == 0) { err = InitSigTrampAddress(context); } assert( (err != 0) || ValidateContext(context) ); return err;}static int BacktraceCore(MoreBTContext *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; MoreBTAddr thisPC; MoreBTAddr thisFrame; MoreBTAddr lowerBound; MoreBTAddr upperBound; bool stopNow; assert(ValidateContext(context)); lowerBound = context->stackBottom; upperBound = context->stackTop; if (upperBound == 0) { if (context->arch->is64Bit) { // This actually generates a theoretical off-by-one error (a fp of // 0xFFFFFFFF FFFFFFFF is falsely considered invalid), but that's // not a problem in practice. upperBound = 0xFFFFFFFFFFFFFFFFLL; } else { upperBound = 0x0000000100000000LL; } } // If you supply bounds, they must make sense. assert(upperBound >= lowerBound); // Handle any leaf frames, and also return to us the initial // PC and FP. assert(context->frameCountOut == 0); // set up by memset in InitContext err = context->arch->handleLeaf(context, &thisPC, &thisFrame); // Handle the normal frames. if (err == 0) { stopNow = false; do { MoreBTFrame * frameOutPtr; MoreBTFrame tmpFrameOut; MoreBTAddr nextFrame; MoreBTAddr nextPC; // Output to a tmpFrameOut unless the client has supplied // a buffer and there's sufficient space left in it. // // IMPORTANT: // You can't just add the frame information (possibly by calling // AddFrame) at the end of this loop, because the crossSignalFrame // callback may add its own frame, and we have to make sure that // this frame is allocated before that one. 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->fp = 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 ( ! context->arch->validPC(context, thisPC) ) { frameOutPtr->flags |= kMoreBTPCBadMask; } else { // On PowerPC I used to report the address of the call, // rather than the return address. That was easy: I just // decremented the returned PC by 4. However, this is // much harder on Intel, where instructions are of variable // length. So, I decided to do what Apple's tools do, // and just report the return address. } // Check the validity of the frame pointer. A bad frame pointer *does* // cause us to stop tracing. if ( (thisFrame == 0) || (thisFrame & context->arch->frameAlignMask) || (thisFrame < lowerBound) || (thisFrame >= upperBound) ) { frameOutPtr->flags |= kMoreBTFrameBadMask; stopNow = true; } if ( (err == 0) && ! stopNow) { // Move to the next frame, either by crossing a signal handler frame // or by the usual mechanism. if ( !(frameOutPtr->flags & kMoreBTPCBadMask) && ( thisPC >= context->sigTrampLowerBound ) && ( thisPC < context->sigTrampUpperBound ) ) { // If this frame is running in _sigtramp, get nextPC and nextFrame // by delving into the signal handler stack block. frameOutPtr->flags |= kMoreBTSignalHandlerMask; err = context->arch->crossSignalFrame(context, thisFrame, &nextPC, &nextFrame); } else { // Read the next frame pointer. A failure here causes us to quit // backtracing. Note that we set kMoreBTFrameBadMask in frameOutPtr // because, if we can't read the contents of the frame pointer, the // frame pointer itself must be bad. err = ReadAddr(context, thisFrame, &nextFrame); if (err != 0) { frameOutPtr->flags |= kMoreBTFrameBadMask; nextFrame = (MoreBTAddr) -1; // No need to set stopNow because err != 0 will // terminate loop. } // Also get the PC of the next frame, or set it to dummy value if // there is no next frame or we can't get the PC from that frame. if ( (frameOutPtr->flags & kMoreBTFrameBadMask) || (context->arch->getFrameNextPC(context, thisFrame, nextFrame, &nextPC) != 0) ) { nextPC = (MoreBTAddr) -1; // an odd value, to trigger above check on next iteration } } // Set up for the next iteration. if (err == 0) { lowerBound = thisFrame; thisPC = nextPC; thisFrame = nextFrame; } } } while ( (err == 0) && ! stopNow ); } assert(ValidateContext(context)); return err;}#pragma mark ***** Mach Infrastructurestatic int MachReadBytes(MoreBTContext *context, MoreBTAddr src, void *dst, size_t size) // A memory read callback for Mach. This simply calls through // to the Mach [mach_]vm_read primitive, which does more-or-less // what we want. // // See the description of MoreBTReadBytesProc for information about // the parameters.{ int err; int junk; vm_offset_t dataRead; mach_msg_type_number_t sizeRead; assert(ValidateContext(context)); assert(dst != NULL); assert(size > 0); // I used to use mach_vm_read_overwrite, which has a better semantic match for // what I'm trying to do than mach_vm_read, but it has some serious problems // on some systems (at least Mac OS X 10.4.4 on PowerPC G4 and G5). So I've // reverting to using [mach_]vm_read, which means I have to vm_deallocate // the space afterwards. Such is life, kerplunk. #ifdef HAVE_MACH_VM_READ if (mach_vm_read != NULL) { err = mach_vm_read( context->task, src, size, &dataRead, &sizeRead ); } else #endif /* HAVE_MACH_VM_READ */ { #if MORE_DEBUG // If I'm running 32-bits, vm_read's arguments are only 32-bits, // and thus an attempt to read a 64-bit address is bad. This // should never happen because systems that support 64-bit // addresses also support mach_vm_read. But if it does happen, // I want to know about it (and investigate what's going on). if ( ! TaskIs64Bits(mach_task_self()) ) { assert( (src & 0xFFFFFFFF00000000LL) == 0 ); assert( ((src + size) & 0xFFFFFFFF00000000LL) == 0 ); } #endif err = vm_read( context->task, (vm_address_t) src, size, &dataRead, &sizeRead ); } if (err == 0) { if (sizeRead != size) { err = KERN_FAILURE; } else { memcpy(dst, (void *) dataRead, size); } // Note that I can use vm_deallocate instead of mach_vm_deallocate because // I know that the thing I'm deallocating is in the my address space, and // thus vm_deallocate, whose parameters scale with the caller's address space, // is just fine. mach_vm_deallocate would work just as well, but that would // put another unnecessary dependency on Mac OS X 10.4. junk = vm_deallocate(mach_task_self(), dataRead, sizeRead); assert(junk == 0); } return err;}static int MoreBacktraceMach( const MoreBTArchInfo * arch, task_t task, const void * threadState, MoreBTAddr stackBottom, MoreBTAddr stackTop, MoreBTFrame * frameArray, size_t frameArrayCount, size_t * frameCountPtr) // Common code for the two exported backtrace routines. // Backtraces a given task, of a given architecture, starting // with the specified thread state. The other parameters // are directly from the client. // // Returns an errno-style error code.{ int err; MoreBTContext context;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -