diff options
author | 2024-09-03 15:37:49 +0000 | |
---|---|---|
committer | 2024-09-17 13:32:04 +0000 | |
commit | 3424f3e76b09ce7d81dd9d1126d8d69a3e26cd1e (patch) | |
tree | f77ff2b15ed2069e5e44550244e4f607483fe8ff | |
parent | 4dad0f3614093667a140963535df5780fec7480b (diff) |
Improve the GC strategy for the class verification fuzzer
Run the GC once every 100 iterations on the device and on host.
The number of executions per second has increased by over 3
times in both cases.
Test: Build the fuzzer for host and device. Run with the same
“-seed=2” flag and for the same time.
Bug: 352721437
Change-Id: I57ad7d7a660ad1a5db0058db042e917d805b273e
-rw-r--r-- | runtime/class_linker.h | 1 | ||||
-rw-r--r-- | tools/fuzzer/libart_verify_classes_fuzzer.cc | 42 |
2 files changed, 40 insertions, 3 deletions
diff --git a/runtime/class_linker.h b/runtime/class_linker.h index bcc0986d18..a60bafd939 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1518,6 +1518,7 @@ class ClassLinker { friend class linker::ImageWriter; // for GetClassRoots friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub + friend class VerifyClassesFuzzerHelper; // for FindDexCacheDataLocked. friend class VMClassLoader; // for LookupClass and FindClassInBaseDexClassLoader. ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for DexLock, and RegisterDexFileLocked ART_FRIEND_TEST(mirror::DexCacheMethodHandlesTest, Open); // for AllocDexCache diff --git a/tools/fuzzer/libart_verify_classes_fuzzer.cc b/tools/fuzzer/libart_verify_classes_fuzzer.cc index d2bf705279..d066ef4b6e 100644 --- a/tools/fuzzer/libart_verify_classes_fuzzer.cc +++ b/tools/fuzzer/libart_verify_classes_fuzzer.cc @@ -32,9 +32,32 @@ #include "verifier/class_verifier.h" #include "well_known_classes.h" +// Global variable to count how many DEX files passed DEX file verification and they were +// registered, since these are the cases for which we would be running the GC. In case of +// scheduling multiple fuzzer jobs, using the ‘-jobs’ flag, this is not shared among the threads. +int skipped_gc_iterations = 0; +// Global variable to call the GC once every maximum number of iterations. +// TODO: These values were obtained from local experimenting. They can be changed after +// further investigation. +static constexpr int kMaxSkipGCIterations = 100; // Global variable to signal LSAN that we are not leaking memory. uint8_t* allocated_signal_stack = nullptr; +namespace art { +// A class to be friends with ClassLinker and access the internal FindDexCacheDataLocked method. +class VerifyClassesFuzzerHelper { + public: + static const ClassLinker::DexCacheData* GetDexCacheData(Runtime* runtime, const DexFile* dex_file) + REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::dex_lock_); + ClassLinker* class_linker = runtime->GetClassLinker(); + const ClassLinker::DexCacheData* cached_data = class_linker->FindDexCacheDataLocked(*dex_file); + return cached_data; + } +}; +} // namespace art + std::string GetDexFileName(const std::string& jar_name) { // The jar files are located in the data directory within the directory of the fuzzer's binary. std::string executable_dir = android::base::GetExecutableDirectory(); @@ -186,11 +209,24 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } } + skipped_gc_iterations++; + + // Delete weak root to the DexCache before removing a DEX file from the cache. This is usually + // handled by the GC, but since we are not calling it every iteration, we need to delete them + // manually. + const art::ClassLinker::DexCacheData* dex_cache_data = + art::VerifyClassesFuzzerHelper::GetDexCacheData(runtime, &dex_file); + soa.Env()->GetVm()->DeleteWeakGlobalRef(soa.Self(), dex_cache_data->weak_root); + + class_linker->RemoveDexFromCaches(dex_file); + // Delete global ref and unload class loader to free RAM. soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), class_loader); - // TODO: Can we run this less frequently? e.g. once every 100 iterations. If this leads to - // OOM errors, maybe combine with a conditional collection if the memory used is more than X. - runtime->GetHeap()->CollectGarbage(/* clear_soft_references */ true); + + if (skipped_gc_iterations == kMaxSkipGCIterations) { + runtime->GetHeap()->CollectGarbage(/* clear_soft_references */ true); + skipped_gc_iterations = 0; + } return 0; } |