Create vdex file for dex loaded with InMemoryDexClassLoader
Previous CL introduced a background verification thread for dex bytecode
loaded with InMemoryDexClassLoader. Extend the logic to collect the
results of class verification into an instance of VerifierDeps and dump
it into a vdex file in the app's data folder.
The background thread does not collect full VerifierDeps (e.g.
assignability dependencies, etc), just a bit vector of whether a class
was successfully verified or not.
The vdex format is extended to include boot classpath checksums and the
class loader context it was created for. These are optional and
currently left empty for regular vdex files.
The generated vdex files are treated as a cache with a limited capacity,
currently capped at 8 files. The least recently used file (in terms of
atime reported by stat()) is unlinked if the cache is full and a new
vdex is about to be generated.
Bug: 72131483
Test: art/tools/run-libcore-tests.sh
Test: art/test.py -b -r -t 692 -t 693
Change-Id: I26080d894d34d8f35f00c7925db569f22f008d2c
diff --git a/test/692-vdex-inmem-loader/expected.txt b/test/692-vdex-inmem-loader/expected.txt
index 0990d72..a127604 100644
--- a/test/692-vdex-inmem-loader/expected.txt
+++ b/test/692-vdex-inmem-loader/expected.txt
@@ -1,3 +1,4 @@
JNI_OnLoad called
Hello
Hello
+Hello
diff --git a/test/692-vdex-inmem-loader/src/Main.java b/test/692-vdex-inmem-loader/src/Main.java
index b3a5e58..75aef8b 100644
--- a/test/692-vdex-inmem-loader/src/Main.java
+++ b/test/692-vdex-inmem-loader/src/Main.java
@@ -40,9 +40,15 @@
return new ClassLoader[] { clA, clB };
}
- private static void test(ClassLoader loader, boolean invokeMethod) throws Exception {
+ private static void test(ClassLoader loader,
+ boolean expectedHasVdexFile,
+ boolean invokeMethod) throws Exception {
+ // If ART created a vdex file, it must have verified all the classes.
+ boolean expectedClassesVerified = expectedHasVdexFile;
+
waitForVerifier();
- check(!isDebuggable(), areClassesVerified(loader), "areClassesVerified");
+ check(expectedClassesVerified, areClassesVerified(loader), "areClassesVerified");
+ check(expectedHasVdexFile, hasVdexFile(loader), "areClassesVerified");
if (invokeMethod) {
loader.loadClass("art.ClassB").getDeclaredMethod("printHello").invoke(null);
@@ -52,21 +58,31 @@
public static void main(String[] args) throws Exception {
System.loadLibrary(args[0]);
ClassLoader[] loaders = null;
+ boolean featureEnabled = !isDebuggable();
- // Test loading both dex files in a single class loader.
- // Background verification task should verify all their classes.
- test(singleLoader(), /*invokeMethod*/true);
+ // Data directory not set. Background verification job should not have run
+ // and vdex should not have been created.
+ test(singleLoader(), /*hasVdex*/ false, /*invokeMethod*/ true);
+
+ // Set data directory for this process.
+ setProcessDataDir(DEX_LOCATION);
+
+ // Data directory is now set. Background verification job should have run,
+ // should have verified classes and written results to a vdex.
+ test(singleLoader(), /*hasVdex*/ featureEnabled, /*invokeMethod*/ true);
// Test loading the two dex files with separate class loaders.
// Background verification task should still verify all classes.
loaders = multiLoader();
- test(loaders[0], /*invokeMethod*/false);
- test(loaders[1], /*invokeMethod*/true);
+ test(loaders[0], /*hasVdex*/ featureEnabled, /*invokeMethod*/ false);
+ test(loaders[1], /*hasVdex*/ featureEnabled, /*invokeMethod*/ true);
}
private static native boolean isDebuggable();
+ private static native void setProcessDataDir(String path);
private static native void waitForVerifier();
private static native boolean areClassesVerified(ClassLoader loader);
+ private static native boolean hasVdexFile(ClassLoader loader);
// Defined in 674-hiddenapi.
private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
index f064953..a5d09e9 100644
--- a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
+++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
@@ -29,6 +29,12 @@
Runtime::Current()->GetOatFileManager().WaitForBackgroundVerificationTasks();
}
+extern "C" JNIEXPORT void JNICALL Java_Main_setProcessDataDir(JNIEnv* env, jclass, jstring jpath) {
+ const char* path = env->GetStringUTFChars(jpath, nullptr);
+ Runtime::Current()->SetProcessDataDirectory(path);
+ env->ReleaseStringUTFChars(jpath, path);
+}
+
extern "C" JNIEXPORT jboolean JNICALL Java_Main_areClassesVerified(JNIEnv*,
jclass,
jobject loader) {
@@ -68,5 +74,47 @@
return all_verified ? JNI_TRUE : JNI_FALSE;
}
+extern "C" JNIEXPORT bool JNICALL Java_Main_hasVdexFile(JNIEnv*,
+ jclass,
+ jobject loader) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> h_loader = hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader));
+
+ std::vector<const DexFile::Header*> dex_headers;
+ VisitClassLoaderDexFiles(
+ soa,
+ h_loader,
+ [&](const DexFile* dex_file) {
+ dex_headers.push_back(&dex_file->GetHeader());
+ return true;
+ });
+
+ uint32_t location_checksum;
+ std::string dex_location;
+ std::string vdex_filename;
+ std::string error_msg;
+ return OatFileAssistant::AnonymousDexVdexLocation(dex_headers,
+ kRuntimeISA,
+ &location_checksum,
+ &dex_location,
+ &vdex_filename) &&
+ OS::FileExists(vdex_filename.c_str());
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getVdexCacheSize(JNIEnv*, jclass) {
+ return static_cast<jint>(OatFileManager::kAnonymousVdexCacheSize);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAnonymousVdexBasename(JNIEnv* env,
+ jclass,
+ jstring basename) {
+ if (basename == nullptr) {
+ return JNI_FALSE;
+ }
+ ScopedUtfChars basename_utf(env, basename);
+ return OatFileAssistant::IsAnonymousVdexBasename(basename_utf.c_str()) ? JNI_TRUE : JNI_FALSE;
+}
+
} // namespace Test692VdexInmemLoader
} // namespace art