/* Returns the TaintPolicyProfile associated with this method * - returns NULL if not found */ TaintProfile* getPolicyProfile(const Method* method) { TaintPolicy* policy = NULL; TaintProfile* profile = NULL; u4 hash; /* temporary variables for the search */ TaintPolicy tPol = {NULL, NULL, NULL}; TaintProfile tProf = {NULL, NULL}; dvmHashTableLock(gPolicyTable); /* Find the class */ hash = dvmComputeUtf8Hash(method->clazz->descriptor); tPol.classDescriptor = method->clazz->descriptor; policy = (TaintPolicy*) dvmHashTableLookup(gPolicyTable, hash, &tPol, hashcmpTaintPolicy, false); if (policy != NULL) { dvmHashTableLock(policy->methodTable); /* Find the Method */ hash = dvmComputeUtf8Hash(method->name); tProf.methodName = method->name; profile = (TaintProfile*) dvmHashTableLookup(policy->methodTable, hash, &tProf, hashcmpTaintProfile, false); dvmHashTableUnlock(policy->methodTable); } dvmHashTableUnlock(gPolicyTable); return profile; }
/* Basically we need to cleanup any state kept around for this thread and let * the other side know the thread is exiting if needed. */ void offThreadExited(Thread* self) { u4 threadId = self->threadId; /* Inform the other endpoint that we're exiting if started locally. We can * avoid this if the other endpoint doesn't even know about this thread. */ if((threadId & 0x8000) == (gDvm.isServer ? 0x8000 : 0)) { if(!self->offLocalOnly) { offWriteU1(self, OFF_ACTION_DEATH); offSyncPush(); ALOGI("THREAD %d WAITING FOR SIGNOFF", threadId); offThreadWaitForResume(self); ALOGI("THREAD %d SIGNING OFF", threadId); } } /* Remove the thread from the thread table. */ if(gDvm.offThreadTable) { dvmHashTableLock(gDvm.offThreadTable); dvmHashTableRemove(gDvm.offThreadTable, threadId, self); dvmHashTableUnlock(gDvm.offThreadTable); } offFlushStream(self); if((threadId & 0x8000) == (gDvm.isServer ? 0x8000 : 0)) { dvmClearBit(gDvm.threadIdMap, ((threadId & 0x7FFF) >> 1) - 1); } else {
void fastiva_dvmScanStatic(Visitor *visitor, void *arg) { HashTable *table = gDvm.loadedClasses; assert(visitor != NULL); assert(table != NULL); dvmHashTableLock(table); for (int i = 0; i < table->tableSize; ++i) { HashEntry *entry = &table->pEntries[i]; if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { #ifdef _DEBUG if (strcmp(((ClassObject*)entry->data)->descriptor, "Lcom/android/location/provider/LocationProviderBase;") == 0) { ALOGD("marking LocationProviderBase %x, %d", ((ClassObject*)entry->data)->accessFlags, i); } #endif (*visitor)(entry->data, arg); } } dvmHashTableUnlock(table); #ifdef _DEBUG ALOGD("fastiva_dvmScanStatic done"); #endif (*visitor)(gDvm.typeVoid, arg); (*visitor)(gDvm.typeBoolean, arg); (*visitor)(gDvm.typeByte, arg); (*visitor)(gDvm.typeShort, arg); (*visitor)(gDvm.typeChar, arg); (*visitor)(gDvm.typeInt, arg); (*visitor)(gDvm.typeLong, arg); (*visitor)(gDvm.typeFloat, arg); (*visitor)(gDvm.typeDouble, arg); }
/* *查询hprof class id. */ hprof_class_object_id hprofLookupClassId(const ClassObject *clazz) { void *val; if (clazz == NULL) { /* Someone's probably looking up the superclass * of java.lang.Object or of a primitive class. */ return (hprof_class_object_id)0; } dvmHashTableLock(gClassHashTable); /* We're using the hash table as a list. * TODO: replace the hash table with a more suitable structure */ val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz), (void *)clazz, classCmp, true); assert(val != NULL); dvmHashTableUnlock(gClassHashTable); /* Make sure that the class's name is in the string table. * This is a bunch of extra work that we only have to do * because of the order of tables in the output file * (strings need to be dumped before classes). */ getPrettyClassNameId(clazz->descriptor); return (hprof_class_object_id)clazz; }
/* * 添加特定的DexOrJar到hash表中 */ static void addToDexFileTable(DexOrJar* pDexOrJar) { /* * Later on, we will receive this pointer as an argument and need * to find it in the hash table without knowing if it's valid or * not, which means we can't compute a hash value from anything * inside DexOrJar. We don't share DexOrJar structs when the same * file is opened multiple times, so we can just use the low 32 * bits of the pointer as the hash. */ u4 hash = (u4) pDexOrJar; void* result; dvmHashTableLock(gDvm.userDexFiles); /* true 代表添加 false 代表不添加*/ result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, true); dvmHashTableUnlock(gDvm.userDexFiles); if (result != pDexOrJar) { ALOGE("Pointer has already been added?"); dvmAbort(); } pDexOrJar->okayToFree = true; }
static Thread* lookupThreadInHash(u4 threadId) { Thread* result; dvmHashTableLock(gDvm.offThreadTable); { // This is a bit of a hack but whatever, it gets the job done without having // to allocate an entire Thread structure. result = (Thread*)dvmHashTableLookup(gDvm.offThreadTable, threadId, ((char*)&threadId) - offsetof(Thread, threadId), compareThreadData, false); } dvmHashTableUnlock(gDvm.offThreadTable); return result; }
/* * Applies a verification function to all present values in the hash table. */ static void visitHashTable(Visitor *visitor, HashTable *table, void *arg) { int i; assert(visitor != NULL); assert(table != NULL); dvmHashTableLock(table); for (i = 0; i < table->tableSize; ++i) { HashEntry *entry = &table->pEntries[i]; if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { (*visitor)(&entry->data, arg); } } dvmHashTableUnlock(table); }
int hprofDumpStacks(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; dvmHashTableLock(gStackTraceHashTable); for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { const StackTraceEntry *stackTraceEntry; int count; int i; hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME); stackTraceEntry = (const StackTraceEntry *) dvmHashIterData(&iter); assert(stackTraceEntry != NULL); /* STACK TRACE format: * * u4: serial number for this stack * u4: serial number for the running thread * u4: number of frames * [ID]*: ID for the stack frame */ hprofAddU4ToRecord(rec, stackTraceEntry->trace.serialNumber); hprofAddU4ToRecord(rec, stackTraceEntry->trace.threadSerialNumber); count = 0; while ((count < STACK_DEPTH) && (stackTraceEntry->trace.frameIds[count] != 0)) { count++; } hprofAddU4ToRecord(rec, count); for (i = 0; i < count; i++) { hprofAddU4ToRecord(rec, stackTraceEntry->trace.frameIds[i]); } } dvmHashTableUnlock(gStackTraceHashTable); return 0; }
/* * Applies a verification function to all present values in the hash table. */ static void visitHashTable(RootVisitor *visitor, HashTable *table, RootType type, void *arg) { assert(visitor != NULL); assert(table != NULL); #ifndef FASTIVA_CONCURRENT_STACK_SCAN dvmHashTableLock(table); #endif for (int i = 0; i < table->tableSize; ++i) { HashEntry *entry = &table->pEntries[i]; if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { (*visitor)(&entry->data, 0, type, arg); } } #ifndef FASTIVA_CONCURRENT_STACK_SCAN dvmHashTableUnlock(table); #endif }
static u4 hprofLookupStackSerialNumber(const StackTraceEntry *stackTrace) { StackTraceEntry *val; u4 hashValue; int serial; /* * Create the hash table on first contact. We can't do this in * hprofStartupStack, because we have to compute stack trace * serial numbers and place them into object headers before the * rest of hprof is triggered by a GC event. */ if (gStackTraceHashTable == NULL) { gStackTraceHashTable = dvmHashTableCreate(512, free); } dvmHashTableLock(gStackTraceHashTable); hashValue = computeStackTraceHash(stackTrace); val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)stackTrace, (HashCompareFunc)stackCmp, false); if (val == NULL) { StackTraceEntry *newStackTrace; newStackTrace = stackDup(stackTrace); newStackTrace->trace.serialNumber = ++gSerialNumber; val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)newStackTrace, (HashCompareFunc)stackCmp, true); assert(val != NULL); } /* Mark the trace as live (in use by an object in the current heap). */ val->live = 1; /* Grab the serial number before unlocking the table. */ serial = val->trace.serialNumber; dvmHashTableUnlock(gStackTraceHashTable); return serial; }
/* * 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; }
int hprofDumpClasses(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; int err; dvmHashTableLock(gClassHashTable); for (err = 0, dvmHashIterBegin(gClassHashTable, &iter); err == 0 && !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME); if (err == 0) { const ClassObject *clazz; clazz = (const ClassObject *)dvmHashIterData(&iter); assert(clazz != NULL); /* LOAD CLASS format: * * u4: class serial number (always > 0) * ID: class object ID * u4: stack trace serial number * ID: class name string ID * * We use the address of the class object structure as its ID. */ hprofAddU4ToRecord(rec, clazz->serialNumber); hprofAddIdToRecord(rec, (hprof_class_object_id)clazz); hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE); hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor)); } } dvmHashTableUnlock(gClassHashTable); return err; }
/* * 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\n", pDexOrJar); if (pDexOrJar == NULL) return false; u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); dvmHashTableLock(gDvm.userDexFiles); void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, false); dvmHashTableUnlock(gDvm.userDexFiles); if (result == NULL) { dvmThrowException("Ljava/lang/RuntimeException;", "invalid DexFile cookie"); return false; } return true; }
/* * private static void closeDexFile(int cookie) * * Release resources associated with a user-loaded DEX file. */ static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args, JValue* pResult) { int cookie = args[0]; DexOrJar* pDexOrJar = (DexOrJar*) cookie; if (pDexOrJar == NULL) RETURN_VOID(); LOGV("Closing DEX file %p (%s)\n", pDexOrJar, pDexOrJar->fileName); if (!validateCookie(cookie)) RETURN_VOID(); /* * We can't just free arbitrary DEX files because they have bits and * pieces of loaded classes. The only exception to this rule is if * they were never used to load classes. * * If we can't free them here, dvmInternalNativeShutdown() will free * them when the VM shuts down. */ if (pDexOrJar->okayToFree) { u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); dvmHashTableLock(gDvm.userDexFiles); if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) { LOGW("WARNING: could not remove '%s' from DEX hash table\n", pDexOrJar->fileName); } dvmHashTableUnlock(gDvm.userDexFiles); LOGV("+++ freeing DexFile '%s' resources\n", pDexOrJar->fileName); dvmFreeDexOrJar(pDexOrJar); } else { LOGV("+++ NOT freeing DexFile '%s' resources\n", pDexOrJar->fileName); } RETURN_VOID(); }
hprof_stack_frame_id hprofLookupStackFrameId(const StackFrameEntry *stackFrameEntry) { StackFrameEntry *val; u4 hashValue; /* * Create the hash table on first contact. We can't do this in * hprofStartupStackFrame, because we have to compute stack trace * serial numbers and place them into object headers before the * rest of hprof is triggered by a GC event. */ if (gStackFrameHashTable == NULL) { gStackFrameHashTable = dvmHashTableCreate(512, free); } dvmHashTableLock(gStackFrameHashTable); hashValue = computeStackFrameHash(stackFrameEntry); val = dvmHashTableLookup(gStackFrameHashTable, hashValue, (void *)stackFrameEntry, (HashCompareFunc)stackFrameCmp, false); if (val == NULL) { const StackFrameEntry *newStackFrameEntry; newStackFrameEntry = stackFrameDup(stackFrameEntry); val = dvmHashTableLookup(gStackFrameHashTable, hashValue, (void *)newStackFrameEntry, (HashCompareFunc)stackFrameCmp, true); assert(val != NULL); } /* Mark the frame as live (in use by an object in the current heap). */ val->live = 1; dvmHashTableUnlock(gStackFrameHashTable); return (hprof_stack_frame_id) val; }
/* Used to propagate taint for JNI methods * Two types of propagation: * 1) simple conservative propagation based on parameters * 2) propagation based on function profile policies */ void dvmTaintPropJniMethod(const u4* args, JValue* pResult, const Method* method, const int nativeTaint) { const DexProto* proto = &method->prototype; DexParameterIterator pIterator; int nParams = dexProtoGetParameterCount(proto); int pStart = (dvmIsStaticMethod(method)?0:1); /* index where params start */ /* Consider 3 arguments. [x] indicates return taint index * 0 1 2 [3] 4 5 6 */ int nArgs = method->insSize; u4* rtaint = (u4*) &args[nArgs]; /* The return taint value */ int tStart = nArgs+1; /* index of args[] where taint values start */ int tEnd = nArgs*2; /* index of args[] where taint values end */ u4 tag = TAINT_CLEAR; int i; #if 0 { char *desc = dexProtoCopyMethodDescriptor(proto); ALOGW("Jni: %s.%s%s, descriptor: %s", method->clazz->descriptor, method->name, dvmIsStaticMethod(method)?"[static]":"", desc); free(desc); } #endif #ifdef TAINT_JNI_LOG /* JNI logging for debugging purposes */ if (gJniLog) { u4 hash; int len; char *inStr, *outStr; len = strlen(method->clazz->descriptor) + 1 + strlen(method->name); inStr = (char*) malloc(len+1); strcpy(inStr, method->clazz->descriptor); strcat(inStr, "."); strcat(inStr, method->name); hash = dvmComputeUtf8Hash(inStr); dvmHashTableLock(gJniLogSeen); outStr = (char*) dvmHashTableLookup(gJniLogSeen, hash, inStr, (HashCompareFunc) strcmp, false); if (outStr == NULL) { /* New method! */ /* add it */ dvmHashTableLookup(gJniLogSeen, hash, inStr, (HashCompareFunc) strcmp, true); } else { free(inStr); /* don't need this anymore */ } dvmHashTableUnlock(gJniLogSeen); } #endif /* Union the taint tags, this includes object ref tags * - we don't need to worry about static vs. not static, because getting * the taint tag on the "this" object reference is a good * - we don't need to worry about wide registers, because the stack * interleaving of taint tags makes it transparent */ /*for (i = tStart; i <= tEnd; i++) { tag |= args[i]; if (args[i]!=0 && args[i]!=TAINT_ACCELEROMETER) //found a taint, and its not the accelerometer spam either ALOGD("dvmTaintPropJNIMethod found taint %08x in method=%s:%s(%s) for arg[%d]=%08x", args[i], method->clazz->descriptor, method->name, method->shorty, i-nArgs, args[i-nArgs]); }*/ /* If not static, pull any taint from the "this" object */ /*if (!dvmIsStaticMethod(method)) { tag |= getObjectTaint((Object*)args[0], method->clazz->descriptor); }*/ /* Union taint from Objects we care about */ dexParameterIteratorInit(&pIterator, proto); for (i=pStart; ; i++) { const char* desc = dexParameterIteratorNextDescriptor(&pIterator); if (desc == NULL) { break; } if (desc[0] == '[' || desc[0] == 'L') { tag |= getObjectTaint((Object*) args[i], desc); } if (desc[0] == 'J' || desc[0] == 'D') { /* wide argument, increment index one more */ i++; } } /* Look at the taint policy profiles (may have return taint) */ //tag |= propMethodProfile(args, method); /* Update return taint according to the return type */ //Native Tainting: get taint Value from native code //if (tag) { if (nativeTaint) { const char* desc = dexProtoGetReturnType(proto); //setReturnTaint(tag, rtaint, pResult, desc); ALOGD("dvmTaintPropJniMethod: setting result taint to %x", nativeTaint); setReturnTaint(nativeTaint, rtaint, pResult, desc); } }
void fastiva_Dalvik_dalvik_system_VMRuntime_preloadDexCaches(dalvik_system_VMRuntime_p self) { #endif if (!kPreloadDexCachesEnabled) { return; } DexCacheStats total; DexCacheStats before; if (kPreloadDexCachesCollectStats) { ALOGI("VMRuntime.preloadDexCaches starting"); preloadDexCachesStatsTotal(&total); preloadDexCachesStatsFilled(&before); } #ifdef FASTIVA return; #endif // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings StringTable strings; if (kPreloadDexCachesStrings) { dvmLockMutex(&gDvm.internLock); dvmHashTableLock(gDvm.literalStrings); for (int i = 0; i < gDvm.literalStrings->tableSize; ++i) { HashEntry *entry = &gDvm.literalStrings->pEntries[i]; if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { preloadDexCachesStringsVisitor(&entry->data, 0, ROOT_INTERNED_STRING, &strings); } } dvmHashTableUnlock(gDvm.literalStrings); dvmUnlockMutex(&gDvm.internLock); } for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) { DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe); const DexHeader* pHeader = pDvmDex->pHeader; const DexFile* pDexFile = pDvmDex->pDexFile; if (kPreloadDexCachesStrings) { for (size_t i = 0; i < pHeader->stringIdsSize; i++) { preloadDexCachesResolveString(pDvmDex, i, strings); } } if (kPreloadDexCachesTypes) { for (size_t i = 0; i < pHeader->typeIdsSize; i++) { preloadDexCachesResolveType(pDvmDex, i); } } if (kPreloadDexCachesFieldsAndMethods) { for (size_t classDefIndex = 0; classDefIndex < pHeader->classDefsSize; classDefIndex++) { const DexClassDef* pClassDef = dexGetClassDef(pDexFile, classDefIndex); const u1* pEncodedData = dexGetClassData(pDexFile, pClassDef); UniquePtr<DexClassData> pClassData(dexReadAndVerifyClassData(&pEncodedData, NULL)); if (pClassData.get() == NULL) { continue; } for (uint32_t fieldIndex = 0; fieldIndex < pClassData->header.staticFieldsSize; fieldIndex++) { const DexField* pField = &pClassData->staticFields[fieldIndex]; preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, false); } for (uint32_t fieldIndex = 0; fieldIndex < pClassData->header.instanceFieldsSize; fieldIndex++) { const DexField* pField = &pClassData->instanceFields[fieldIndex]; preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, true); } for (uint32_t methodIndex = 0; methodIndex < pClassData->header.directMethodsSize; methodIndex++) { const DexMethod* pDexMethod = &pClassData->directMethods[methodIndex]; MethodType methodType = (((pDexMethod->accessFlags & ACC_STATIC) != 0) ? METHOD_STATIC : METHOD_DIRECT); preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, methodType); } for (uint32_t methodIndex = 0; methodIndex < pClassData->header.virtualMethodsSize; methodIndex++) { const DexMethod* pDexMethod = &pClassData->virtualMethods[methodIndex]; preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, METHOD_VIRTUAL); } } } } if (kPreloadDexCachesCollectStats) { DexCacheStats after; preloadDexCachesStatsFilled(&after); ALOGI("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d", total.numStrings, before.numStrings, after.numStrings); ALOGI("VMRuntime.preloadDexCaches types total=%d before=%d after=%d", total.numTypes, before.numTypes, after.numTypes); ALOGI("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d", total.numFields, before.numFields, after.numFields); ALOGI("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d", total.numMethods, before.numMethods, after.numMethods); ALOGI("VMRuntime.preloadDexCaches finished"); } RETURN_VOID(); }
int hprofDumpStackFrames(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; dvmHashTableLock(gStackFrameHashTable); for (dvmHashIterBegin(gStackFrameHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { const StackFrameEntry *stackFrameEntry; const Method *method; int pc; const char *sourceFile; ClassObject *clazz; int lineNum; hprofStartNewRecord(ctx, HPROF_TAG_STACK_FRAME, HPROF_TIME); stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter); assert(stackFrameEntry != NULL); method = stackFrameEntry->frame.method; pc = stackFrameEntry->frame.pc; sourceFile = dvmGetMethodSourceFile(method); if (sourceFile == NULL) { sourceFile = "<unknown>"; lineNum = 0; } else { lineNum = dvmLineNumFromPC(method, pc); } clazz = (ClassObject *) hprofLookupClassId(method->clazz); /* STACK FRAME format: * * ID: ID for this stack frame * ID: ID for the method name * ID: ID for the method descriptor * ID: ID for the source file name * u4: class serial number * u4: line number, 0 = no line information * * We use the address of the stack frame as its ID. */ DexStringCache cache; const char* descriptor; dexStringCacheInit(&cache); descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache); hprofAddIdToRecord(rec, (u4) stackFrameEntry); hprofAddIdToRecord(rec, hprofLookupStringId(method->name)); hprofAddIdToRecord(rec, hprofLookupStringId(descriptor)); hprofAddIdToRecord(rec, hprofLookupStringId(sourceFile)); hprofAddU4ToRecord(rec, (u4) clazz->serialNumber); hprofAddU4ToRecord(rec, (u4) lineNum); dexStringCacheRelease(&cache); } dvmHashTableUnlock(gStackFrameHashTable); return 0; }
/* * private static int openDexFile(String sourceName, String outputName, * int flags) throws IOException * * Open a DEX file, returning a pointer to our internal data structure. * * "sourceName" should point to the "source" jar or DEX file. * * If "outputName" is NULL, the DEX code will automatically find the * "optimized" version in the cache directory, creating it if necessary. * If it's non-NULL, the specified file will be used instead. * * TODO: at present we will happily open the same file more than once. * To optimize this away we could search for existing entries in the hash * table and refCount them. Requires atomic ops or adding "synchronized" * to the non-native code that calls here. */ static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, JValue* pResult) { StringObject* sourceNameObj = (StringObject*) args[0]; StringObject* outputNameObj = (StringObject*) args[1]; int flags = args[2]; DexOrJar* pDexOrJar = NULL; JarFile* pJarFile; RawDexFile* pRawDexFile; char* sourceName; char* outputName; if (sourceNameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } sourceName = dvmCreateCstrFromString(sourceNameObj); if (outputNameObj != NULL) outputName = dvmCreateCstrFromString(outputNameObj); else outputName = NULL; /* * We have to deal with the possibility that somebody might try to * open one of our bootstrap class DEX files. The set of dependencies * will be different, and hence the results of optimization might be * different, which means we'd actually need to have two versions of * the optimized DEX: one that only knows about part of the boot class * path, and one that knows about everything in it. The latter might * optimize field/method accesses based on a class that appeared later * in the class path. * * We can't let the user-defined class loader open it and start using * the classes, since the optimized form of the code skips some of * the method and field resolution that we would ordinarily do, and * we'd have the wrong semantics. * * We have to reject attempts to manually open a DEX file from the boot * class path. The easiest way to do this is by filename, which works * out because variations in name (e.g. "/system/framework/./ext.jar") * result in us hitting a different dalvik-cache entry. It's also fine * if the caller specifies their own output file. */ if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { LOGW("Refusing to reopen boot DEX '%s'\n", sourceName); dvmThrowException("Ljava/io/IOException;", "Re-opening BOOTCLASSPATH DEX files is not allowed"); free(sourceName); RETURN_VOID(); } /* * Try to open it directly as a DEX. If that fails, try it as a Zip * with a "classes.dex" inside. */ if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { LOGV("Opening DEX file '%s' (DEX)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = true; pDexOrJar->pRawDexFile = pRawDexFile; } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { LOGV("Opening DEX file '%s' (Jar)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = false; pDexOrJar->pJarFile = pJarFile; } else { LOGV("Unable to open DEX file '%s'\n", sourceName); dvmThrowException("Ljava/io/IOException;", "unable to open DEX file"); } if (pDexOrJar != NULL) { pDexOrJar->fileName = sourceName; /* add to hash table */ u4 hash = dvmComputeUtf8Hash(sourceName); void* result; dvmHashTableLock(gDvm.userDexFiles); result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, true); dvmHashTableUnlock(gDvm.userDexFiles); if (result != pDexOrJar) { LOGE("Pointer has already been added?\n"); dvmAbort(); } pDexOrJar->okayToFree = true; } else free(sourceName); RETURN_PTR(pDexOrJar); }
static void addThreadToHash(Thread* thread) { dvmHashTableLock(gDvm.offThreadTable); { dvmHashTableLookup(gDvm.offThreadTable, thread->threadId, thread, compareThreadData, true); } dvmHashTableUnlock(gDvm.offThreadTable); }
/* * Some quick hash table tests. */ bool dvmTestHash() { HashTable* pTab; char tmpStr[64]; const char* str; u4 hash; int i; ALOGV("TestHash BEGIN"); pTab = dvmHashTableCreate(dvmHashSize(12), free); if (pTab == NULL) return false; dvmHashTableLock(pTab); /* add some entries */ for (i = 0; i < kNumTestEntries; i++) { sprintf(tmpStr, "entry %d", i); hash = dvmComputeUtf8Hash(tmpStr); dvmHashTableLookup(pTab, hash, strdup(tmpStr), (HashCompareFunc) strcmp, true); } dvmHashTableUnlock(pTab); /* make sure we can find all entries */ for (i = 0; i < kNumTestEntries; i++) { sprintf(tmpStr, "entry %d", i); hash = dvmComputeUtf8Hash(tmpStr); str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr, (HashCompareFunc) strcmp, false); if (str == NULL) { ALOGE("TestHash: failure: could not find '%s'", tmpStr); /* return false */ } } /* make sure it behaves correctly when entry not found and !doAdd */ sprintf(tmpStr, "entry %d", 17); hash = dvmComputeUtf8Hash(tmpStr); str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr, (HashCompareFunc) strcmp, false); if (str == NULL) { /* good */ } else { ALOGE("TestHash found nonexistent string (improper add?)"); } dumpForeach(pTab); dumpIterator(pTab); /* make sure they all get freed */ dvmHashTableFree(pTab); /* * Round 2: verify probing & tombstones. */ pTab = dvmHashTableCreate(dvmHashSize(2), free); if (pTab == NULL) return false; hash = 0; /* two entries, same hash, different values */ const char* str1; str1 = (char*) dvmHashTableLookup(pTab, hash, strdup("one"), (HashCompareFunc) strcmp, true); assert(str1 != NULL); str = (const char*) dvmHashTableLookup(pTab, hash, strdup("two"), (HashCompareFunc) strcmp, true); /* remove the first one */ if (!dvmHashTableRemove(pTab, hash, (void*)str1)) ALOGE("TestHash failed to delete item"); else free((void*)str1); // "Remove" doesn't call the free func /* make sure iterator doesn't included deleted entries */ int count = 0; HashIter iter; for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { count++; } if (count != 1) { ALOGE("TestHash wrong number of entries (%d)", count); } /* see if we can find them */ str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"one", (HashCompareFunc) strcmp,false); if (str != NULL) ALOGE("TestHash deleted entry has returned!"); str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"two", (HashCompareFunc) strcmp,false); if (str == NULL) ALOGE("TestHash entry vanished"); /* force a table realloc to exercise tombstone removal */ for (i = 0; i < 20; i++) { sprintf(tmpStr, "entry %d", i); str = (const char*) dvmHashTableLookup(pTab, hash, strdup(tmpStr), (HashCompareFunc) strcmp, true); assert(str != NULL); } dvmHashTableFree(pTab); ALOGV("TestHash END"); return true; }