/* * 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; }
/* * 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; }
/* * This is used by the JNI PushLocalFrame call. We push a new frame onto * the stack that has no ins, outs, or locals, and no break frame above it. * It's strictly used for tracking JNI local refs, and will be popped off * by dvmPopFrame if it's not removed explicitly. */ bool dvmPushLocalFrame(Thread* self, const Method* method) { StackSaveArea* saveBlock; int stackReq; u1* stackPtr; assert(dvmIsNativeMethod(method)); stackReq = sizeof(StackSaveArea); // regular frame assert(self->curFrame != NULL); stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame); if (stackPtr - stackReq < self->interpStackEnd) { /* not enough space; let JNI throw the exception */ LOGW("Stack overflow on PushLocal " "(req=%d top=%p cur=%p size=%d '%s')\n", stackReq, self->interpStackStart, self->curFrame, self->interpStackSize, method->name); dvmHandleStackOverflow(self, method); assert(dvmCheckException(self)); return false; } /* * Shift the stack pointer down, leaving space for just the stack save * area for the break frame, then shift down farther for the full frame. */ stackPtr -= sizeof(StackSaveArea); saveBlock = (StackSaveArea*) stackPtr; #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) /* debug -- memset the new stack */ memset(stackPtr, 0xaf, stackReq); #endif #ifdef EASY_GDB saveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame); #endif saveBlock->prevFrame = self->curFrame; saveBlock->savedPc = NULL; // not required #ifdef USE_INDIRECT_REF saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; #else saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry; #endif saveBlock->method = method; LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)\n", self->curFrame, FP_FROM_SAVEAREA(saveBlock), (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); self->curFrame = FP_FROM_SAVEAREA(saveBlock); return true; }
/* * Main interpreter loop. * * This was written with an ARM implementation in mind. */ void dvmInterpretPortable(Thread* self) { #if defined(EASY_GDB) StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame); #endif DvmDex* methodClassDex; // curMethod->clazz->pDvmDex JValue retval; /* core state */ const Method* curMethod; // method we're interpreting const u2* pc; // program counter u4* fp; // frame pointer u2 inst; // current instruction /* instruction decoding */ u4 ref; // 16 or 32-bit quantity fetched directly u2 vsrc1, vsrc2, vdst; // usually used for register indexes /* method call setup */ const Method* methodToCall; bool methodCallRange; /* static computed goto table */ DEFINE_GOTO_TABLE(handlerTable); /* copy state in */ curMethod = self->interpSave.method; pc = self->interpSave.pc; fp = self->interpSave.curFrame; retval = self->interpSave.retval; /* only need for kInterpEntryReturn? */ methodClassDex = curMethod->clazz->pDvmDex; LOGVV("threadid=%d: %s.%s pc=%#x fp=%p", self->threadId, curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns, fp); /* * Handle any ongoing profiling and prep for debugging. */ if (self->interpBreak.ctl.subMode != 0) { TRACE_METHOD_ENTER(self, curMethod); self->debugIsMethodEntry = true; // Always true on startup } /* * DEBUG: scramble this to ensure we're not relying on it. */ methodToCall = (const Method*) -1; #if 0 if (self->debugIsMethodEntry) { ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor, curMethod->name); DUMP_REGS(curMethod, self->interpSave.curFrame, false); } #endif FINISH(0); /* fetch and execute first instruction */
/* * Get the caller's class. Pass in the current fp. * * This is used by e.g. java.lang.Class. */ ClassObject* dvmGetCallerClass(const void* curFrame) { void* caller; caller = dvmGetCallerFP(curFrame); if (caller == NULL) return NULL; return SAVEAREA_FROM_FP(caller)->method->clazz; }
/* * 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; }
/* * 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 the StackSaveArea for the specified frame pointer. */ void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea) { StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea); #ifdef EASY_GDB printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n", saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc, saveArea->method, saveArea->xtra.currentPc); #else printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n", saveArea->prevFrame, saveArea->savedPc, saveArea->method, saveArea->xtra.currentPc, *(u4*)fp); #endif }
static void crawlDalvikStack(Thread *thread, bool print) { void *fp = thread->interpSave.curFrame; StackSaveArea* saveArea = NULL; int stackLevel = 0; /* Crawl the Dalvik stack frames to clear the returnAddr field */ while (fp != NULL) { saveArea = SAVEAREA_FROM_FP(fp); stackLevel++; saveArea->returnAddr = 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); }
/* * Open up the reserved area and throw an exception. The reserved area * should only be needed to create and initialize the exception itself. * * If we already opened it and we're continuing to overflow, abort the VM. * * We have to leave the "reserved" area open until the "catch" handler has * finished doing its processing. This is because the catch handler may * need to resolve classes, which requires calling into the class loader if * the classes aren't already in the "initiating loader" list. */ void dvmHandleStackOverflow(Thread* self, const Method* method) { /* * Can we make the reserved area available? */ if (self->stackOverflowed) { /* * Already did, nothing to do but bail. */ LOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting\n", self->threadId); dvmDumpThread(self, false); dvmAbort(); } /* open it up to the full range */ LOGI("threadid=%d: stack overflow on call to %s.%s:%s\n", self->threadId, method->clazz->descriptor, method->name, method->shorty); StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->curFrame); LOGI(" method requires %d+%d+%d=%d bytes, fp is %p (%d left)\n", method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4, (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea), saveArea, (u1*) saveArea - self->interpStackEnd); LOGI(" expanding stack end (%p to %p)\n", self->interpStackEnd, self->interpStackStart - self->interpStackSize); //dvmDumpThread(self, false); self->interpStackEnd = self->interpStackStart - self->interpStackSize; self->stackOverflowed = true; /* * If we were trying to throw an exception when the stack overflowed, * we will blow up when doing the class lookup on StackOverflowError * because of the pending exception. So, we clear it and make it * the cause of the SOE. */ Object* excep = dvmGetException(self); if (excep != NULL) { LOGW("Stack overflow while throwing exception\n"); dvmClearException(self); } dvmThrowChainedExceptionByClass(gDvm.classJavaLangStackOverflowError, NULL, excep); }
/** * @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); }
// work-around to get a reference wrapper to an object so that it can be used // for certain calls to the JNI environment. almost verbatim copy from Jni.cpp static jobject dexspyAddLocalReference(::Thread* self, Object* obj) { if (obj == NULL) { return NULL; } IndirectRefTable* pRefTable = &self->jniLocalRefTable; void* curFrame = self->interpSave.curFrame; u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; jobject jobj = (jobject) pRefTable->add(cookie, obj); if (UNLIKELY(jobj == NULL)) { pRefTable->dump("JNI local"); ALOGE("Failed adding to JNI local ref table (has %zd entries)", pRefTable->capacity()); dvmDumpThread(self, false); dvmAbort(); // spec says call FatalError; this is equivalent } if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) { // Hand out direct pointers to support broken old apps. return reinterpret_cast<jobject>(obj); } return jobj; }
/* * 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"); }
/* * Lock a monitor. */ static void lockMonitor(Thread* self, Monitor* mon) { ThreadStatus oldStatus; u4 waitThreshold, samplePercent; u8 waitStart, waitEnd, waitMs; if (mon->owner == self) { mon->lockCount++; return; } if (dvmTryLockMutex(&mon->lock) != 0) { oldStatus = dvmChangeStatus(self, THREAD_MONITOR); waitThreshold = gDvm.lockProfThreshold; if (waitThreshold) { waitStart = dvmGetRelativeTimeUsec(); } const Method* currentOwnerMethod = mon->ownerMethod; u4 currentOwnerPc = mon->ownerPc; dvmLockMutex(&mon->lock); if (waitThreshold) { waitEnd = dvmGetRelativeTimeUsec(); } dvmChangeStatus(self, oldStatus); if (waitThreshold) { waitMs = (waitEnd - waitStart) / 1000; if (waitMs >= waitThreshold) { samplePercent = 100; } else { samplePercent = 100 * waitMs / waitThreshold; } if (samplePercent != 0 && ((u4)rand() % 100 < samplePercent)) { const char* currentOwnerFileName = "no_method"; u4 currentOwnerLineNumber = 0; if (currentOwnerMethod != NULL) { currentOwnerFileName = dvmGetMethodSourceFile(currentOwnerMethod); if (currentOwnerFileName == NULL) { currentOwnerFileName = "no_method_file"; } currentOwnerLineNumber = dvmLineNumFromPC(currentOwnerMethod, currentOwnerPc); } logContentionEvent(self, waitMs, samplePercent, currentOwnerFileName, currentOwnerLineNumber); } } } mon->owner = self; assert(mon->lockCount == 0); // When debugging, save the current monitor holder for future // acquisition failures to use in sampled logging. if (gDvm.lockProfThreshold > 0) { mon->ownerMethod = NULL; mon->ownerPc = 0; if (self->interpSave.curFrame == NULL) { return; } const StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame); if (saveArea == NULL) { return; } mon->ownerMethod = saveArea->method; mon->ownerPc = (saveArea->xtra.currentPc - saveArea->method->insns); } }
static void logContentionEvent(Thread *self, u4 waitMs, u4 samplePercent, const char *ownerFileName, u4 ownerLineNumber) { const StackSaveArea *saveArea; const Method *meth; u4 relativePc; char eventBuffer[174]; const char *fileName; char procName[33]; char *cp; size_t len; int fd; /* When a thread is being destroyed it is normal that the frame depth is zero */ if (self->interpSave.curFrame == NULL) { return; } saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame); meth = saveArea->method; cp = eventBuffer; /* Emit the event list length, 1 byte. */ *cp++ = 9; /* Emit the process name, <= 37 bytes. */ fd = open("/proc/self/cmdline", O_RDONLY); memset(procName, 0, sizeof(procName)); read(fd, procName, sizeof(procName) - 1); close(fd); len = strlen(procName); cp = logWriteString(cp, procName, len); /* Emit the sensitive thread ("main thread") status, 5 bytes. */ bool isSensitive = false; if (gDvm.isSensitiveThreadHook != NULL) { isSensitive = gDvm.isSensitiveThreadHook(); } cp = logWriteInt(cp, isSensitive); /* Emit self thread name string, <= 37 bytes. */ std::string selfName = dvmGetThreadName(self); cp = logWriteString(cp, selfName.c_str(), selfName.size()); /* Emit the wait time, 5 bytes. */ cp = logWriteInt(cp, waitMs); /* Emit the source code file name, <= 37 bytes. */ fileName = dvmGetMethodSourceFile(meth); if (fileName == NULL) fileName = ""; cp = logWriteString(cp, fileName, strlen(fileName)); /* Emit the source code line number, 5 bytes. */ relativePc = saveArea->xtra.currentPc - saveArea->method->insns; cp = logWriteInt(cp, dvmLineNumFromPC(meth, relativePc)); /* Emit the lock owner source code file name, <= 37 bytes. */ if (ownerFileName == NULL) { ownerFileName = ""; } else if (strcmp(fileName, ownerFileName) == 0) { /* Common case, so save on log space. */ ownerFileName = "-"; } cp = logWriteString(cp, ownerFileName, strlen(ownerFileName)); /* Emit the source code line number, 5 bytes. */ cp = logWriteInt(cp, ownerLineNumber); /* Emit the sample percentage, 5 bytes. */ cp = logWriteInt(cp, samplePercent); assert((size_t)(cp - eventBuffer) <= sizeof(eventBuffer)); android_btWriteLog(EVENT_LOG_TAG_dvm_lock_sample, EVENT_TYPE_LIST, eventBuffer, (size_t)(cp - eventBuffer)); }
/* * Visits all stack slots except those belonging to native method * arguments. */ static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg) { assert(visitor != NULL); assert(thread != NULL); u4 threadId = thread->threadId; const StackSaveArea *saveArea; for (u4 *fp = (u4 *)thread->interpSave.curFrame; fp != NULL; fp = (u4 *)saveArea->prevFrame) { Method *method; saveArea = SAVEAREA_FROM_FP(fp); method = (Method *)saveArea->method; if (method != NULL && !dvmIsNativeMethod(method)) { #ifdef FASTIVA // @zee do not call any malloc in gc task. // cf) dvmGetExpandedRegisterMap() const RegisterMap* pMap = NULL; #else const RegisterMap* pMap = dvmGetExpandedRegisterMap(method); #endif const u1* regVector = NULL; #ifndef FASTIVA if (pMap != NULL) { /* found map, get registers for this address */ int addr = saveArea->xtra.currentPc - method->insns; regVector = dvmRegisterMapGetLine(pMap, addr); } #endif if (regVector == NULL) { /* * Either there was no register map or there is no * info for the current PC. Perform a conservative * scan. */ for (size_t i = 0; i < method->registersSize; ++i) { if (dvmIsValidObject((Object *)fp[i])) { (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg); } } } else { /* * Precise scan. v0 is at the lowest address on the * interpreted stack, and is the first bit in the * register vector, so we can walk through the * register map and memory in the same direction. * * A '1' bit indicates a live reference. */ u2 bits = 1 << 1; for (size_t i = 0; i < method->registersSize; ++i) { bits >>= 1; if (bits == 1) { /* set bit 9 so we can tell when we're empty */ bits = *regVector++ | 0x0100; } if ((bits & 0x1) != 0) { /* * Register is marked as live, it's a valid root. */ #if WITH_EXTRA_GC_CHECKS if (fp[i] != 0 && !dvmIsValidObject((Object *)fp[i])) { /* this is very bad */ ALOGE("PGC: invalid ref in reg %d: %#x", method->registersSize - 1 - i, fp[i]); ALOGE("PGC: %s.%s addr %#x", method->clazz->descriptor, method->name, saveArea->xtra.currentPc - method->insns); continue; } #endif (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg); } } dvmReleaseRegisterMapLine(pMap, regVector); } } /* * Don't fall into an infinite loop if things get corrupted. */ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)fp || saveArea->prevFrame == NULL); } #ifdef FASTIVA int* stack_bottom = (int*)thread->m_pNativeStackBottom; int* stack_top = (int*)thread->m_pNativeStackPointer; const bool DUMP_STACK = 0; if (DUMP_STACK) { ALOGE("##### scan_stack %i %p~%p", thread->systemTid, stack_top, stack_bottom); } assert(thread->status != THREAD_RUNNING || thread == dvmThreadSelf()); while (stack_top < stack_bottom) { if (dvmIsValidObject((Object*)stack_top[0])) { (*visitor)(stack_top, threadId, ROOT_JAVA_FRAME, arg); } stack_top ++; } #endif }
/* * We're calling an interpreted method from an internal VM function or * via reflection. * * Push a frame for an interpreted method onto the stack. This is only * used when calling into interpreted code from native code. (The * interpreter does its own stack frame manipulation for interp-->interp * calls.) * * The size we need to reserve is the sum of parameters, local variables, * saved goodies, and outbound parameters. * * We start by inserting a "break" frame, which ensures that the interpreter * hands control back to us after the function we call returns or an * uncaught exception is thrown. */ static bool dvmPushInterpFrame(Thread* self, const Method* method) { StackSaveArea* saveBlock; StackSaveArea* breakSaveBlock; int stackReq; u1* stackPtr; assert(!dvmIsNativeMethod(method)); assert(!dvmIsAbstractMethod(method)); stackReq = method->registersSize * 4 // params + locals + sizeof(StackSaveArea) * 2 // break frame + regular frame + method->outsSize * 4; // args to other methods if (self->interpSave.curFrame != NULL) stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); else stackPtr = self->interpStackStart; if (stackPtr - stackReq < self->interpStackEnd) { /* not enough space */ ALOGW("Stack overflow on call to interp " "(req=%d top=%p cur=%p size=%d %s.%s)", stackReq, self->interpStackStart, self->interpSave.curFrame, self->interpStackSize, method->clazz->descriptor, method->name); dvmHandleStackOverflow(self, method); assert(dvmCheckException(self)); return false; } /* * Shift the stack pointer down, leaving space for the function's * args/registers and save area. */ stackPtr -= sizeof(StackSaveArea); breakSaveBlock = (StackSaveArea*)stackPtr; stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); saveBlock = (StackSaveArea*) stackPtr; #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) /* debug -- memset the new stack, unless we want valgrind's help */ memset(stackPtr - (method->outsSize*4), 0xaf, stackReq); #endif #ifdef EASY_GDB breakSaveBlock->prevSave = (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame); saveBlock->prevSave = breakSaveBlock; #endif breakSaveBlock->prevFrame = self->interpSave.curFrame; breakSaveBlock->savedPc = NULL; // not required breakSaveBlock->xtra.localRefCookie = 0; // not required breakSaveBlock->method = NULL; saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); saveBlock->savedPc = NULL; // not required saveBlock->xtra.currentPc = NULL; // not required? saveBlock->method = method; LOGVV("PUSH frame: old=%p new=%p (size=%d)", self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); return true; }
/* * Visits all stack slots. TODO: visit native methods. */ static void visitThreadStack(Visitor *visitor, Thread *thread, void *arg) { const StackSaveArea *saveArea; u4 *framePtr; assert(visitor != NULL); assert(thread != NULL); framePtr = (u4 *)thread->curFrame; for (; framePtr != NULL; framePtr = saveArea->prevFrame) { Method *method; saveArea = SAVEAREA_FROM_FP(framePtr); method = (Method *)saveArea->method; if (method != NULL && !dvmIsNativeMethod(method)) { const RegisterMap* pMap = dvmGetExpandedRegisterMap(method); const u1* regVector = NULL; size_t i; if (pMap != NULL) { /* found map, get registers for this address */ int addr = saveArea->xtra.currentPc - method->insns; regVector = dvmRegisterMapGetLine(pMap, addr); } if (regVector == NULL) { /* * Either there was no register map or there is no * info for the current PC. Perform a conservative * scan. */ for (i = 0; i < method->registersSize; ++i) { if (dvmIsValidObject((Object *)framePtr[i])) { (*visitor)(&framePtr[i], arg); } } } else { /* * Precise scan. v0 is at the lowest address on the * interpreted stack, and is the first bit in the * register vector, so we can walk through the * register map and memory in the same direction. * * A '1' bit indicates a live reference. */ u2 bits = 1 << 1; for (i = 0; i < method->registersSize; ++i) { bits >>= 1; if (bits == 1) { /* set bit 9 so we can tell when we're empty */ bits = *regVector++ | 0x0100; } if ((bits & 0x1) != 0) { /* * Register is marked as live, it's a valid root. */ (*visitor)(&framePtr[i], arg); } } dvmReleaseRegisterMapLine(pMap, regVector); } } /* * Don't fall into an infinite loop if things get corrupted. */ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr || saveArea->prevFrame == NULL); } }
/* * Dump the stack for the specified thread, which is still running. * * This is very dangerous, because stack frames are being pushed on and * popped off, and if the thread exits we'll be looking at freed memory. * The plan here is to take a snapshot of the stack and then dump that * to try to minimize the chances of catching it mid-update. This should * work reasonably well on a single-CPU system. * * There is a small chance that calling here will crash the VM. */ void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread) { StackSaveArea* saveArea; const u1* origStack; u1* stackCopy = NULL; int origSize, fpOffset; void* fp; int depthLimit = 200; if (thread == NULL || thread->interpSave.curFrame == NULL) { dvmPrintDebugMessage(target, "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n", thread, (thread != NULL) ? thread->threadId : 0); return; } /* wait for a full quantum */ sched_yield(); /* copy the info we need, then the stack itself */ origSize = thread->interpStackSize; origStack = (const u1*) thread->interpStackStart - origSize; stackCopy = (u1*) malloc(origSize); fpOffset = (u1*) thread->interpSave.curFrame - origStack; memcpy(stackCopy, origStack, origSize); /* * Run through the stack and rewrite the "prev" pointers. */ //ALOGI("DR: fpOff=%d (from %p %p)",fpOffset, origStack, // thread->interpSave.curFrame); fp = stackCopy + fpOffset; while (true) { int prevOffset; if (depthLimit-- < 0) { /* we're probably screwed */ dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n"); dvmAbort(); } saveArea = SAVEAREA_FROM_FP(fp); if (saveArea->prevFrame == NULL) break; prevOffset = (u1*) saveArea->prevFrame - origStack; if (prevOffset < 0 || prevOffset > origSize) { dvmPrintDebugMessage(target, "DumpRunning: bad offset found: %d (from %p %p)\n", prevOffset, origStack, saveArea->prevFrame); saveArea->prevFrame = NULL; break; } saveArea->prevFrame = (u4*)(stackCopy + prevOffset); fp = saveArea->prevFrame; } /* * We still need to pass the Thread for some monitor wait stuff. */ dumpFrames(target, stackCopy + fpOffset, thread); free(stackCopy); }
/* * We're calling a JNI native method from an internal VM fuction or * via reflection. This is also used to create the "fake" native-method * frames at the top of the interpreted stack. * * This actually pushes two frames; the first is a "break" frame. * * The top frame has additional space for JNI local reference tracking. */ bool dvmPushJNIFrame(Thread* self, const Method* method) { StackSaveArea* saveBlock; StackSaveArea* breakSaveBlock; int stackReq; u1* stackPtr; assert(dvmIsNativeMethod(method)); stackReq = method->registersSize * 4 // params only + sizeof(StackSaveArea) * 2; // break frame + regular frame if (self->interpSave.curFrame != NULL) stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); else stackPtr = self->interpStackStart; if (stackPtr - stackReq < self->interpStackEnd) { /* not enough space */ ALOGW("Stack overflow on call to native " "(req=%d top=%p cur=%p size=%d '%s')", stackReq, self->interpStackStart, self->interpSave.curFrame, self->interpStackSize, method->name); dvmHandleStackOverflow(self, method); assert(dvmCheckException(self)); return false; } /* * Shift the stack pointer down, leaving space for just the stack save * area for the break frame, then shift down farther for the full frame. * We leave space for the method args, which are copied in later. */ stackPtr -= sizeof(StackSaveArea); breakSaveBlock = (StackSaveArea*)stackPtr; stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); saveBlock = (StackSaveArea*) stackPtr; #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) /* debug -- memset the new stack */ memset(stackPtr, 0xaf, stackReq); #endif #ifdef EASY_GDB if (self->interpSave.curFrame == NULL) breakSaveBlock->prevSave = NULL; else { void* fp = FP_FROM_SAVEAREA(self->interpSave.curFrame); breakSaveBlock->prevSave = (StackSaveArea*)fp; } saveBlock->prevSave = breakSaveBlock; #endif breakSaveBlock->prevFrame = self->interpSave.curFrame; breakSaveBlock->savedPc = NULL; // not required breakSaveBlock->xtra.localRefCookie = 0; // not required breakSaveBlock->method = NULL; saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); saveBlock->savedPc = NULL; // not required saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; saveBlock->method = method; LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)", self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 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); }
/* * Main interpreter loop. * * This was written with an ARM implementation in mind. */ bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState) { #if defined(EASY_GDB) StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame); #endif #if INTERP_TYPE == INTERP_DBG bool debugIsMethodEntry = interpState->debugIsMethodEntry; #endif #if defined(WITH_TRACKREF_CHECKS) int debugTrackedRefStart = interpState->debugTrackedRefStart; #endif DvmDex* methodClassDex; // curMethod->clazz->pDvmDex JValue retval; /* core state */ const Method* curMethod; // method we're interpreting const u2* pc; // program counter u4* fp; // frame pointer u2 inst; // current instruction /* instruction decoding */ u2 ref; // 16-bit quantity fetched directly u2 vsrc1, vsrc2, vdst; // usually used for register indexes /* method call setup */ const Method* methodToCall; bool methodCallRange; #if defined(THREADED_INTERP) /* static computed goto table */ DEFINE_GOTO_TABLE(handlerTable); #endif #if defined(WITH_JIT) #if 0 LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n", interpState->entryPoint, interpState->pc, interpState->method->name); #endif #if INTERP_TYPE == INTERP_DBG /* Check to see if we've got a trace selection request. */ if ( /* * Only perform dvmJitCheckTraceRequest if the entry point is * EntryInstr and the jit state is either kJitTSelectRequest or * kJitTSelectRequestHot. If debugger/profiler happens to be attached, * dvmJitCheckTraceRequest will change the jitState to kJitDone but * but stay in the dbg interpreter. */ (interpState->entryPoint == kInterpEntryInstr) && (interpState->jitState == kJitTSelectRequest || interpState->jitState == kJitTSelectRequestHot) && dvmJitCheckTraceRequest(self, interpState)) { interpState->nextMode = INTERP_STD; //LOGD("Invalid trace request, exiting\n"); return true; } #endif /* INTERP_TYPE == INTERP_DBG */ #endif /* WITH_JIT */ /* copy state in */ curMethod = interpState->method; pc = interpState->pc; fp = interpState->fp; retval = interpState->retval; /* only need for kInterpEntryReturn? */ methodClassDex = curMethod->clazz->pDvmDex; LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n", self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG", curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns, fp, interpState->entryPoint); /* * DEBUG: scramble this to ensure we're not relying on it. */ methodToCall = (const Method*) -1; #if INTERP_TYPE == INTERP_DBG if (debugIsMethodEntry) { ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor, curMethod->name); DUMP_REGS(curMethod, interpState->fp, false); } #endif switch (interpState->entryPoint) { case kInterpEntryInstr: /* just fall through to instruction loop or threaded kickstart */ break; case kInterpEntryReturn: CHECK_JIT(); goto returnFromMethod; case kInterpEntryThrow: goto exceptionThrown; default: dvmAbort(); } #ifdef THREADED_INTERP FINISH(0); /* fetch and execute first instruction */ #else while (1) { CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */ CHECK_TRACKED_REFS(); /* check local reference tracking */ /* fetch the next 16 bits from the instruction stream */ inst = FETCH(0); switch (INST_INST(inst)) {
/* * Main interpreter loop. * * This was written with an ARM implementation in mind. * portable���͵Ľ������Ľ���ѭ����� */ void dvmInterpretPortable(Thread* self) { #if defined(EASY_GDB) // ��������Ƿ�����ԣ�������ջ֡�ĵ�ַ StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame); #endif DvmDex* methodClassDex; // curMethod->clazz->pDvmDex JValue retval; /* core state */ const Method* curMethod; // method we're interpreting ��ǰ����Ҫ���͵ķ��� const u2* pc; // program counter ��������� u4* fp; // frame pointer ָ֡�� u2 inst; // current instruction ��ǰָ�� /* instruction decoding */ u4 ref; // 16 or 32-bit quantity fetched directly u2 vsrc1, vsrc2, vdst; // usually used for register indexes /* method call setup */ const Method* methodToCall; bool methodCallRange; /* * static computed goto table * ��̬����õ���ת�� * ʵ���Ͼ��Ƕ���õ�һ����� * �þ�̬��ת����libdex��dexopcode.h�ж��� * [��Ҫע����壺�ñ�ֻ�ṩ����cʵ�ֵĽ�������ʹ��] * ������������ * static const void* handlerTable[0x100] = { \ * H(OP_NOP), \ * H(OP_MOVE), \ * .... * } * ���������opcode-gen������߶�̬���ɵģ�����˵�����������ʲô�����ɵģ���Ҫ�ο��ù��ߵ�ʵ�� * * # define H(_op) &&op_##_op * ʵ�����������������&&op_OP_NOP �����ĵ�ַ */ DEFINE_GOTO_TABLE(handlerTable); /* copy state in * ��ʼ��һЩ״ֵ̬ */ curMethod = self->interpSave.method; pc = self->interpSave.pc; fp = self->interpSave.curFrame; retval = self->interpSave.retval; /* only need for kInterpEntryReturn? */ methodClassDex = curMethod->clazz->pDvmDex; //��ȡdex��ص�����(������Ҫ�ο�vm\DvmDex.cpp��������ʵ��) LOGVV("threadid=%d: %s.%s pc=%#x fp=%p", self->threadId, curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns, fp); /* * Handle any ongoing profiling and prep for debugging. * ������Ҫ�Ƿ�����ԣ�����������һ�����巽���Ľ��� */ if (self->interpBreak.ctl.subMode != 0) { TRACE_METHOD_ENTER(self, curMethod); self->debugIsMethodEntry = true; // Always true on startup } /* * DEBUG: scramble this to ensure we're not relying on it. */ methodToCall = (const Method*) -1; #if 0 if (self->debugIsMethodEntry) { ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor, curMethod->name); DUMP_REGS(curMethod, self->interpSave.curFrame, false); } #endif //�����↑ʼ����ȡָ�ִ�У����صĽ� // ������ʵ���Ͻ�����һ��do - while��ѭ����ֱ��ִ����Ϸ��� FINISH(0); /* fetch and execute first instruction */