diff options
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r-- | runtime/class_linker.cc | 315 |
1 files changed, 160 insertions, 155 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 8fed3cafad..1343b16193 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1326,14 +1326,16 @@ class CHAOnDeleteUpdateClassVisitor { }; /* - * A class used to ensure that all strings in an AppImage have been properly - * interned, and is only ever run in debug mode. + * A class used to ensure that all references to strings interned in an AppImage have been + * properly recorded in the interned references list, and is only ever run in debug mode. */ -class VerifyStringInterningVisitor { +class CountInternedStringReferencesVisitor { public: - explicit VerifyStringInterningVisitor(const gc::space::ImageSpace& space) : - space_(space), - intern_table_(*Runtime::Current()->GetInternTable()) {} + CountInternedStringReferencesVisitor(const gc::space::ImageSpace& space, + const InternTable::UnorderedSet& image_interns) + : space_(space), + image_interns_(image_interns), + count_(0u) {} void TestObject(ObjPtr<mirror::Object> referred_obj) const REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1341,15 +1343,9 @@ class VerifyStringInterningVisitor { space_.HasAddress(referred_obj.Ptr()) && referred_obj->IsString()) { ObjPtr<mirror::String> referred_str = referred_obj->AsString(); - - if (kIsDebugBuild) { - // Saved to temporary variables to aid in debugging. - ObjPtr<mirror::String> strong_lookup_result = - intern_table_.LookupStrong(Thread::Current(), referred_str); - ObjPtr<mirror::String> weak_lookup_result = - intern_table_.LookupWeak(Thread::Current(), referred_str); - - DCHECK((strong_lookup_result == referred_str) || (weak_lookup_result == referred_str)); + auto it = image_interns_.find(GcRoot<mirror::String>(referred_str)); + if (it != image_interns_.end() && it->Read() == referred_str) { + ++count_; } } } @@ -1372,33 +1368,35 @@ class VerifyStringInterningVisitor { MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { - // There could be overlap between ranges, we must avoid visiting the same reference twice. - // Avoid the class field since we already fixed it up in FixupClassVisitor. - if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) { - // Updating images, don't do a read barrier. - ObjPtr<mirror::Object> referred_obj = - obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset); - - TestObject(referred_obj); - } + // References within image or across images don't need a read barrier. + ObjPtr<mirror::Object> referred_obj = + obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset); + TestObject(referred_obj); } void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, ObjPtr<mirror::Reference> ref) const REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { - operator()(ref, mirror::Reference::ReferentOffset(), false); + operator()(ref, mirror::Reference::ReferentOffset(), /*is_static=*/ false); } + size_t GetCount() const { + return count_; + } + + private: const gc::space::ImageSpace& space_; - InternTable& intern_table_; + const InternTable::UnorderedSet& image_interns_; + mutable size_t count_; // Modified from the `const` callbacks. }; /* - * This function verifies that string references in the AppImage have been - * properly interned. To be considered properly interned a reference must - * point to the same version of the string that the intern table does. + * This function counts references to strings interned in the AppImage. + * This is used in debug build to check against the number of the recorded references. */ -void VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks::mutator_lock_) { +size_t CountInternedStringReferences(gc::space::ImageSpace& space, + const InternTable::UnorderedSet& image_interns) + REQUIRES_SHARED(Locks::mutator_lock_) { const gc::accounting::ContinuousSpaceBitmap* bitmap = space.GetMarkBitmap(); const ImageHeader& image_header = space.GetImageHeader(); const uint8_t* target_base = space.GetMemMap()->Begin(); @@ -1407,7 +1405,7 @@ void VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks:: auto objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); auto objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); - VerifyStringInterningVisitor visitor(space); + CountInternedStringReferencesVisitor visitor(space, image_interns); bitmap->VisitMarkedRange(objects_begin, objects_end, [&space, &visitor](mirror::Object* obj) @@ -1427,6 +1425,119 @@ void VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks:: } } }); + return visitor.GetCount(); +} + +template <typename Visitor> +static void VisitInternedStringReferences( + gc::space::ImageSpace* space, + bool use_preresolved_strings, + const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) { + const uint8_t* target_base = space->Begin(); + const ImageSection& sro_section = + space->GetImageHeader().GetImageStringReferenceOffsetsSection(); + const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo); + + VLOG(image) + << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = " + << num_string_offsets; + + const auto* sro_base = + reinterpret_cast<const AppImageReferenceOffsetInfo*>(target_base + sro_section.Offset()); + + for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) { + uint32_t base_offset = sro_base[offset_index].first; + + if (HasDexCacheStringNativeRefTag(base_offset)) { + base_offset = ClearDexCacheNativeRefTags(base_offset); + DCHECK_ALIGNED(base_offset, 2); + + ObjPtr<mirror::DexCache> dex_cache = + reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset); + uint32_t string_slot_index = sro_base[offset_index].second; + + mirror::StringDexCachePair source = + dex_cache->GetStrings()[string_slot_index].load(std::memory_order_relaxed); + ObjPtr<mirror::String> referred_string = source.object.Read(); + DCHECK(referred_string != nullptr); + + ObjPtr<mirror::String> visited = visitor(referred_string); + if (visited != referred_string) { + // Because we are not using a helper function we need to mark the GC card manually. + WriteBarrier::ForEveryFieldWrite(dex_cache); + dex_cache->GetStrings()[string_slot_index].store( + mirror::StringDexCachePair(visited, source.index), std::memory_order_relaxed); + } + } else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) { + if (use_preresolved_strings) { + base_offset = ClearDexCacheNativeRefTags(base_offset); + DCHECK_ALIGNED(base_offset, 2); + + ObjPtr<mirror::DexCache> dex_cache = + reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset); + uint32_t string_index = sro_base[offset_index].second; + + ObjPtr<mirror::String> referred_string = + dex_cache->GetPreResolvedStrings()[string_index].Read(); + DCHECK(referred_string != nullptr); + + ObjPtr<mirror::String> visited = visitor(referred_string); + if (visited != referred_string) { + // Because we are not using a helper function we need to mark the GC card manually. + WriteBarrier::ForEveryFieldWrite(dex_cache); + dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(visited); + } + } + } else { + uint32_t raw_member_offset = sro_base[offset_index].second; + DCHECK_ALIGNED(base_offset, 2); + DCHECK_ALIGNED(raw_member_offset, 2); + + ObjPtr<mirror::Object> obj_ptr = + reinterpret_cast<mirror::Object*>(space->Begin() + base_offset); + MemberOffset member_offset(raw_member_offset); + ObjPtr<mirror::String> referred_string = + obj_ptr->GetFieldObject<mirror::String, + kVerifyNone, + kWithoutReadBarrier, + /* kIsVolatile= */ false>(member_offset); + DCHECK(referred_string != nullptr); + + ObjPtr<mirror::String> visited = visitor(referred_string); + if (visited != referred_string) { + obj_ptr->SetFieldObject</* kTransactionActive= */ false, + /* kCheckTransaction= */ false, + kVerifyNone, + /* kIsVolatile= */ false>(member_offset, visited); + } + } + } +} + +static void VerifyInternedStringReferences(gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_) { + InternTable::UnorderedSet image_interns; + const ImageSection& section = space->GetImageHeader().GetInternedStringsSection(); + if (section.Size() > 0) { + size_t read_count; + const uint8_t* data = space->Begin() + section.Offset(); + InternTable::UnorderedSet image_set(data, /*make_copy_of_data=*/ false, &read_count); + image_set.swap(image_interns); + } + size_t num_recorded_refs = 0u; + VisitInternedStringReferences( + space, + /*use_preresolved_strings=*/ true, + [&image_interns, &num_recorded_refs](ObjPtr<mirror::String> str) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto it = image_interns.find(GcRoot<mirror::String>(str)); + CHECK(it != image_interns.end()); + CHECK(it->Read() == str); + ++num_recorded_refs; + return str; + }); + size_t num_found_refs = CountInternedStringReferences(*space, image_interns); + CHECK_EQ(num_recorded_refs, num_found_refs); } // new_class_set is the set of classes that were read from the class table section in the image. @@ -1445,12 +1556,6 @@ class AppImageLoadingHelper { static void HandleAppImageStrings(gc::space::ImageSpace* space) REQUIRES_SHARED(Locks::mutator_lock_); - - static void UpdateInternStrings( - gc::space::ImageSpace* space, - bool use_preresolved_strings, - const SafeMap<mirror::String*, mirror::String*>& intern_remap) - REQUIRES_SHARED(Locks::mutator_lock_); }; void AppImageLoadingHelper::Update( @@ -1463,6 +1568,12 @@ void AppImageLoadingHelper::Update( REQUIRES_SHARED(Locks::mutator_lock_) { ScopedTrace app_image_timing("AppImage:Updating"); + if (kIsDebugBuild && ClassLinker::kAppImageMayContainStrings) { + // In debug build, verify the string references before applying + // the Runtime::LoadAppImageStartupCache() option. + VerifyInternedStringReferences(space); + } + Thread* const self = Thread::Current(); Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); @@ -1535,10 +1646,6 @@ void AppImageLoadingHelper::Update( if (ClassLinker::kAppImageMayContainStrings) { HandleAppImageStrings(space); - - if (kIsDebugBuild) { - VerifyStringInterning(*space); - } } if (kVerifyArtMethodDeclaringClasses) { @@ -1555,104 +1662,6 @@ void AppImageLoadingHelper::Update( } } -void AppImageLoadingHelper::UpdateInternStrings( - gc::space::ImageSpace* space, - bool use_preresolved_strings, - const SafeMap<mirror::String*, mirror::String*>& intern_remap) { - const uint8_t* target_base = space->Begin(); - const ImageSection& sro_section = - space->GetImageHeader().GetImageStringReferenceOffsetsSection(); - const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo); - InternTable* const intern_table = Runtime::Current()->GetInternTable(); - - VLOG(image) - << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = " - << num_string_offsets; - - const auto* sro_base = - reinterpret_cast<const AppImageReferenceOffsetInfo*>(target_base + sro_section.Offset()); - - for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) { - uint32_t base_offset = sro_base[offset_index].first; - - if (HasDexCacheStringNativeRefTag(base_offset)) { - base_offset = ClearDexCacheNativeRefTags(base_offset); - DCHECK_ALIGNED(base_offset, 2); - - ObjPtr<mirror::DexCache> dex_cache = - reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset); - uint32_t string_index = sro_base[offset_index].second; - - mirror::StringDexCachePair source = dex_cache->GetStrings()[string_index].load(); - ObjPtr<mirror::String> referred_string = source.object.Read(); - DCHECK(referred_string != nullptr); - - auto it = intern_remap.find(referred_string.Ptr()); - if (it != intern_remap.end()) { - // This doesn't use SetResolvedString to maintain consistency with how - // we load the string. The index from the source string must be - // re-used due to the circular nature of the cache. Because we are not - // using a helper function we need to mark the GC card manually. - WriteBarrier::ForEveryFieldWrite(dex_cache); - dex_cache->GetStrings()[string_index].store( - mirror::StringDexCachePair(it->second, source.index)); - } else if (!use_preresolved_strings) { - dex_cache->GetStrings()[string_index].store( - mirror::StringDexCachePair(intern_table->InternStrong(referred_string), source.index)); - } - } else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) { - if (use_preresolved_strings) { - base_offset = ClearDexCacheNativeRefTags(base_offset); - DCHECK_ALIGNED(base_offset, 2); - - ObjPtr<mirror::DexCache> dex_cache = - reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset); - uint32_t string_index = sro_base[offset_index].second; - - ObjPtr<mirror::String> referred_string = - dex_cache->GetPreResolvedStrings()[string_index].Read(); - DCHECK(referred_string != nullptr); - - auto it = intern_remap.find(referred_string.Ptr()); - if (it != intern_remap.end()) { - // Because we are not using a helper function we need to mark the GC card manually. - WriteBarrier::ForEveryFieldWrite(dex_cache); - dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second); - } - } - } else { - uint32_t raw_member_offset = sro_base[offset_index].second; - DCHECK_ALIGNED(base_offset, 2); - DCHECK_ALIGNED(raw_member_offset, 2); - - ObjPtr<mirror::Object> obj_ptr = - reinterpret_cast<mirror::Object*>(space->Begin() + base_offset); - MemberOffset member_offset(raw_member_offset); - ObjPtr<mirror::String> referred_string = - obj_ptr->GetFieldObject<mirror::String, - kVerifyNone, - kWithoutReadBarrier, - /* kIsVolatile= */ false>(member_offset); - DCHECK(referred_string != nullptr); - - auto it = intern_remap.find(referred_string.Ptr()); - if (it != intern_remap.end()) { - obj_ptr->SetFieldObject</* kTransactionActive= */ false, - /* kCheckTransaction= */ false, - kVerifyNone, - /* kIsVolatile= */ false>(member_offset, it->second); - } else if (!use_preresolved_strings) { - obj_ptr->SetFieldObject</* kTransactionActive= */ false, - /* kCheckTransaction= */ false, - kVerifyNone, - /* kIsVolatile= */ false>( - member_offset, - intern_table->InternStrong(referred_string)); - } - } - } -} - void AppImageLoadingHelper::HandleAppImageStrings(gc::space::ImageSpace* space) { // Iterate over the string reference offsets stored in the image and intern // the strings they point to. @@ -1711,23 +1720,19 @@ void AppImageLoadingHelper::HandleAppImageStrings(gc::space::ImageSpace* space) } } }; - - bool update_intern_strings; - if (load_startup_cache) { - VLOG(image) << "AppImage:load_startup_cache"; - // Only add the intern table if we are using the startup cache. Otherwise, - // UpdateInternStrings adds the strings to the intern table. - intern_table->AddImageStringsToTable(space, func); - update_intern_strings = kIsDebugBuild || !intern_remap.empty(); + intern_table->AddImageStringsToTable(space, func); + if (!intern_remap.empty()) { VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size(); - } else { - update_intern_strings = true; - } - - // For debug builds, always run the code below to get coverage. - if (update_intern_strings) { - // Slow path case is when there are conflicting intern strings to fix up. - UpdateInternStrings(space, /*use_preresolved_strings=*/ load_startup_cache, intern_remap); + VisitInternedStringReferences( + space, + load_startup_cache, + [&intern_remap](ObjPtr<mirror::String> str) REQUIRES_SHARED(Locks::mutator_lock_) { + auto it = intern_remap.find(str.Ptr()); + if (it != intern_remap.end()) { + return ObjPtr<mirror::String>(it->second); + } + return str; + }); } } |