Exemplo n.º 1
0
/*
 * Pop one frame pushed on by JNI PushLocalFrame.
 *
 * If we've gone too far, the previous frame is either a break frame or
 * an interpreted frame.  Either way, the method pointer won't match.
 */
bool dvmPopLocalFrame(Thread* self)
{
    StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame);

    assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
    if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) {
        /*
         * The previous frame doesn't have the same method pointer -- we've
         * been asked to pop too much.
         */
        assert(dvmIsBreakFrame((u4*)saveBlock->prevFrame) ||
               !dvmIsNativeMethod(
                       SAVEAREA_FROM_FP(saveBlock->prevFrame)->method));
        return false;
    }

    LOGVV("POP JNI local frame: removing %s, now %s",
        saveBlock->method->name,
        SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name);
    dvmPopJniLocals(self, saveBlock);
    self->interpSave.curFrame = saveBlock->prevFrame;
#ifdef WITH_OFFLOAD
    offStackFramePopped(self);
#endif

    return true;
}
Exemplo n.º 2
0
/*
 * Get the calling frame.  Pass in the current fp.
 *
 * Skip "break" frames and reflection invoke frames.
 */
void* dvmGetCallerFP(const void* curFrame)
{
    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
    StackSaveArea* saveArea;

retry:
    if (dvmIsBreakFrame((u4*)caller)) {
        /* pop up one more */
        caller = SAVEAREA_FROM_FP(caller)->prevFrame;
        if (caller == NULL)
            return NULL;        /* hit the top */

        /*
         * If we got here by java.lang.reflect.Method.invoke(), we don't
         * want to return Method's class loader.  Shift up one and try
         * again.
         */
        saveArea = SAVEAREA_FROM_FP(caller);
        if (dvmIsReflectionMethod(saveArea->method)) {
            caller = saveArea->prevFrame;
            assert(caller != NULL);
            goto retry;
        }
    }

    return caller;
}
Exemplo n.º 3
0
/*
 * Compute the frame depth.
 *
 * Excludes "break" frames.
 */
int dvmComputeExactFrameDepth(const void* fp)
{
    int count = 0;

    for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
        if (!dvmIsBreakFrame((u4*)fp))
            count++;
    }

    return count;
}
Exemplo n.º 4
0
/*
 * Fill a flat array of methods that comprise the current interpreter
 * stack trace.  Pass in the current frame ptr.  Break frames are
 * skipped, but reflection invocations are not.
 *
 * The current frame will be in element 0.
 */
void dvmFillStackTraceArray(const void* fp, const Method** array, size_t length)
{
    assert(fp != NULL);
    assert(array != NULL);
    size_t i = 0;
    while (fp != NULL) {
        if (!dvmIsBreakFrame((u4*)fp)) {
            assert(i < length);
            array[i++] = SAVEAREA_FROM_FP(fp)->method;
        }
        fp = SAVEAREA_FROM_FP(fp)->prevFrame;
    }
}
Exemplo n.º 5
0
/*
 * Get the caller's caller's class.  Pass in the current fp.
 *
 * This is used by e.g. java.lang.Class, which wants to know about the
 * class loader of the method that called it.
 */
ClassObject* dvmGetCaller2Class(const void* curFrame)
{
    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
    void* callerCaller;

    /* at the top? */
    if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
        return NULL;

    /* go one more */
    callerCaller = dvmGetCallerFP(caller);
    if (callerCaller == NULL)
        return NULL;

    return SAVEAREA_FROM_FP(callerCaller)->method->clazz;
}
Exemplo n.º 6
0
/*
 * Pop a frame we added.  There should be one method frame and one break
 * frame.
 *
 * If JNI Push/PopLocalFrame calls were mismatched, we might end up
 * popping multiple method frames before we find the break.
 *
 * Returns "false" if there was no frame to pop.
 */
static bool dvmPopFrame(Thread* self)
{
    StackSaveArea* saveBlock;

    if (self->interpSave.curFrame == NULL)
        return false;

    saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame);
    assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));

    /*
     * Remove everything up to the break frame.  If this was a call into
     * native code, pop the JNI local references table.
     */
    while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) {
        /* probably a native->native JNI call */

        if (dvmIsNativeMethod(saveBlock->method)) {
            LOGVV("Popping JNI stack frame for %s.%s%s",
                saveBlock->method->clazz->descriptor,
                saveBlock->method->name,
                (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
                "" : " (JNI local)");
            dvmPopJniLocals(self, saveBlock);
        }

        saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame);
    }
    if (saveBlock->method != NULL) {
        ALOGE("PopFrame missed the break");
        assert(false);
        dvmAbort();     // stack trashed -- nowhere to go in this thread
    }

    LOGVV("POP frame: cur=%p new=%p",
        self->interpSave.curFrame, saveBlock->prevFrame);

    self->interpSave.curFrame = saveBlock->prevFrame;

#ifdef WITH_OFFLOAD
    offStackFramePopped(self);
    self->breakFrames--;
    CHECK_BREAK_FRAMES();
#endif
    return true;
}
Exemplo n.º 7
0
/*
 * Get the caller's caller's caller's class.  Pass in the current fp.
 *
 * This is used by e.g. java.lang.Class, which wants to know about the
 * class loader of the method that called it.
 */
ClassObject* dvmGetCaller3Class(const void* curFrame)
{
    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
    int i;

    /* at the top? */
    if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
        return NULL;

    /* Walk up two frames if possible. */
    for (i = 0; i < 2; i++) {
        caller = dvmGetCallerFP(caller);
        if (caller == NULL)
            return NULL;
    }
    
    return SAVEAREA_FROM_FP(caller)->method->clazz;
}
Exemplo n.º 8
0
/*
 * Pop a frame we added.  There should be one method frame and one break
 * frame.
 *
 * If JNI Push/PopLocalFrame calls were mismatched, we might end up
 * popping multiple method frames before we find the break.
 *
 * Returns "false" if there was no frame to pop.
 */
static bool dvmPopFrame(Thread* self)
{
    StackSaveArea* saveBlock;

    if (self->curFrame == NULL)
        return false;

    saveBlock = SAVEAREA_FROM_FP(self->curFrame);
    assert(!dvmIsBreakFrame(self->curFrame));

    /*
     * Remove everything up to the break frame.  If this was a call into
     * native code, pop the JNI local references table.
     */
    while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) {
        /* probably a native->native JNI call */

        if (dvmIsNativeMethod(saveBlock->method)) {
            LOGVV("Popping JNI stack frame for %s.%s%s\n",
                saveBlock->method->clazz->descriptor,
                saveBlock->method->name,
                (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
                "" : " (JNI local)");
            assert(saveBlock->xtra.localRefCookie != 0);
            //assert(saveBlock->xtra.localRefCookie >= self->jniLocalRefTable.table &&
            //    saveBlock->xtra.localRefCookie <=self->jniLocalRefTable.nextEntry);

            dvmPopJniLocals(self, saveBlock);
        }

        saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame);
    }
    if (saveBlock->method != NULL) {
        LOGE("PopFrame missed the break\n");
        assert(false);
        dvmAbort();     // stack trashed -- nowhere to go in this thread
    }

    LOGVV("POP frame: cur=%p new=%p\n",
        self->curFrame, saveBlock->prevFrame);

    self->curFrame = saveBlock->prevFrame;
    return true;
}
Exemplo n.º 9
0
/**
 * @brief 遍历dalvik栈
 * @param thread 线程结构指针
 * @param print 是否打印
 */
static void crawlDalvikStack(Thread *thread, bool print)
{
    void *fp = thread->interpSave.curFrame;			/* 获取栈指针 */
    StackSaveArea* saveArea = NULL;
    int stackLevel = 0;

    if (print) {
        ALOGD("Crawling tid %d (%s / %p %s)", thread->systemTid,
             dvmGetThreadStatusStr(thread->status),
             thread->inJitCodeCache,
             thread->inJitCodeCache ? "jit" : "interp");
    }
    /* Crawl the Dalvik stack frames to clear the returnAddr field */
	/* 遍历清除返回地址字段 */
    while (fp != NULL) {
        saveArea = SAVEAREA_FROM_FP(fp);	/* 取出一个单元 */

        if (print) {
            if (dvmIsBreakFrame((u4*)fp)) {
                ALOGD("  #%d: break frame (%p)",
                     stackLevel, saveArea->returnAddr);
            }
            else {
                ALOGD("  #%d: %s.%s%s (%p)",
                     stackLevel,
                     saveArea->method->clazz->descriptor,
                     saveArea->method->name,
                     dvmIsNativeMethod(saveArea->method) ?
                         " (native)" : "",
                     saveArea->returnAddr);
            }
        }
        stackLevel++;
        saveArea->returnAddr = NULL;			/* 设置返回值为NULL */
        assert(fp != saveArea->prevFrame);
        fp = saveArea->prevFrame;
    }
    /* Make sure the stack is fully unwound to the bottom */
    assert(saveArea == NULL ||
           (u1 *) (saveArea+1) == thread->interpStackStart);
}
Exemplo n.º 10
0
/*
 * Create a flat array of methods that comprise the current interpreter
 * stack trace.  Pass in the current frame ptr.
 *
 * Allocates a new array and fills it with method pointers.  Break frames
 * are skipped, but reflection invocations are not.  The caller must free
 * "*pArray".
 *
 * The current frame will be in element 0.
 *
 * Returns "true" on success, "false" on failure (e.g. malloc failed).
 */
bool dvmCreateStackTraceArray(const void* fp, const Method*** pArray,
    int* pLength)
{
    const Method** array;
    int idx, depth;

    depth = dvmComputeExactFrameDepth(fp);
    array = (const Method**) malloc(depth * sizeof(Method*));
    if (array == NULL)
        return false;

    for (idx = 0; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
        if (!dvmIsBreakFrame(fp))
            array[idx++] = SAVEAREA_FROM_FP(fp)->method;
    }
    assert(idx == depth);

    *pArray = array;
    *pLength = depth;
    return true;
}
Exemplo n.º 11
0
/*
 * Dump stack frames, starting from the specified frame and moving down.
 *
 * Each frame holds a pointer to the currently executing method, and the
 * saved program counter from the caller ("previous" frame).  This means
 * we don't have the PC for the current method on the stack, which is
 * pretty reasonable since it's in the "PC register" for the VM.  Because
 * exceptions need to show the correct line number we actually *do* have
 * an updated version in the fame's "xtra.currentPc", but it's unreliable.
 *
 * Note "framePtr" could be NULL in rare circumstances.
 */
static void dumpFrames(const DebugOutputTarget* target, void* framePtr,
    Thread* thread)
{
    const StackSaveArea* saveArea;
    const Method* method;
    int checkCount = 0;
    const u2* currentPc = NULL;
    bool first = true;

    /*
     * We call functions that require us to be holding the thread list lock.
     * It's probable that the caller has already done so, but it's not
     * guaranteed.  If it's not locked, lock it now.
     */
    bool needThreadUnlock = dvmTryLockThreadList();

    /*
     * The "currentPc" is updated whenever we execute an instruction that
     * might throw an exception.  Show it here.
     */
    if (framePtr != NULL && !dvmIsBreakFrame((u4*)framePtr)) {
        saveArea = SAVEAREA_FROM_FP(framePtr);

        if (saveArea->xtra.currentPc != NULL)
            currentPc = saveArea->xtra.currentPc;
    }

    while (framePtr != NULL) {
        saveArea = SAVEAREA_FROM_FP(framePtr);
        method = saveArea->method;

        if (dvmIsBreakFrame((u4*)framePtr)) {
            //dvmPrintDebugMessage(target, "  (break frame)\n");
        } else {
            int relPc;

            if (currentPc != NULL)
                relPc = currentPc - saveArea->method->insns;
            else
                relPc = -1;

            std::string methodName(dvmHumanReadableMethod(method, false));
            if (dvmIsNativeMethod(method)) {
                dvmPrintDebugMessage(target, "  at %s(Native Method)\n",
                        methodName.c_str());
            } else {
                dvmPrintDebugMessage(target, "  at %s(%s:%s%d)\n",
                        methodName.c_str(), dvmGetMethodSourceFile(method),
                        (relPc >= 0 && first) ? "~" : "",
                        relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
            }

            if (first) {
                /*
                 * Decorate WAIT and MONITOR threads with some detail on
                 * the first frame.
                 *
                 * warning: wait status not stable, even in suspend
                 */
                if (thread->status == THREAD_WAIT ||
                    thread->status == THREAD_TIMED_WAIT)
                {
                    Monitor* mon = thread->waitMonitor;
                    Object* obj = dvmGetMonitorObject(mon);
                    if (obj != NULL) {
                        Thread* joinThread = NULL;
                        if (obj->clazz == gDvm.classJavaLangVMThread) {
                            joinThread = dvmGetThreadFromThreadObject(obj);
                        }
                        if (joinThread == NULL) {
                            joinThread = dvmGetObjectLockHolder(obj);
                        }
                        printWaitMessage(target, "on", obj, joinThread);
                    }
                } else if (thread->status == THREAD_MONITOR) {
                    Object* obj;
                    Thread* owner;
                    if (extractMonitorEnterObject(thread, &obj, &owner)) {
                        printWaitMessage(target, "to lock", obj, owner);
                    }
                }
            }
        }

        /*
         * Get saved PC for previous frame.  There's no savedPc in a "break"
         * frame, because that represents native or interpreted code
         * invoked by the VM.  The saved PC is sitting in the "PC register",
         * a local variable on the native stack.
         */
        currentPc = saveArea->savedPc;

        first = false;

        if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) {
            ALOGW("Warning: loop in stack trace at frame %d (%p -> %p)",
                checkCount, framePtr, saveArea->prevFrame);
            break;
        }
        framePtr = saveArea->prevFrame;

        checkCount++;
        if (checkCount > 300) {
            dvmPrintDebugMessage(target,
                "  ***** printed %d frames, not showing any more\n",
                checkCount);
            break;
        }
    }

    if (needThreadUnlock) {
        dvmUnlockThreadList();
    }
}
Exemplo n.º 12
0
/*
 * Extract the object that is the target of a monitor-enter instruction
 * in the top stack frame of "thread".
 *
 * The other thread might be alive, so this has to work carefully.
 *
 * The thread list lock must be held.
 *
 * Returns "true" if we successfully recover the object.  "*pOwner" will
 * be NULL if we can't determine the owner for some reason (e.g. race
 * condition on ownership transfer).
 */
static bool extractMonitorEnterObject(Thread* thread, Object** pLockObj,
    Thread** pOwner)
{
    void* framePtr = thread->interpSave.curFrame;

    if (framePtr == NULL || dvmIsBreakFrame((u4*)framePtr))
        return false;

    const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
    const Method* method = saveArea->method;
    const u2* currentPc = saveArea->xtra.currentPc;

    /* check Method* */
    if (!dvmLinearAllocContains(method, sizeof(Method))) {
        ALOGD("ExtrMon: method %p not valid", method);
        return false;
    }

    /* check currentPc */
    u4 insnsSize = dvmGetMethodInsnsSize(method);
    if (currentPc < method->insns ||
        currentPc >= method->insns + insnsSize)
    {
        ALOGD("ExtrMon: insns %p not valid (%p - %p)",
            currentPc, method->insns, method->insns + insnsSize);
        return false;
    }

    /* check the instruction */
    if ((*currentPc & 0xff) != OP_MONITOR_ENTER) {
        ALOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)",
            currentPc, *currentPc & 0xff);
        return false;
    }

    /* get and check the register index */
    unsigned int reg = *currentPc >> 8;
    if (reg >= method->registersSize) {
        ALOGD("ExtrMon: invalid register %d (max %d)",
            reg, method->registersSize);
        return false;
    }

    /* get and check the object in that register */
    u4* fp = (u4*) framePtr;
    Object* obj = (Object*) fp[reg];
    if (obj != NULL && !dvmIsHeapAddress(obj)) {
        ALOGD("ExtrMon: invalid object %p at %p[%d]", obj, fp, reg);
        return false;
    }
    *pLockObj = obj;

    /*
     * Try to determine the object's lock holder; it's okay if this fails.
     *
     * We're assuming the thread list lock is already held by this thread.
     * If it's not, we may be living dangerously if we have to scan through
     * the thread list to find a match.  (The VM will generally be in a
     * suspended state when executing here, so this is a minor concern
     * unless we're dumping while threads are running, in which case there's
     * a good chance of stuff blowing up anyway.)
     */
    *pOwner = dvmGetObjectLockHolder(obj);

    return true;
}
Exemplo n.º 13
0
void
hprofFillInStackTrace(void *objectPtr)

{
    DvmHeapChunk *chunk;
    StackTraceEntry stackTraceEntry;
    Thread* self;
    void* fp;
    int i;
    
    if (objectPtr == NULL) {
        return;
    }
    self = dvmThreadSelf();
    if (self == NULL) {
        return;
    }
    fp = self->curFrame;

    /* Serial number to be filled in later. */
    stackTraceEntry.trace.serialNumber = -1;

    /*
     * TODO - The HAT tool doesn't care about thread data, so we can defer
     * actually emitting thread records and assigning thread serial numbers.
     */
    stackTraceEntry.trace.threadSerialNumber = (int) self;

    memset(&stackTraceEntry.trace.frameIds, 0,
            sizeof(stackTraceEntry.trace.frameIds));

    i = 0;
    while ((fp != NULL) && (i < STACK_DEPTH)) {
        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
        const Method* method = saveArea->method;
        StackFrameEntry frame;

        if (!dvmIsBreakFrame(fp)) {
            frame.frame.method = method;
            if (dvmIsNativeMethod(method)) {
                frame.frame.pc = 0; /* no saved PC for native methods */
            } else {
                assert(saveArea->xtra.currentPc >= method->insns &&
                        saveArea->xtra.currentPc <
                        method->insns + dvmGetMethodInsnsSize(method));
                frame.frame.pc = (int) (saveArea->xtra.currentPc -
                        method->insns);
            }

            // Canonicalize the frame and cache it in the hprof context
            stackTraceEntry.trace.frameIds[i++] =
                hprofLookupStackFrameId(&frame);
        }

        assert(fp != saveArea->prevFrame);
        fp = saveArea->prevFrame;
    }

    /* Store the stack trace serial number in the object header */
    chunk = ptr2chunk(objectPtr);
    chunk->stackTraceSerialNumber =
            hprofLookupStackSerialNumber(&stackTraceEntry);
}
Exemplo n.º 14
0
/*
 * Dump stack frames, starting from the specified frame and moving down.
 *
 * Each frame holds a pointer to the currently executing method, and the
 * saved program counter from the caller ("previous" frame).  This means
 * we don't have the PC for the current method on the stack, which is
 * pretty reasonable since it's in the "PC register" for the VM.  Because
 * exceptions need to show the correct line number we actually *do* have
 * an updated version in the fame's "xtra.currentPc", but it's unreliable.
 *
 * Note "framePtr" could be NULL in rare circumstances.
 */
static void dumpFrames(const DebugOutputTarget* target, void* framePtr,
    Thread* thread)
{
    const StackSaveArea* saveArea;
    const Method* method;
    int checkCount = 0;
    const u2* currentPc = NULL;
    bool first = true;

    /*
     * The "currentPc" is updated whenever we execute an instruction that
     * might throw an exception.  Show it here.
     */
    if (framePtr != NULL && !dvmIsBreakFrame(framePtr)) {
        saveArea = SAVEAREA_FROM_FP(framePtr);

        if (saveArea->xtra.currentPc != NULL)
            currentPc = saveArea->xtra.currentPc;
    }

    while (framePtr != NULL) {
        saveArea = SAVEAREA_FROM_FP(framePtr);
        method = saveArea->method;

        if (dvmIsBreakFrame(framePtr)) {
            //dvmPrintDebugMessage(target, "  (break frame)\n");
        } else {
            int relPc;

            if (currentPc != NULL)
                relPc = currentPc - saveArea->method->insns;
            else
                relPc = -1;

            char* className = dvmDescriptorToDot(method->clazz->descriptor);
            if (dvmIsNativeMethod(method))
                dvmPrintDebugMessage(target,
                    "  at %s.%s(Native Method)\n", className, method->name);
            else {
                dvmPrintDebugMessage(target,
                    "  at %s.%s(%s:%s%d)\n",
                    className, method->name, dvmGetMethodSourceFile(method),
                    (relPc >= 0 && first) ? "~" : "",
                    relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
            }
            free(className);

            if (first) {
                /*
                 * Decorate WAIT and MONITOR threads with some detail on
                 * the first frame.
                 *
                 * warning: wait status not stable, even in suspend
                 */
                if (thread->status == THREAD_WAIT ||
                    thread->status == THREAD_TIMED_WAIT)
                {
                    Monitor* mon = thread->waitMonitor;
                    Object* obj = dvmGetMonitorObject(mon);
                    if (obj != NULL) {
                        className = dvmDescriptorToDot(obj->clazz->descriptor);
                        dvmPrintDebugMessage(target,
                            "  - waiting on <%p> (a %s)\n", obj, className);
                        free(className);
                    }
                } else if (thread->status == THREAD_MONITOR) {
                    Object* obj;
                    Thread* owner;
                    if (extractMonitorEnterObject(thread, &obj, &owner)) {
                        className = dvmDescriptorToDot(obj->clazz->descriptor);
                        if (owner != NULL) {
                            char* threadName = dvmGetThreadName(owner);
                            dvmPrintDebugMessage(target,
                                "  - waiting to lock <%p> (a %s) held by threadid=%d (%s)\n",
                                obj, className, owner->threadId, threadName);
                            free(threadName);
                        } else {
                            dvmPrintDebugMessage(target,
                                "  - waiting to lock <%p> (a %s) held by ???\n",
                                obj, className);
                        }
                        free(className);
                    }
                }
            }
        }

        /*
         * Get saved PC for previous frame.  There's no savedPc in a "break"
         * frame, because that represents native or interpreted code
         * invoked by the VM.  The saved PC is sitting in the "PC register",
         * a local variable on the native stack.
         */
        currentPc = saveArea->savedPc;

        first = false;

        if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) {
            LOGW("Warning: loop in stack trace at frame %d (%p -> %p)\n",
                checkCount, framePtr, saveArea->prevFrame);
            break;
        }
        framePtr = saveArea->prevFrame;

        checkCount++;
        if (checkCount > 300) {
            dvmPrintDebugMessage(target,
                "  ***** printed %d frames, not showing any more\n",
                checkCount);
            break;
        }
    }
    dvmPrintDebugMessage(target, "\n");
}
Exemplo n.º 15
0
/*
 * We have to carry the exception's stack trace around, but in many cases
 * it will never be examined.  It makes sense to keep it in a compact,
 * VM-specific object, rather than an array of Objects with strings.
 *
 * Pass in the thread whose stack we're interested in.  If "thread" is
 * not self, the thread must be suspended.  This implies that the thread
 * list lock is held, which means we can't allocate objects or we risk
 * jamming the GC.  So, we allow this function to return different formats.
 * (This shouldn't be called directly -- see the inline functions in the
 * header file.)
 *
 * If "wantObject" is true, this returns a newly-allocated Object, which is
 * presently an array of integers, but could become something else in the
 * future.  If "wantObject" is false, return plain malloc data.
 *
 * NOTE: if we support class unloading, we will need to scan the class
 * object references out of these arrays.
 */
void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount)
{
    ArrayObject* stackData = NULL;
    int* simpleData = NULL;
    void* fp;
    void* startFp;
    int stackDepth;
    int* intPtr;

    if (pCount != NULL)
        *pCount = 0;
    fp = thread->curFrame;

    assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));

    /*
     * We're looking at a stack frame for code running below a Throwable
     * constructor.  We want to remove the Throwable methods and the
     * superclass initializations so the user doesn't see them when they
     * read the stack dump.
     *
     * TODO: this just scrapes off the top layers of Throwable.  Might not do
     * the right thing if we create an exception object or cause a VM
     * exception while in a Throwable method.
     */
    while (fp != NULL) {
        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
        const Method* method = saveArea->method;

        if (dvmIsBreakFrame(fp))
            break;
        if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
            break;
        //LOGD("EXCEP: ignoring %s.%s\n",
        //         method->clazz->descriptor, method->name);
        fp = saveArea->prevFrame;
    }
    startFp = fp;

    /*
     * Compute the stack depth.
     */
    stackDepth = 0;
    while (fp != NULL) {
        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);

        if (!dvmIsBreakFrame(fp))
            stackDepth++;

        assert(fp != saveArea->prevFrame);
        fp = saveArea->prevFrame;
    }
    //LOGD("EXCEP: stack depth is %d\n", stackDepth);

    if (!stackDepth)
        goto bail;

    /*
     * We need to store a pointer to the Method and the program counter.
     * We have 4-byte pointers, so we use '[I'.
     */
    if (wantObject) {
        assert(sizeof(Method*) == 4);
        stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
        if (stackData == NULL) {
            assert(dvmCheckException(dvmThreadSelf()));
            goto bail;
        }
        intPtr = (int*) stackData->contents;
    } else {
        /* array of ints; first entry is stack depth */
        assert(sizeof(Method*) == sizeof(int));
        simpleData = (int*) malloc(sizeof(int) * stackDepth*2);
        if (simpleData == NULL)
            goto bail;

        assert(pCount != NULL);
        intPtr = simpleData;
    }
    if (pCount != NULL)
        *pCount = stackDepth;

    fp = startFp;
    while (fp != NULL) {
        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
        const Method* method = saveArea->method;

        if (!dvmIsBreakFrame(fp)) {
            //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor,
            //         method->name);

            *intPtr++ = (int) method;
            if (dvmIsNativeMethod(method)) {
                *intPtr++ = 0;      /* no saved PC for native methods */
            } else {
                assert(saveArea->xtra.currentPc >= method->insns &&
                        saveArea->xtra.currentPc <
                        method->insns + dvmGetMethodInsnsSize(method));
                *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
            }

            stackDepth--;       // for verification
        }

        assert(fp != saveArea->prevFrame);
        fp = saveArea->prevFrame;
    }
    assert(stackDepth == 0);

bail:
    if (wantObject) {
        dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
        return stackData;
    } else {
        return simpleData;
    }
}
Exemplo n.º 16
0
/*
 * Find a matching "catch" block.  "pc" is the relative PC within the
 * current method, indicating the offset from the start in 16-bit units.
 *
 * Returns the offset to the catch block, or -1 if we run up against a
 * break frame without finding anything.
 *
 * The class resolution stuff we have to do while evaluating the "catch"
 * blocks could cause an exception.  The caller should clear the exception
 * before calling here and restore it after.
 *
 * Sets *newFrame to the frame pointer of the frame with the catch block.
 * If "scanOnly" is false, self->curFrame is also set to this value.
 */
int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
    bool scanOnly, void** newFrame)
{
    void* fp = self->curFrame;
    int catchAddr = -1;

    assert(!dvmCheckException(self));

    while (true) {
        StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
        catchAddr = findCatchInMethod(self, saveArea->method, relPc,
                        exception->clazz);
        if (catchAddr >= 0)
            break;

        /*
         * Normally we'd check for ACC_SYNCHRONIZED methods and unlock
         * them as we unroll.  Dalvik uses what amount to generated
         * "finally" blocks to take care of this for us.
         */

        /* output method profiling info */
        if (!scanOnly) {
            TRACE_METHOD_UNROLL(self, saveArea->method);
        }

        /*
         * Move up one frame.  If the next thing up is a break frame,
         * break out now so we're left unrolled to the last method frame.
         * We need to point there so we can roll up the JNI local refs
         * if this was a native method.
         */
        assert(saveArea->prevFrame != NULL);
        if (dvmIsBreakFrame(saveArea->prevFrame)) {
            if (!scanOnly)
                break;      // bail with catchAddr == -1

            /*
             * We're scanning for the debugger.  It needs to know if this
             * exception is going to be caught or not, and we need to figure
             * out if it will be caught *ever* not just between the current
             * position and the next break frame.  We can't tell what native
             * code is going to do, so we assume it never catches exceptions.
             *
             * Start by finding an interpreted code frame.
             */
            fp = saveArea->prevFrame;           // this is the break frame
            saveArea = SAVEAREA_FROM_FP(fp);
            fp = saveArea->prevFrame;           // this may be a good one
            while (fp != NULL) {
                if (!dvmIsBreakFrame(fp)) {
                    saveArea = SAVEAREA_FROM_FP(fp);
                    if (!dvmIsNativeMethod(saveArea->method))
                        break;
                }

                fp = SAVEAREA_FROM_FP(fp)->prevFrame;
            }
            if (fp == NULL)
                break;      // bail with catchAddr == -1

            /*
             * Now fp points to the "good" frame.  When the interp code
             * invoked the native code, it saved a copy of its current PC
             * into xtra.currentPc.  Pull it out of there.
             */
            relPc =
                saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
        } else {
            fp = saveArea->prevFrame;

            /* savedPc in was-current frame goes with method in now-current */
            relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
        }
    }

    if (!scanOnly)
        self->curFrame = fp;

    /*
     * The class resolution in findCatchInMethod() could cause an exception.
     * Clear it to be safe.
     */
    self->exception = NULL;

    *newFrame = fp;
    return catchAddr;
}