Commits

Anonymous committed fe6d8fc

Preload DexCaches

Bug: 11045348
Change-Id: I287703eaa964dcbd2e8d5a6c7db7ac378e19e2c1

Comments (0)

Files changed (1)

vm/native/dalvik_system_VMRuntime.cpp

  * dalvik.system.VMRuntime
  */
 #include "Dalvik.h"
-#include "alloc/HeapSource.h"
 #include "ScopedPthreadMutexLock.h"
+#include "UniquePtr.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Visit.h"
+#include "libdex/DexClass.h"
 #include "native/InternalNativePriv.h"
 
 #include <limits.h>
 
+#include <map>
 
 /*
  * public native float getTargetHeapUtilization()
   RETURN_VOID();
 }
 
+static DvmDex* getDvmDexFromClassPathEntry(ClassPathEntry* cpe) {
+    if (cpe->kind == kCpeDex) {
+        return ((RawDexFile*) cpe->ptr)->pDvmDex;
+    }
+    if (cpe->kind == kCpeJar) {
+        return ((JarFile*) cpe->ptr)->pDvmDex;
+    }
+    LOG_ALWAYS_FATAL("Unknown cpe->kind=%d", cpe->kind);
+}
+
+typedef std::map<std::string, StringObject*> StringTable;
+
+static void preloadDexCachesStringsVisitor(void* addr, u4 threadId, RootType type, void* arg) {
+    StringTable& table = *(StringTable*) arg;
+    StringObject* strObj = *(StringObject**) addr;
+    LOG_FATAL_IF(strObj->clazz != gDvm.classJavaLangString, "Unknown class for supposed string");
+    char* newStr = dvmCreateCstrFromString(strObj);
+    // ALOGI("VMRuntime.preloadDexCaches interned=%s", newStr);
+    table[newStr] = strObj;
+    free(newStr);
+}
+
+// Based on dvmResolveString.
+static void preloadDexCachesResolveString(DvmDex* pDvmDex,
+                                          uint32_t stringIdx,
+                                          StringTable& strings) {
+    StringObject* string = dvmDexGetResolvedString(pDvmDex, stringIdx);
+    if (string != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    uint32_t utf16Size;
+    const char* utf8 = dexStringAndSizeById(pDexFile, stringIdx, &utf16Size);
+    string = strings[utf8];
+    if (string == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found string=%s", utf8);
+    dvmDexSetResolvedString(pDvmDex, stringIdx, string);
+}
+
+// Based on dvmResolveClass.
+static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) {
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx);
+    if (clazz != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const char* className = dexStringByTypeIdx(pDexFile, typeIdx);
+    if (className[0] != '\0' && className[1] == '\0') {
+        /* primitive type */
+        clazz = dvmFindPrimitiveClass(className[0]);
+    } else {
+        clazz = dvmLookupClass(className, NULL, true);
+    }
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip uninitialized classes because filled cache entry implies it is initialized.
+    if (!dvmIsClassInitialized(clazz)) {
+        // ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className);
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className);
+    dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz);
+}
+
+// Based on dvmResolveInstField/dvmResolveStaticField.
+static void preloadDexCachesResolveField(DvmDex* pDvmDex, uint32_t fieldIdx, bool instance) {
+    Field* field = dvmDexGetResolvedField(pDvmDex, fieldIdx);
+    if (field != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const DexFieldId* pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pFieldId->classIdx);
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip static fields for uninitialized classes because a filled
+    // cache entry implies the class is initialized.
+    if (!instance && !dvmIsClassInitialized(clazz)) {
+        return;
+    }
+    const char* fieldName = dexStringById(pDexFile, pFieldId->nameIdx);
+    const char* signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    if (instance) {
+        field = dvmFindInstanceFieldHier(clazz, fieldName, signature);
+    } else {
+        field = dvmFindStaticFieldHier(clazz, fieldName, signature);
+    }
+    if (field == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found field %s %s.%s",
+    //       signature, clazz->descriptor, fieldName);
+    dvmDexSetResolvedField(pDvmDex, fieldIdx, field);
+}
+
+// Based on dvmResolveMethod.
+static void preloadDexCachesResolveMethod(DvmDex* pDvmDex,
+                                          uint32_t methodIdx,
+                                          MethodType methodType) {
+    Method* method = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (method != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pMethodId->classIdx);
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip static methods for uninitialized classes because a filled
+    // cache entry implies the class is initialized.
+    if ((methodType != METHOD_STATIC) && !dvmIsClassInitialized(clazz)) {
+        return;
+    }
+    const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+
+    if (methodType == METHOD_DIRECT) {
+        method = dvmFindDirectMethod(clazz, methodName, &proto);
+    } else if (methodType == METHOD_STATIC) {
+        method = dvmFindDirectMethodHier(clazz, methodName, &proto);
+    } else {
+        method = dvmFindVirtualMethodHier(clazz, methodName, &proto);
+    }
+    if (method == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found method %s.%s",
+    //        clazz->descriptor, methodName);
+    dvmDexSetResolvedMethod(pDvmDex, methodIdx, method);
+}
+
+struct DexCacheStats {
+    uint32_t numStrings;
+    uint32_t numTypes;
+    uint32_t numFields;
+    uint32_t numMethods;
+    DexCacheStats() : numStrings(0), numTypes(0), numFields(0), numMethods(0) {};
+};
+
+static const bool kPreloadDexCachesEnabled = true;
+
+// Disabled because it takes a long time (extra half second) but
+// gives almost no benefit in terms of saving private dirty pages.
+static const bool kPreloadDexCachesStrings = false;
+
+static const bool kPreloadDexCachesTypes = true;
+static const bool kPreloadDexCachesFieldsAndMethods = true;
+
+static const bool kPreloadDexCachesCollectStats = false;
+
+static void preloadDexCachesStatsTotal(DexCacheStats* total) {
+    if (!kPreloadDexCachesCollectStats) {
+        return;
+    }
+
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        total->numStrings += pHeader->stringIdsSize;
+        total->numFields += pHeader->fieldIdsSize;
+        total->numMethods += pHeader->methodIdsSize;
+        total->numTypes += pHeader->typeIdsSize;
+    }
+}
+
+static void preloadDexCachesStatsFilled(DexCacheStats* filled) {
+    if (!kPreloadDexCachesCollectStats) {
+        return;
+    }
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
+            StringObject* string = dvmDexGetResolvedString(pDvmDex, i);
+            if (string != NULL) {
+                filled->numStrings++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
+            ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, i);
+            if (clazz != NULL) {
+                filled->numTypes++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->fieldIdsSize; i++) {
+            Field* field = dvmDexGetResolvedField(pDvmDex, i);
+            if (field != NULL) {
+                filled->numFields++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->methodIdsSize; i++) {
+            Method* method = dvmDexGetResolvedMethod(pDvmDex, i);
+            if (method != NULL) {
+                filled->numMethods++;
+            }
+        }
+    }
+}
+
+static void Dalvik_dalvik_system_VMRuntime_preloadDexCaches(const u4* args, JValue* pResult)
+{
+    if (!kPreloadDexCachesEnabled) {
+        return;
+    }
+
+    DexCacheStats total;
+    DexCacheStats before;
+    if (kPreloadDexCachesCollectStats) {
+        ALOGI("VMRuntime.preloadDexCaches starting");
+        preloadDexCachesStatsTotal(&total);
+        preloadDexCachesStatsFilled(&before);
+    }
+
+    // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
+    StringTable strings;
+    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();
+}
+
 const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
     { "addressOf", "(Ljava/lang/Object;)J",
         Dalvik_dalvik_system_VMRuntime_addressOf },
         Dalvik_dalvik_system_VMRuntime_registerNativeAllocation },
     { "registerNativeFree", "(I)V",
         Dalvik_dalvik_system_VMRuntime_registerNativeFree },
+    { "preloadDexCaches", "()V",
+        Dalvik_dalvik_system_VMRuntime_preloadDexCaches },
     { NULL, NULL, NULL },
 };
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.