/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; } }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }
/** * @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); }
/* * 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; }
/* * 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(); } }
/* * 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; }
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); }
/* * 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"); }
/* * 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; } }
/* * 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; }