Do not preload dex caches for assume-verified.
And completely clear dex caches when in the pruning phase.
The preloading, if requested, will fill it later.
This reduces the time it takes to compile the boot image
extension in memory by over 120ms on aosp_taimen-userdebug
with --compiler-filter=assume-verified. Measured with
adb root && \
adb shell stop && \
adb shell setprop dalvik.vm.boot-image \
'boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof' && \
adb shell setprop dalvik.vm.image-dex2oat-filter \
assume-verified && \
adb shell 'setprop dalvik.vm.extra-opts \
"-Ximage-compiler-option --dump-timings"' && \
adb shell start
# Collect dex2oat lines from logcat.
(To avoid dex2oat crashes, we have to revert
https://android-review.googlesource.com/1240047 and
https://android-review.googlesource.com/1246288 .
TODO: Investigate and fix those crashes.)
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: aosp_taimen-userdebug boots.
Test: boot-framework.art checksums are unchanged with the
default filter, i.e. "verify" (and therefore all the
boot images should be unchanged).
Bug: 119800099
Change-Id: I2de4d672accd2272dd39e403dc08f2d608269fa1
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 90ba613..fbe1c0a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2234,9 +2234,13 @@
class_loader,
dirty_image_objects_.get()));
- // We need to prepare method offsets in the image address space for direct method patching.
+ // We need to prepare method offsets in the image address space for resolving linker patches.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
- if (!image_writer_->PrepareImageAddressSpace(timings_)) {
+ // Do not preload dex caches for "assume-verified". This filter is used for in-memory
+ // compilation of boot image extension; in that scenario it is undesirable to use a lot
+ // of time to look up things now in hope it will be somewhat useful later.
+ bool preload_dex_caches = !compiler_options_->AssumeDexFilesAreVerified();
+ if (!image_writer_->PrepareImageAddressSpace(preload_dex_caches, timings_)) {
LOG(ERROR) << "Failed to prepare image address space.";
return false;
}
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index ef296fc..303c262 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -276,7 +276,8 @@
ASSERT_TRUE(cur_opened_dex_files.empty());
}
}
- bool image_space_ok = writer->PrepareImageAddressSpace(&timings);
+ bool image_space_ok =
+ writer->PrepareImageAddressSpace(/*preload_dex_caches=*/ true, &timings);
ASSERT_TRUE(image_space_ok);
DCHECK_EQ(out_helper.vdex_files.size(), out_helper.oat_files.size());
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index af8c50b..9efcf16 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -237,7 +237,7 @@
Runtime::Current()->GetHeap()->VisitObjects(visitor);
}
-bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) {
+bool ImageWriter::PrepareImageAddressSpace(bool preload_dex_caches, TimingLogger* timings) {
target_ptr_size_ = InstructionSetPointerSize(compiler_options_.GetInstructionSet());
Thread* const self = Thread::Current();
@@ -277,7 +277,7 @@
Runtime::Current()->GetInternTable()->PromoteWeakToStrong();
}
- {
+ if (preload_dex_caches) {
TimingLogger::ScopedTiming t("PreloadDexCaches", timings);
// Preload deterministic contents to the dex cache arrays we're going to write.
ScopedObjectAccess soa(self);
@@ -1235,75 +1235,40 @@
Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor);
}
-void ImageWriter::PruneDexCache(ObjPtr<mirror::DexCache> dex_cache,
- ObjPtr<mirror::ClassLoader> class_loader) {
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- const DexFile& dex_file = *dex_cache->GetDexFile();
- // Prune methods.
- dex::TypeIndex last_class_idx; // Initialized to invalid index.
- ObjPtr<mirror::Class> last_class = nullptr;
+void ImageWriter::ClearDexCache(ObjPtr<mirror::DexCache> dex_cache) {
+ // Clear methods.
mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
for (size_t slot_idx = 0, num = dex_cache->NumResolvedMethods(); slot_idx != num; ++slot_idx) {
auto pair =
mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_);
- uint32_t stored_index = pair.index;
- ArtMethod* method = pair.object;
- if (method == nullptr) {
- continue; // Empty entry.
- }
- // Check if the referenced class is in the image. Note that we want to check the referenced
- // class rather than the declaring class to preserve the semantics, i.e. using a MethodId
- // results in resolving the referenced class and that can for example throw OOME.
- const dex::MethodId& method_id = dex_file.GetMethodId(stored_index);
- if (method_id.class_idx_ != last_class_idx) {
- last_class_idx = method_id.class_idx_;
- last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
- if (last_class != nullptr && !KeepClass(last_class)) {
- last_class = nullptr;
- }
- }
- if (last_class == nullptr) {
- dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
+ if (pair.object != nullptr) {
+ dex_cache->ClearResolvedMethod(pair.index, target_ptr_size_);
}
}
- // Prune fields.
+ // Clear fields.
mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
- last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
- last_class = nullptr;
for (size_t slot_idx = 0, num = dex_cache->NumResolvedFields(); slot_idx != num; ++slot_idx) {
auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_);
- uint32_t stored_index = pair.index;
- ArtField* field = pair.object;
- if (field == nullptr) {
- continue; // Empty entry.
- }
- // Check if the referenced class is in the image. Note that we want to check the referenced
- // class rather than the declaring class to preserve the semantics, i.e. using a FieldId
- // results in resolving the referenced class and that can for example throw OOME.
- const dex::FieldId& field_id = dex_file.GetFieldId(stored_index);
- if (field_id.class_idx_ != last_class_idx) {
- last_class_idx = field_id.class_idx_;
- last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
- if (last_class != nullptr && !KeepClass(last_class)) {
- last_class = nullptr;
- }
- }
- if (last_class == nullptr) {
- dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
+ if (pair.object != nullptr) {
+ dex_cache->ClearResolvedField(pair.index, target_ptr_size_);
}
}
- // Prune types.
+ // Clear types.
for (size_t slot_idx = 0, num = dex_cache->NumResolvedTypes(); slot_idx != num; ++slot_idx) {
mirror::TypeDexCachePair pair =
dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed);
- uint32_t stored_index = pair.index;
- ObjPtr<mirror::Class> klass = pair.object.Read();
- if (klass != nullptr && !KeepClass(klass)) {
- dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+ if (!pair.object.IsNull()) {
+ dex_cache->ClearResolvedType(dex::TypeIndex(pair.index));
}
}
- // Strings do not need pruning.
+ // Clear strings.
+ for (size_t slot_idx = 0, num = dex_cache->NumStrings(); slot_idx != num; ++slot_idx) {
+ mirror::StringDexCachePair pair =
+ dex_cache->GetStrings()[slot_idx].load(std::memory_order_relaxed);
+ if (!pair.object.IsNull()) {
+ dex_cache->ClearString(dex::StringIndex(pair.index));
+ }
+ }
}
void ImageWriter::PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
@@ -1443,13 +1408,10 @@
VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
}
- // Clear references to removed classes from the DexCaches.
+ // Completely clear DexCaches. They shall be re-filled in PreloadDexCaches if requested.
std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
- // Pass the class loader associated with the DexCache. This can either be
- // the app's `class_loader` or `nullptr` if boot class loader.
- bool is_app_image_dex_cache = compiler_options_.IsAppImage() && IsImageDexCache(dex_cache);
- PruneDexCache(dex_cache, is_app_image_dex_cache ? GetAppClassLoader() : nullptr);
+ ClearDexCache(dex_cache);
}
// Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 8cd306f..22fd8f4 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -101,7 +101,7 @@
* image have been initialized and all native methods have been generated. In
* addition, no other thread should be modifying the heap.
*/
- bool PrepareImageAddressSpace(TimingLogger* timings);
+ bool PrepareImageAddressSpace(bool preload_dex_caches, TimingLogger* timings);
bool IsImageAddressSpaceReady() const {
DCHECK(!image_infos_.empty());
@@ -450,10 +450,9 @@
// Remove unwanted classes from various roots.
void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_);
- // Remove unwanted classes from the DexCache roots.
- void PruneDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::classlinker_classes_lock_);
+ // Remove everything from the DexCache.
+ void ClearDexCache(ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Preload deterministic DexCache contents.
void PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)