/* * C mterp entry point. This just calls the various C fallbacks, making * this a slow but portable interpeter. * * This is only used for the "allstubs" variant. */ void dvmMterpStdRun(Thread* self) { jmp_buf jmpBuf; self->interpSave.bailPtr = &jmpBuf; /* We exit via a longjmp */ if (setjmp(jmpBuf)) { LOGVV("mterp threadid=%d returning", dvmThreadSelf()->threadId); return; } /* run until somebody longjmp()s out */ while (true) { typedef void (*Handler)(Thread* self); u2 inst = /*self->interpSave.*/pc[0]; /* * In mterp, dvmCheckBefore is handled via the altHandlerTable, * while in the portable interpreter it is part of the handler * FINISH code. For allstubs, we must do an explicit check * in the interpretation loop. */ if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff]; (void) gDvmMterpHandlerNames; /* avoid gcc "defined but not used" */ LOGVV("handler %p %s", handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]); (*handler)(self); } }
/* Certain VI functionalities are only supported on certain NIC types. * This function validates that the requested functionality is present * on the selected NIC. */ static int check_nic_compatibility(unsigned vi_flags, unsigned ef_vi_arch) { switch (ef_vi_arch) { case EFHW_ARCH_FALCON: if (vi_flags & EF_VI_TX_PUSH_ALWAYS) { LOGVV(ef_log("%s: ERROR: TX PUSH ALWAYS flag not supported" " on FALCON architecture", __FUNCTION__)); return -EOPNOTSUPP; } if (vi_flags & EF_VI_RX_TIMESTAMPS) { LOGVV(ef_log("%s: ERROR: RX TIMESTAMPS flag not supported" " on FALCON architecture", __FUNCTION__)); return -EOPNOTSUPP; } if (vi_flags & EF_VI_TX_TIMESTAMPS) { LOGVV(ef_log("%s: ERROR: TX TIMESTAMPS flag not supported" " on FALCON architecture", __FUNCTION__)); return -EOPNOTSUPP; } return 0; case EFHW_ARCH_EF10: return 0; default: return -EINVAL; } }
/* * Convert types and widen primitives. Puts the value of "arg" into * "destPtr". * * Returns the width of the argument in 32-bit words (1 or 2), or -1 on error. */ int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* destPtr) { int retVal; if (dvmIsPrimitiveClass(type)) { /* e.g.: "arg" is java/lang/Float instance, "type" is VM float class */ PrimitiveType srcType; s4* valuePtr; srcType = getBoxedType(arg); if (srcType == PRIM_NOT) { // didn't pass a boxed primitive in LOGVV("conv arg: type '%s' not boxed primitive", arg->clazz->descriptor); return -1; } /* assumes value is stored in first instance field */ valuePtr = (s4*) arg->instanceData; retVal = dvmConvertPrimitiveValue(srcType, type->primitiveType, valuePtr, destPtr); } else { /* verify object is compatible */ if ((arg == NULL) || dvmInstanceof(arg->clazz, type)) { *destPtr = (s4) arg; retVal = 1; } else { LOGVV("Arg %p (%s) not compatible with %s", arg, arg->clazz->descriptor, type->descriptor); retVal = -1; } } return retVal; }
/* * "Standard" mterp entry point. This sets up a "glue" structure and then * calls into the assembly interpreter implementation. * * (There is presently no "debug" entry point.) */ bool dvmMterpStd(Thread* self, InterpState* glue) { int changeInterp; /* configure mterp items */ glue->self = self; glue->methodClassDex = glue->method->clazz->pDvmDex; glue->interpStackEnd = self->interpStackEnd; glue->pSelfSuspendCount = &self->suspendCount; glue->cardTable = gDvm.biasedCardTableBase; #if defined(WITH_JIT) glue->pJitProfTable = gDvmJit.pProfTable; glue->ppJitProfTable = &gDvmJit.pProfTable; glue->jitThreshold = gDvmJit.threshold; #endif if (gDvm.jdwpConfigured) { glue->pDebuggerActive = &gDvm.debuggerActive; } else { glue->pDebuggerActive = NULL; } glue->pActiveProfilers = &gDvm.activeProfilers; IF_LOGVV() { char* desc = dexProtoCopyMethodDescriptor(&glue->method->prototype); LOGVV("mterp threadid=%d entry %d: %s.%s %s\n", dvmThreadSelf()->threadId, glue->entryPoint, glue->method->clazz->descriptor, glue->method->name, desc); free(desc); } //LOGI("glue is %p, pc=%p, fp=%p\n", glue, glue->pc, glue->fp); //LOGI("first instruction is 0x%04x\n", glue->pc[0]); changeInterp = dvmMterpStdRun(glue); #if defined(WITH_JIT) if (glue->jitState != kJitSingleStep) { glue->self->inJitCodeCache = NULL; } #endif if (!changeInterp) { /* this is a "normal" exit; we're not coming back */ #ifdef LOG_INSTR LOGD("|-- Leaving interpreter loop"); #endif return false; } else { /* we're "standard", so switch to "debug" */ LOGVV(" mterp returned, changeInterp=%d\n", changeInterp); glue->nextMode = INTERP_DBG; return true; } }
/* * Resolve an instance field reference. * * Returns NULL and throws an exception on error (no such field, illegal * access). */ InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; InstField* resField; LOGVV("--- resolving field %u (referrer=%s cl=%p)", ifieldIdx, referrer->descriptor, referrer->classLoader); pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindInstanceFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowNoSuchFieldError( dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * Class must be initialized by now (unless verifier is buggy). We * could still be in the process of initializing it if the field * access is from a static initializer. */ assert(dvmIsClassInitialized(resField->clazz) || dvmIsClassInitializing(resField->clazz)); /* * The class is initialized (or initializing), the field has been * found. Add a pointer to our data structure so we don't have to * jump through the hoops again. * * Anything that uses the resolved table entry must have an instance * of the class, so any class init activity has already happened (or * been deliberately bypassed when <clinit> created an instance). * So it's always okay to update the table. */ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField); LOGVV(" field %u is %s.%s", ifieldIdx, resField->clazz->descriptor, resField->name); return resField; }
/* * Find a class by name, initializing it if requested. */ ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader, bool doInit) { ClassObject* clazz = NULL; char* name = NULL; char* descriptor = NULL; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); goto bail; } name = dvmCreateCstrFromString(nameObj); /* * We need to validate and convert the name (from x.y.z to x/y/z). This * is especially handy for array types, since we want to avoid * auto-generating bogus array classes. */ if (!validateClassName(name)) { LOGW("dvmFindClassByName rejecting '%s'\n", name); dvmThrowException("Ljava/lang/ClassNotFoundException;", name); goto bail; } descriptor = dvmDotToDescriptor(name); if (descriptor == NULL) { goto bail; } if (doInit) clazz = dvmFindClass(descriptor, loader); else clazz = dvmFindClassNoInit(descriptor, loader); if (clazz == NULL) { LOGVV("FAIL: load %s (%d)\n", descriptor, doInit); Thread* self = dvmThreadSelf(); Object* oldExcep = dvmGetException(self); dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */ dvmClearException(self); dvmThrowChainedException("Ljava/lang/ClassNotFoundException;", name, oldExcep); dvmReleaseTrackedAlloc(oldExcep, self); } else { LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n", descriptor, doInit, clazz, clazz->classLoader); } bail: free(name); free(descriptor); return clazz; }
/* return the number of bytes to increase the bytecode pointer by */ s4 dvmJitHandlePackedSwitch(const s4* entries, s4 firstKey, u2 size, s4 testVal) { if (testVal < firstKey || testVal >= firstKey + size) { LOGVV("Value %d not found in switch (%d-%d)", testVal, firstKey, firstKey+size-1); return 2*3;//bytecode packed_switch is 6(2*3) bytes long } LOGVV("Value %d found in slot %d (goto 0x%02x)", testVal, testVal - firstKey, s4FromSwitchData(&entries[testVal - firstKey])); return 2*s4FromSwitchData(&entries[testVal - firstKey]); //convert from u2 to byte }
/* * Call this repeatedly, with successively higher values for "iteration", * to sleep for a period of time not to exceed "maxTotalSleep". * * For example, when called with iteration==0 we will sleep for a very * brief time. On the next call we will sleep for a longer time. When * the sum total of all sleeps reaches "maxTotalSleep", this returns false. * * The initial start time value for "relStartTime" MUST come from the * dvmGetRelativeTimeUsec call. On the device this must come from the * monotonic clock source, not the wall clock. * * This should be used wherever you might be tempted to call sched_yield() * in a loop. The problem with sched_yield is that, for a high-priority * thread, the kernel might not actually transfer control elsewhere. * * Returns "false" if we were unable to sleep because our time was up. */ bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime) { /* * Minimum sleep is one millisecond, it is important to keep this value * low to ensure short GC pauses since dvmSuspendAllThreads() uses this * function. */ const int minSleep = 1000; u8 curTime; int curDelay; /* * Get current time, and see if we've already exceeded the limit. */ curTime = dvmGetRelativeTimeUsec(); if (curTime >= relStartTime + maxTotalSleep) { LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)", relStartTime, maxTotalSleep, curTime); return false; } /* * Compute current delay. We're bounded by "maxTotalSleep", so no * real risk of overflow assuming "usleep" isn't returning early. * (Besides, 2^30 usec is about 18 minutes by itself.) * * For iteration==0 we just call sched_yield(), so the first sleep * at iteration==1 is actually (minSleep * 2). */ curDelay = minSleep; while (iteration-- > 0) curDelay *= 2; assert(curDelay > 0); if (curTime + curDelay >= relStartTime + maxTotalSleep) { LOGVV("exsl: reduced delay from %d to %d", curDelay, (int) ((relStartTime + maxTotalSleep) - curTime)); curDelay = (int) ((relStartTime + maxTotalSleep) - curTime); } if (iteration == 0) { LOGVV("exsl: yield"); sched_yield(); } else { LOGVV("exsl: sleep for %d", curDelay); usleep(curDelay); } return true; }
/* * Convert the method signature to an array of classes. * * The tokenization process may mangle "*pSignature". On return, it will * be pointing at the closing ')'. * * "defClass" is the method's class, which is needed to make class loaders * happy. */ static ArrayObject* convertSignatureToClassArray(char** pSignature, ClassObject* defClass) { char* signature = *pSignature; assert(*signature == '('); signature++; /* count up the number of parameters */ size_t count = 0; char* cp = signature; while (*cp != ')') { count++; if (*cp == '[') { while (*++cp == '[') ; } if (*cp == 'L') { while (*++cp != ';') ; } cp++; } LOGVV("REFLECT found %d parameters in '%s'", count, *pSignature); /* create an array to hold them */ ArrayObject* classArray = dvmAllocArrayByClass(gDvm.classJavaLangClassArray, count, ALLOC_DEFAULT); if (classArray == NULL) return NULL; /* fill it in */ cp = signature; for (size_t i = 0; i < count; i++) { ClassObject* clazz = convertSignaturePartToClass(&cp, defClass); if (clazz == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } LOGVV("REFLECT %d: '%s'", i, clazz->descriptor); dvmSetObjectArrayElement(classArray, i, (Object *)clazz); } *pSignature = cp; /* caller must call dvmReleaseTrackedAlloc */ return classArray; }
/* * Resolve an instance field reference. * * Returns NULL and throws an exception on error (no such field, illegal * access). */ InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; InstField* resField; LOGVV("--- resolving field %u (referrer=%s cl=%p)\n", ifieldIdx, referrer->descriptor, referrer->classLoader); pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindInstanceFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldError;", dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * Class must be initialized by now (unless verifier is buggy). We * could still be in the process of initializing it if the field * access is from a static initializer. */ assert(dvmIsClassInitialized(resField->field.clazz) || dvmIsClassInitializing(resField->field.clazz)); /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField); LOGVV(" field %u is %s.%s\n", ifieldIdx, resField->field.clazz->descriptor, resField->field.name); return resField; }
/* * static Class findLoadedClass(ClassLoader cl, String name) */ static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args, JValue* pResult) { Object* loader = (Object*) args[0]; StringObject* nameObj = (StringObject*) args[1]; ClassObject* clazz = NULL; char* name = NULL; char* descriptor = NULL; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); goto bail; } /* * Get a UTF-8 copy of the string, and convert dots to slashes. */ name = dvmCreateCstrFromString(nameObj); if (name == NULL) goto bail; descriptor = dvmDotToDescriptor(name); if (descriptor == NULL) goto bail; clazz = dvmLookupClass(descriptor, loader, false); LOGVV("look: %s ldr=%p --> %p\n", descriptor, loader, clazz); bail: free(name); free(descriptor); RETURN_PTR(clazz); }
/* * "Mterp entry point. */ void dvmMterpStd(Thread* self) { /* configure mterp items */ self->interpSave.methodClassDex = self->interpSave.method->clazz->pDvmDex; IF_LOGVV() { char* desc = dexProtoCopyMethodDescriptor( &self->interpSave.method->prototype); LOGVV("mterp threadid=%d : %s.%s %s", dvmThreadSelf()->threadId, self->interpSave.method->clazz->descriptor, self->interpSave.method->name, desc); free(desc); } //ALOGI("self is %p, pc=%p, fp=%p", self, self->interpSave.pc, // self->interpSave.curFrame); //ALOGI("first instruction is 0x%04x", self->interpSave.pc[0]); /* * Handle any ongoing profiling and prep for debugging */ if (self->interpBreak.ctl.subMode != 0) { TRACE_METHOD_ENTER(self, self->interpSave.method); self->debugIsMethodEntry = true; // Always true on startup } dvmMterpStdRun(self); #ifdef LOG_INSTR ALOGD("|-- Leaving interpreter loop"); #endif }
/* * Create an instance of the specified class. * * Returns NULL and throws an exception on failure. */ Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) { flags |= ALLOC_FINALIZABLE; } /* allocate on GC heap; memory is zeroed out */ newObj = dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); Monitor* mon = NULL;//dvmCreateMonitor(newObj); newObj->lock = (u4)mon | LW_SHAPE_FAT; LOGVV("AllocObject: %s (%d)\n", clazz->descriptor, (int) clazz->objectSize); #if WITH_HPROF && WITH_HPROF_STACK hprofFillInStackTrace(newObj); #endif dvmTrackAllocation(clazz, clazz->objectSize); } return newObj; }
/* * 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; }
int ef_vi_free(ef_vi* ep, ef_driver_handle fd) { int rc; if( ep->vi_io_mmap_ptr != NULL ) { rc = ci_resource_munmap(fd, ep->vi_io_mmap_ptr, ep->vi_io_mmap_bytes); if( rc < 0 ) { LOGV(ef_log("%s: ci_resource_munmap %d", __FUNCTION__, rc)); return rc; } } if( ep->vi_mem_mmap_ptr != NULL ) { /* TODO: support variable sized DMAQ and evq */ rc = ci_resource_munmap(fd, ep->vi_mem_mmap_ptr, ep->vi_mem_mmap_bytes); if( rc < 0 ) { LOGVV(ef_log("%s: ci_resource_munmap iobuffer %d", __FUNCTION__, rc)); return rc; } } free(ep->ep_state); EF_VI_DEBUG(memset(ep, 0, sizeof(*ep))); LOGVVV(ef_log("%s: DONE", __FUNCTION__)); return 0; }
/* * Resolve a native method and invoke it. * * This is executed as if it were a native bridge or function. If the * resolution succeeds, method->insns is replaced, and we don't go through * here again. * * Initializes method's class if necessary. * * An exception is thrown on resolution failure. */ void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) { ClassObject* clazz = method->clazz; void* func; /* * If this is a static method, it could be called before the class * has been initialized. */ if (dvmIsStaticMethod(method)) { if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { assert(dvmCheckException(dvmThreadSelf())); return; } } else { assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); } /* start with our internal-native methods */ func = dvmLookupInternalNativeMethod(method); if (func != NULL) { /* resolution always gets the same answer, so no race here */ IF_LOGVV() { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGVV("+++ resolved native %s.%s %s, invoking\n", clazz->descriptor, method->name, desc); free(desc); }
/* * Initialize JDWP stuff. * * Allocates a new state structure. If "port" is non-negative, this also * tries to bind to a listen port. If "port" is less than zero, we assume * we're preparing for an outbound connection, and return without binding * to anything. * * This may be called several times if we're probing for a port. * * Returns 0 on success. */ static JdwpNetState* netStartup(short port) { JdwpNetState* netState; int one = 1; netState = (JdwpNetState*) malloc(sizeof(*netState)); memset(netState, 0, sizeof(*netState)); netState->listenSock = -1; netState->clientSock = -1; netState->wakePipe[0] = -1; netState->wakePipe[1] = -1; if (port < 0) return netState; assert(port != 0); netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (netState->listenSock < 0) { LOGE("Socket create failed: %s\n", strerror(errno)); goto fail; } /* allow immediate re-use */ if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { LOGE("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno)); goto fail; } union { struct sockaddr_in addrInet; struct sockaddr addrPlain; } addr; addr.addrInet.sin_family = AF_INET; addr.addrInet.sin_port = htons(port); inet_aton("127.0.0.1", &addr.addrInet.sin_addr); if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) { LOGV("attempt to bind to port %u failed: %s\n", port, strerror(errno)); goto fail; } netState->listenPort = port; LOGVV("+++ bound to port %d\n", netState->listenPort); if (listen(netState->listenSock, 5) != 0) { LOGE("Listen failed: %s\n", strerror(errno)); goto fail; } return netState; fail: netShutdown(netState); netFree(netState); return NULL; }
/* * 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; }
/* * Find the matching case. Returns the offset to the handler instructions. * * Returns 3 if we don't find a match (it's the size of the packed-switch * instruction). */ s4 dvmNcgHandlePackedSwitch(const s4* entries, s4 firstKey, u2 size, s4 testVal) { //skip add_reg_reg (ADD_REG_REG_SIZE) and jump_reg (JUMP_REG_SIZE) const int kInstrLen = 4; //default to next bytecode if (testVal < firstKey || testVal >= firstKey + size) { LOGVV("Value %d not found in switch (%d-%d)", testVal, firstKey, firstKey+size-1); return kInstrLen; } assert(testVal - firstKey >= 0 && testVal - firstKey < size); LOGVV("Value %d found in slot %d (goto 0x%02x)", testVal, testVal - firstKey, s4FromSwitchData(&entries[testVal - firstKey])); return s4FromSwitchData(&entries[testVal - firstKey]); }
/* * Resolve a static field reference. The DexFile format doesn't distinguish * between static and instance field references, so the "resolved" pointer * in the Dex struct will have the wrong type. We trivially cast it here. * * Causes the field's class to be initialized. */ StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; StaticField* resField; pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindStaticFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowNoSuchFieldError( dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * If we're the first to resolve the field in which this class resides, * we need to do it now. Note that, if the field was inherited from * a superclass, it is not necessarily the same as "resClass". */ if (!dvmIsClassInitialized(resField->clazz) && !dvmInitClass(resField->clazz)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } /* * If the class has been initialized, add a pointer to our data structure * so we don't have to jump through the hoops again. If it's still * initializing (i.e. this thread is executing <clinit>), don't do * the store, otherwise other threads could use the field without waiting * for class init to finish. */ if (dvmIsClassInitialized(resField->clazz)) { dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); } else { LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)", resField->clazz->descriptor, resField->name, dvmIsClassInitializing(resField->clazz), dvmIsClassInitialized(resField->clazz)); } return resField; }
/* return the number of bytes to increase the bytecode pointer by */ s4 dvmJitHandleSparseSwitch(const s4* keys, u2 size, s4 testVal) { const s4* entries = keys + size; int i; for (i = 0; i < size; i++) { s4 k = s4FromSwitchData(&keys[i]); if (k == testVal) { LOGVV("Value %d found in entry %d (goto 0x%02x)", testVal, i, s4FromSwitchData(&entries[i])); return 2*s4FromSwitchData(&entries[i]); //convert from u2 to byte } else if (k > testVal) { break; } } LOGVV("Value %d not found in switch", testVal); return 2*3; //bytecode sparse_switch is 6(2*3) bytes long }
/* * Explicitly initiate garbage collection. */ void dvmCollectGarbage(bool collectSoftReferences) { dvmLockHeap(); LOGVV("Explicit GC\n"); dvmCollectGarbageInternal(collectSoftReferences, GC_EXPLICIT); dvmUnlockHeap(); }
/* * 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; }
/* * 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 */
/* * 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; }
/* * Find the matching case. Returns the offset to the handler instructions. * * Returns 3 if we don't find a match (it's the size of the sparse-switch * instruction). */ s4 dvmNcgHandleSparseSwitch(const s4* keys, u2 size, s4 testVal) { const int kInstrLen = 4; //CHECK const s4* entries = keys + size; int i; for (i = 0; i < size; i++) { s4 k = s4FromSwitchData(&keys[i]); if (k == testVal) { LOGVV("Value %d found in entry %d (goto 0x%02x)", testVal, i, s4FromSwitchData(&entries[i])); return s4FromSwitchData(&entries[i]); } else if (k > testVal) { break; } } LOGVV("Value %d not found in switch", testVal); return kInstrLen; }
/* * Map part of a file (from fd's current offset) into a shared, read-only * memory segment. * * On success, returns 0 and fills out "pMap". On failure, returns a nonzero * value and does not disturb "pMap". */ int sysMapFileSegmentInShmem(int fd, off_t start, long length, MemMapping* pMap) { #ifdef HAVE_POSIX_FILEMAP off_t dummy; size_t fileLength, actualLength; off_t actualStart; int adjust; void* memPtr; assert(pMap != NULL); if (getFileStartAndLength(fd, &dummy, &fileLength) < 0) return -1; if (start + length > (long)fileLength) { LOGW("bad segment: st=%d len=%ld flen=%d\n", (int) start, length, (int) fileLength); return -1; } /* adjust to be page-aligned */ adjust = start % DEFAULT_PAGE_SIZE; actualStart = start - adjust; actualLength = length + adjust; memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED, fd, actualStart); if (memPtr == MAP_FAILED) { LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) actualLength, fd, (int) actualStart, strerror(errno)); return -1; } pMap->baseAddr = memPtr; pMap->baseLength = actualLength; pMap->addr = (char*)memPtr + adjust; pMap->length = length; LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n", (int) start, (int) length, pMap->baseAddr, (int) pMap->baseLength, pMap->addr, (int) pMap->length); return 0; #else LOGE("sysMapFileSegmentInShmem not implemented.\n"); return -1; #endif }
/* * Accept a connection. This will block waiting for somebody to show up. * If that's not desirable, use checkConnection() to make sure something * is pending. */ static bool acceptConnection(JdwpState* state) { JdwpNetState* netState = state->netState; union { struct sockaddr_in addrInet; struct sockaddr addrPlain; } addr; socklen_t addrlen; int sock; if (netState->listenSock < 0) return false; /* you're not listening! */ assert(netState->clientSock < 0); /* must not already be talking */ addrlen = sizeof(addr); do { sock = accept(netState->listenSock, &addr.addrPlain, &addrlen); if (sock < 0 && errno != EINTR) { // When we call shutdown() on the socket, accept() returns with // EINVAL. Don't gripe about it. if (errno == EINVAL) LOGVV("accept failed: %s", strerror(errno)); else ALOGE("accept failed: %s", strerror(errno)); return false; } } while (sock < 0); netState->remoteAddr = addr.addrInet.sin_addr; netState->remotePort = ntohs(addr.addrInet.sin_port); ALOGV("+++ accepted connection from %s:%u", inet_ntoa(netState->remoteAddr), netState->remotePort); netState->clientSock = sock; netState->awaitingHandshake = true; netState->inputCount = 0; ALOGV("Setting TCP_NODELAY on accepted socket"); setNoDelay(netState->clientSock); if (pipe(netState->wakePipe) < 0) { ALOGE("pipe failed"); return false; } return true; }
/* * Resolve a string reference. * * Finding the string is easy. We need to return a reference to a * java/lang/String object, not a bunch of characters, which means the * first time we get here we need to create an interned string. */ StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx) { DvmDex* pDvmDex = referrer->pDvmDex; StringObject* strObj; StringObject* internStrObj; const char* utf8; u4 utf16Size; LOGVV("+++ resolving string, referrer is %s\n", referrer->descriptor); /* * Create a UTF-16 version so we can trivially compare it to what's * already interned. */ utf8 = dexStringAndSizeById(pDvmDex->pDexFile, stringIdx, &utf16Size); strObj = dvmCreateStringFromCstrAndLength(utf8, utf16Size, ALLOC_DEFAULT); if (strObj == NULL) { /* ran out of space in GC heap? */ assert(dvmCheckException(dvmThreadSelf())); goto bail; } /* * Add it to the intern list. The return value is the one in the * intern list, which (due to race conditions) may or may not be * the one we just created. The intern list is synchronized, so * there will be only one "live" version. * * By requesting an immortal interned string, we guarantee that * the returned object will never be collected by the GC. * * A NULL return here indicates some sort of hashing failure. */ internStrObj = dvmLookupImmortalInternedString(strObj); dvmReleaseTrackedAlloc((Object*) strObj, NULL); strObj = internStrObj; if (strObj == NULL) { assert(dvmCheckException(dvmThreadSelf())); goto bail; } /* save a reference so we can go straight to the object next time */ dvmDexSetResolvedString(pDvmDex, stringIdx, strObj); bail: return strObj; }
/* * Verify that the "cookie" is a DEX file we opened. * * Expects that the hash table will be *unlocked* here. * * If the cookie is invalid, we throw an exception and return "false". */ static bool validateCookie(int cookie) { DexOrJar* pDexOrJar = (DexOrJar*) cookie; LOGVV("+++ dex verifying cookie %p", pDexOrJar); if (pDexOrJar == NULL) return false; u4 hash = cookie; dvmHashTableLock(gDvm.userDexFiles); void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, false); dvmHashTableUnlock(gDvm.userDexFiles); if (result == NULL) { dvmThrowRuntimeException("invalid DexFile cookie"); return false; } return true; }