diff options
Diffstat (limited to 'emulator/qtools/callstack.h')
| -rw-r--r-- | emulator/qtools/callstack.h | 775 |
1 files changed, 0 insertions, 775 deletions
diff --git a/emulator/qtools/callstack.h b/emulator/qtools/callstack.h deleted file mode 100644 index 8982330..0000000 --- a/emulator/qtools/callstack.h +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright 2006 The Android Open Source Project - -#ifndef CALL_STACK_H -#define CALL_STACK_H - -#include "opcode.h" -#include "armdis.h" - -class CallStackBase { - public: - int getId() { return mId; } - void setId(int id) { mId = id; } - - private: - int mId; -}; - -// Define a template class for the stack frame. The template parameter -// SYM is the symbol_type from the TraceReader<> template class. To -// use the CallStack class, the user derives a subclass of StackFrame -// and defines push() and pop() methods. This derived class is then -// passed as a template parameter to CallStack. -template <class SYM> -class StackFrame { - public: - - virtual ~StackFrame() {}; - - virtual void push(int stackLevel, uint64_t time, CallStackBase *base) {}; - virtual void pop(int stackLevel, uint64_t time, CallStackBase *base) {}; - - typedef SYM symbol_type; - static const uint32_t kCausedException = 0x01; - static const uint32_t kInterpreted = 0x02; - static const uint32_t kStartNative = 0x04; - static const uint32_t kPopBarrier = (kCausedException | kInterpreted - | kStartNative); - - symbol_type *function; // the symbol for the function we entered - uint32_t addr; // return address when this function returns - uint32_t flags; - uint32_t time; // for debugging when a problem occurred - uint32_t global_time; // for debugging when a problem occurred -}; - -template <class FRAME, class BASE = CallStackBase> -class CallStack : public BASE { -public: - typedef FRAME frame_type; - typedef typename FRAME::symbol_type symbol_type; - typedef typename FRAME::symbol_type::region_type region_type; - typedef BASE base_type; - - CallStack(int id, int numFrames, TraceReaderType *trace); - ~CallStack(); - - void updateStack(BBEvent *event, symbol_type *function); - void popAll(uint64_t time); - void threadStart(uint64_t time); - void threadStop(uint64_t time); - - // Set to true if you don't want to see any Java methods ever - void setNativeOnly(bool nativeOnly) { - mNativeOnly = nativeOnly; - } - - int getStackLevel() { return mTop; } - - uint64_t getGlobalTime(uint64_t time) { return time + mSkippedTime; } - void showStack(FILE *stream); - - int mNumFrames; - FRAME *mFrames; - int mTop; // index of the next stack frame to write - -private: - enum Action { NONE, PUSH, POP, NATIVE_PUSH }; - - Action getAction(BBEvent *event, symbol_type *function); - void doMethodAction(BBEvent *event, symbol_type *function); - void doMethodPop(BBEvent *event, uint32_t addr, const uint32_t flags); - void doSimplePush(symbol_type *function, uint32_t addr, - uint64_t time, int flags); - void doSimplePop(uint64_t time); - void doPush(BBEvent *event, symbol_type *function); - void doPop(BBEvent *event, symbol_type *function, Action methodAction); - - TraceReaderType *mTrace; - - // This is a global switch that disables Java methods from appearing - // on the stack. - bool mNativeOnly; - - // This keeps track of whether native frames are currently allowed on the - // stack. - bool mAllowNativeFrames; - - symbol_type mDummyFunction; - region_type mDummyRegion; - - symbol_type *mPrevFunction; - BBEvent mPrevEvent; - - symbol_type *mUserFunction; - BBEvent mUserEvent; // the previous user-mode event - - uint64_t mSkippedTime; - uint64_t mLastRunTime; - - static MethodRec sCurrentMethod; - static MethodRec sNextMethod; -}; - -template<class FRAME, class BASE> -MethodRec CallStack<FRAME, BASE>::sCurrentMethod; -template<class FRAME, class BASE> -MethodRec CallStack<FRAME, BASE>::sNextMethod; - -template<class FRAME, class BASE> -CallStack<FRAME, BASE>::CallStack(int id, int numFrames, TraceReaderType *trace) -{ - mNativeOnly = false; - mTrace = trace; - BASE::setId(id); - mNumFrames = numFrames; - mFrames = new FRAME[mNumFrames]; - mTop = 0; - mAllowNativeFrames = true; - - memset(&mDummyFunction, 0, sizeof(symbol_type)); - memset(&mDummyRegion, 0, sizeof(region_type)); - mDummyFunction.region = &mDummyRegion; - mPrevFunction = &mDummyFunction; - memset(&mPrevEvent, 0, sizeof(BBEvent)); - mUserFunction = &mDummyFunction; - memset(&mUserEvent, 0, sizeof(BBEvent)); - mSkippedTime = 0; - mLastRunTime = 0; - - // Read the first two methods from the trace if we haven't already read - // from the method trace yet. - if (sCurrentMethod.time == 0) { - if (mTrace->ReadMethod(&sCurrentMethod)) { - sCurrentMethod.time = ~0ull; - sNextMethod.time = ~0ull; - } - if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) { - sNextMethod.time = ~0ull; - } - } -} - -template<class FRAME, class BASE> -CallStack<FRAME, BASE>::~CallStack() -{ - delete mFrames; -} - -template<class FRAME, class BASE> -void -CallStack<FRAME, BASE>::updateStack(BBEvent *event, symbol_type *function) -{ - if (mNativeOnly) { - // If this is an interpreted function, then use the native VM function - // instead. - if (function->vm_sym != NULL) - function = function->vm_sym; - } else { - doMethodAction(event, function); - } - - Action action = getAction(event, function); - - // Allow native frames if we are executing in the kernel. - if (!mAllowNativeFrames - && (function->region->flags & region_type::kIsKernelRegion) == 0) { - action = NONE; - } - - if (function->vm_sym != NULL) { - function = function->vm_sym; - function->vm_sym = NULL; - } - if (action == PUSH) { - doPush(event, function); - } else if (action == POP) { - doPop(event, function, NONE); - } - -#if 0 - // Pop off native functions before pushing or popping Java methods. - if (action == POP && mPrevFunction->vm_sym == NULL) { - // Pop off the previous function first. - doPop(event, function, NONE); - if (methodAction == POP) { - doPop(event, function, POP); - } else if (methodAction == PUSH) { - doPush(event, function); - } - } else { - if (methodAction != NONE) { - // If the method trace has a push or pop, then do it. - action = methodAction; - } else if (function->vm_sym != NULL) { - // This function is a Java method. Don't push or pop the - // Java method without a corresponding method trace record. - action = NONE; - } - if (action == POP) { - doPop(event, function, methodAction); - } else if (action == PUSH) { - doPush(event, function); - } - } -#endif - - // If the stack is now empty, then push the current function. - if (mTop == 0) { - uint64_t time = event->time - mSkippedTime; - int flags = 0; - if (function->vm_sym != NULL) { - flags = FRAME::kInterpreted; - } - doSimplePush(function, 0, time, 0); - } - - mPrevFunction = function; - mPrevEvent = *event; -} - -template<class FRAME, class BASE> -void -CallStack<FRAME, BASE>::threadStart(uint64_t time) -{ - mSkippedTime += time - mLastRunTime; -} - -template<class FRAME, class BASE> -void -CallStack<FRAME, BASE>::threadStop(uint64_t time) -{ - mLastRunTime = time; -} - -template<class FRAME, class BASE> -typename CallStack<FRAME, BASE>::Action -CallStack<FRAME, BASE>::getAction(BBEvent *event, symbol_type *function) -{ - Action action; - uint32_t offset; - - // Compute the offset from the start of the function to this basic - // block address. - offset = event->bb_addr - function->addr - function->region->base_addr; - - // Get the previously executed instruction - Opcode op = OP_INVALID; - int numInsns = mPrevEvent.num_insns; - uint32_t insn = 0; - if (numInsns > 0) { - insn = mPrevEvent.insns[numInsns - 1]; - if (mPrevEvent.is_thumb) { - insn = insn_unwrap_thumb(insn); - op = decode_insn_thumb(insn); - } else { - op = Arm::decode(insn); - } - } - - // The number of bytes in the previous basic block depends on - // whether the basic block was ARM or THUMB instructions. - int numBytes; - if (mPrevEvent.is_thumb) { - numBytes = numInsns << 1; - } else { - numBytes = numInsns << 2; - } - - // If this basic block follows the previous one, then return NONE. - // If we don't do this, then we may be fooled into thinking this - // is a POP if the previous block ended with a conditional - // (non-executed) ldmia instruction. We do this check before - // checking if we are in a different function because we otherwise - // we might be fooled into thinking this is a PUSH to a new function - // when it is really just a fall-thru into a local kernel symbol - // that just looks like a new function. - uint32_t prev_end_addr = mPrevEvent.bb_addr + numBytes; - if (prev_end_addr == event->bb_addr) { - return NONE; - } - - // If this basic block is in the same function as the last basic block, - // then just return NONE (but see the exceptions below). - // Exception 1: if the function calls itself (offset == 0) then we - // want to push this function. - // Exception 2: if the function returns to itself, then we want - // to pop this function. We detect this case by checking if the last - // instruction in the previous basic block was a load-multiple (ldm) - // and included r15 as one of the loaded registers. - if (function == mPrevFunction) { - if (numInsns > 0) { - // If this is the beginning of the function and the previous - // instruction was not a branch, then it's a PUSH. - if (offset == 0 && op != OP_B && op != OP_THUMB_B) - return PUSH; - - // If the previous instruction was an ldm that loaded r15, - // then it's a POP. - if (offset != 0 && ((op == OP_LDM && (insn & 0x8000)) - || (op == OP_THUMB_POP && (insn & 0x100)))) { - return POP; - } - } - - return NONE; - } - - // We have to figure out if this new function is a call or a - // return. We don't necessarily have a complete call stack (since - // we could have started tracing at any point), so we have to use - // heuristics. If the address we are jumping to is the beginning - // of a function, or if the instruction that took us there was - // either "bl" or "blx" then this is a PUSH. Also, if the - // function offset is non-zero and the previous instruction is a - // branch instruction, we will call it a PUSH. This happens in - // the kernel a lot when there is a branch to an offset from a - // label. A couple more special cases: - // - // - entering a .plt section ("procedure linkage table") is a PUSH, - // - an exception that jumps into the kernel vector entry point - // is also a push. - // - // If the function offset is non-zero and the previous instruction - // is a bx or some non-branch instruction, then it's a POP. - // - // There's another special case that comes up. The user code - // might execute an instruction that returns but before the pc - // starts executing in the caller, a kernel interrupt occurs. - // But it may be hard to tell if this is a return until after - // the kernel interrupt code is done and returns to user space. - // So we save the last user basic block and look at it when - // we come back into user space. - - const uint32_t kIsKernelRegion = region_type::kIsKernelRegion; - - if (((mPrevFunction->region->flags & kIsKernelRegion) == 0) - && (function->region->flags & kIsKernelRegion)) { - // We just switched into the kernel. Save the previous - // user-mode basic block and function. - mUserEvent = mPrevEvent; - mUserFunction = mPrevFunction; - } else if ((mPrevFunction->region->flags & kIsKernelRegion) - && ((function->region->flags & kIsKernelRegion) == 0)) { - // We just switched from kernel to user mode. - return POP; - } - - action = PUSH; - if (offset != 0 && mPrevFunction != &mDummyFunction) { - // We are jumping into the middle of a function, so this is - // probably a return, not a function call. But look at the - // previous instruction first to see if it was a branch-and-link. - - // If the previous instruction was not a branch (and not a - // branch-and-link) then POP; or if it is a "bx" instruction - // then POP because that is used to return from functions. - if (!isBranch(op) || op == OP_BX || op == OP_THUMB_BX) { - action = POP; - } else if (isBranch(op) && !isBranchLink(op)) { - // If the previous instruction was a normal branch to a - // local symbol then don't count it as a push or a pop. - action = NONE; - } - - if (function->flags & symbol_type::kIsVectorTable) - action = PUSH; - } - return action; -} - - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::doPush(BBEvent *event, symbol_type *function) -{ - uint64_t time = event->time - mSkippedTime; - - // Check for stack overflow - if (mTop >= mNumFrames) { - // Don't show the stack by default because this generates a lot - // of output and this is seen by users if there is an error when - // post-processing the trace. But this is useful for debugging. -#if 0 - showStack(stderr); -#endif - fprintf(stderr, "Error: stack overflow (%d frames)\n", mTop); - exit(1); - } - - // Compute the return address here because we may need to change - // it if we are popping off a frame for a vector table. - int numBytes; - if (mPrevEvent.is_thumb) { - numBytes = mPrevEvent.num_insns << 1; - } else { - numBytes = mPrevEvent.num_insns << 2; - } - uint32_t retAddr = mPrevEvent.bb_addr + numBytes; - - // If this is a Java method then set the return address to zero. - // We won't be using it for popping the method and it may lead - // to false matches when searching the stack. - if (function->vm_sym != NULL) { - retAddr = 0; - } - -#if 0 - // For debugging only. Show the stack before entering the kernel - // exception-handling code. - if (function->flags & symbol_type::kIsVectorStart) { - printf("stack before entering exception\n"); - showStack(stderr); - } -#endif - - // If the top of stack is a vector table, then pop it - // off before pushing on the new function. Also, change the - // return address for the new function to the return address - // from the vector table. - if (mTop > 0 - && (mFrames[mTop - 1].function->flags & symbol_type::kIsVectorTable)) { - retAddr = mFrames[mTop - 1].addr; - doSimplePop(time); - } - - const uint32_t kIsKernelRegion = region_type::kIsKernelRegion; - - // The following code handles the case where one function, F1, - // calls another function, F2, but the before F2 can start - // executing, it takes a page fault (on the first instruction - // in F2). The kernel is entered, handles the page fault, and - // then returns to the called function. The problem is that - // this looks like a new function call to F2 from the kernel. - // The following code cleans up the stack by popping the - // kernel frames back to F1 (but not including F1). The - // return address for F2 also has to be fixed up to point to - // F1 instead of the kernel. - // - // We detect this case by checking if the previous basic block - // was in the kernel and the current basic block is not. - if ((mPrevFunction->region->flags & kIsKernelRegion) - && ((function->region->flags & kIsKernelRegion) == 0) - && mTop > 0) { - // We are switching from kernel mode to user mode. -#if 0 - // For debugging. - printf(" doPush(): popping to user mode, bb_addr: 0x%08x\n", - event->bb_addr); - showStack(stderr); -#endif - do { - // Pop off the kernel frames until we reach the one that - // caused the exception. - doSimplePop(time); - - // If the next stack frame is the one that caused an - // exception then stop popping frames. - if (mTop > 0 - && (mFrames[mTop - 1].flags & FRAME::kCausedException)) { - mFrames[mTop - 1].flags &= ~FRAME::kCausedException; - retAddr = mFrames[mTop].addr; - break; - } - } while (mTop > 0); -#if 0 - // For debugging - printf(" doPush() popping to level %d, using retAddr 0x%08x\n", - mTop, retAddr); -#endif - } - - // If we are starting an exception handler, then mark the previous - // stack frame so that we know where to return when the exception - // handler finishes. - if ((function->flags & symbol_type::kIsVectorStart) && mTop > 0) - mFrames[mTop - 1].flags |= FRAME::kCausedException; - - // If the function being pushed is a Java method, then mark it on - // the stack so that we don't pop it off until we get a matching - // trace record from the method trace file. - int flags = 0; - if (function->vm_sym != NULL) { - flags = FRAME::kInterpreted; - } - doSimplePush(function, retAddr, time, flags); -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::doSimplePush(symbol_type *function, uint32_t addr, - uint64_t time, int flags) -{ - // Check for stack overflow - if (mTop >= mNumFrames) { - showStack(stderr); - fprintf(stderr, "too many stack frames (%d)\n", mTop); - exit(1); - } - - mFrames[mTop].addr = addr; - mFrames[mTop].function = function; - mFrames[mTop].flags = flags; - mFrames[mTop].time = time; - mFrames[mTop].global_time = time + mSkippedTime; - - mFrames[mTop].push(mTop, time, this); - mTop += 1; -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::doSimplePop(uint64_t time) -{ - if (mTop <= 0) { - return; - } - - mTop -= 1; - mFrames[mTop].pop(mTop, time, this); - - if (mNativeOnly) - return; - - // If the stack is empty, then allow more native frames. - // Otherwise, if we are transitioning from Java to native, then allow - // more native frames. - // Otherwise, if we are transitioning from native to Java, then disallow - // more native frames. - if (mTop == 0) { - mAllowNativeFrames = true; - } else { - bool newerIsJava = (mFrames[mTop].flags & FRAME::kInterpreted) != 0; - bool olderIsJava = (mFrames[mTop - 1].flags & FRAME::kInterpreted) != 0; - if (newerIsJava && !olderIsJava) { - // We are transitioning from Java to native - mAllowNativeFrames = true; - } else if (!newerIsJava && olderIsJava) { - // We are transitioning from native to Java - mAllowNativeFrames = false; - } - } -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::doPop(BBEvent *event, symbol_type *function, - Action methodAction) -{ - uint64_t time = event->time - mSkippedTime; - - // Search backward on the stack for a matching return address. - // The most common case is that we pop one stack frame, but - // sometimes we pop more than one. - int stackLevel; - bool allowMethodPop = (methodAction == POP); - for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) { - if (event->bb_addr == mFrames[stackLevel].addr) { - // We found a matching return address on the stack. - break; - } - - // If this stack frame caused an exception, then do not pop - // this stack frame. - if (mFrames[stackLevel].flags & FRAME::kPopBarrier) { - // If this is a Java method, then allow a pop only if we - // have a matching trace record. - if (mFrames[stackLevel].flags & FRAME::kInterpreted) { - if (allowMethodPop) { - // Allow at most one method pop - allowMethodPop = false; - continue; - } - } - stackLevel += 1; - break; - } - } - - // If we didn't find a matching return address then search the stack - // again for a matching function. - if (stackLevel < 0 || event->bb_addr != mFrames[stackLevel].addr) { - bool allowMethodPop = (methodAction == POP); - for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) { - // Compare the function with the one in the stack frame. - if (function == mFrames[stackLevel].function) { - // We found a matching function. We want to pop up to but not - // including this frame. But allow popping this frame if this - // method called itself and we have a method pop. - if (allowMethodPop && function == mPrevFunction) { - // pop this frame - break; - } - // do not pop this frame - stackLevel += 1; - break; - } - - // If this stack frame caused an exception, then do not pop - // this stack frame. - if (mFrames[stackLevel].flags & FRAME::kPopBarrier) { - // If this is a Java method, then allow a pop only if we - // have a matching trace record. - if (mFrames[stackLevel].flags & FRAME::kInterpreted) { - if (allowMethodPop) { - // Allow at most one method pop - allowMethodPop = false; - continue; - } - } - stackLevel += 1; - break; - } - } - if (stackLevel < 0) - stackLevel = 0; - } - - // Note that if we didn't find a matching stack frame, we will pop - // the whole stack (unless there is a Java method or exception - // frame on the stack). This is intentional because we may have - // started the trace in the middle of an executing program that is - // returning up the stack and we do not know the whole stack. So - // the right thing to do is to empty the stack. - - // If we are emptying the stack, then add the current function - // on top. If the current function is the same as the top of - // stack, then avoid an extraneous pop and push. - if (stackLevel == 0 && mFrames[0].function == function) - stackLevel = 1; - -#if 0 - // If we are popping off a large number of stack frames, then - // we might have a bug. - if (mTop - stackLevel > 7) { - printf("popping thru level %d\n", stackLevel); - showStack(stderr); - } -#endif - - // Pop the stack frames - for (int ii = mTop - 1; ii >= stackLevel; --ii) - doSimplePop(time); - - // Clear the "caused exception" bit on the current stack frame - if (mTop > 0) { - mFrames[mTop - 1].flags &= ~FRAME::kCausedException; - } - - // Also handle the case where F1 calls F2 and F2 returns to - // F1, but before we can execute any instructions in F1, we - // switch to the kernel. Then when we return from the kernel - // we want to pop off F2 from the stack instead of pushing F1 - // on top of F2. To handle this case, we saved the last - // user-mode basic block when we entered the kernel (in - // the getAction() function) and now we can check to see if - // that was a return to F1 instead of a call. We use the - // getAction() function to determine this. - const uint32_t kIsKernelRegion = region_type::kIsKernelRegion; - if ((mPrevFunction->region->flags & kIsKernelRegion) - && ((function->region->flags & kIsKernelRegion) == 0)) { - mPrevEvent = mUserEvent; - mPrevFunction = mUserFunction; - if (getAction(event, function) == POP) { - // We may need to pop more than one frame, so just - // call doPop() again. This won't be an infinite loop - // here because we changed mPrevEvent to the last - // user-mode event. - doPop(event, function, methodAction); - return; - } - } -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::popAll(uint64_t time) -{ - time -= mSkippedTime; - while (mTop != 0) { - doSimplePop(time); - } -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::doMethodPop(BBEvent *event, uint32_t addr, - const uint32_t flags) -{ - uint64_t time = event->time - mSkippedTime; - - // Search the stack from the top down for a frame that contains a - // matching method. - int stackLevel; - for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) { - if (mFrames[stackLevel].flags & flags) { - // If we are searching for a native method, then don't bother trying - // to match the address. - if (flags == FRAME::kStartNative) - break; - symbol_type *func = mFrames[stackLevel].function; - uint32_t methodAddr = func->region->base_addr + func->addr; - if (methodAddr == addr) { - break; - } - } - } - - // If we found a matching frame then pop the stack up to and including - // that frame. - if (stackLevel >= 0) { - // Pop the stack frames - for (int ii = mTop - 1; ii >= stackLevel; --ii) - doSimplePop(time); - } -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::doMethodAction(BBEvent *event, symbol_type *function) -{ - // If the events get ahead of the method trace, then read ahead until we - // sync up again. This can happen if there is a pop of a method in the - // method trace for which we don't have a previous push. Such an unmatched - // pop can happen because the user can start tracing at any time and so - // there might already be a stack when we start tracing. - while (event->time >= sNextMethod.time) { - sCurrentMethod = sNextMethod; - if (mTrace->ReadMethod(&sNextMethod)) { - sNextMethod.time = ~0ull; - } - } - - if (event->time >= sCurrentMethod.time && event->pid == sCurrentMethod.pid) { - uint64_t time = event->time - mSkippedTime; - int flags = sCurrentMethod.flags; - if (flags == kMethodEnter) { - doSimplePush(function, 0, time, FRAME::kInterpreted); - mAllowNativeFrames = false; - } else if (flags == kNativeEnter) { - doSimplePush(function, 0, time, FRAME::kStartNative); - mAllowNativeFrames = true; - } else if (flags == kMethodExit || flags == kMethodException) { - doMethodPop(event, sCurrentMethod.addr, FRAME::kInterpreted); - } else if (flags == kNativeExit || flags == kNativeException) { - doMethodPop(event, sCurrentMethod.addr, FRAME::kStartNative); - } - - // We found a match, so read the next record. When we get to the end - // of the trace, we set the time to the maximum value (~0). - sCurrentMethod = sNextMethod; - if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) { - sNextMethod.time = ~0ull; - } - } -} - -template<class FRAME, class BASE> -void CallStack<FRAME, BASE>::showStack(FILE *stream) -{ - fprintf(stream, "mTop: %d skippedTime: %llu\n", mTop, mSkippedTime); - for (int ii = 0; ii < mTop; ++ii) { - uint32_t addr = mFrames[ii].function->addr; - addr += mFrames[ii].function->region->vstart; - fprintf(stream, " %d: t %d gt %d f %x 0x%08x 0x%08x %s\n", - ii, mFrames[ii].time, mFrames[ii].global_time, - mFrames[ii].flags, - mFrames[ii].addr, addr, - mFrames[ii].function->name); - } -} - -#endif /* CALL_STACK_H */ |
