diff options
author | 2022-12-23 10:04:16 +0000 | |
---|---|---|
committer | 2022-12-29 07:56:42 +0000 | |
commit | aa124735bb61071f9036e1140355c4d1a088b1a9 (patch) | |
tree | 55b52bc85033b15f39344c11023365a4a0d3a08a | |
parent | cc77cdec4eeb4299efa1a94d942cec99ffe16adb (diff) |
Reland "Add strings from dex caches in runtime app image."
This reverts commit d1faf6d0d72c464753d64f1c92bdd999f69e179e.
Bug: 260557058
Bug: 263016547
Reason for reland: create String::Equals version without ObjPtr
Change-Id: Ib1c0d5c7a0e1e6c8f0cd821eb8ff0d1cb5561559
-rw-r--r-- | runtime/mirror/string.cc | 2 | ||||
-rw-r--r-- | runtime/mirror/string.h | 9 | ||||
-rw-r--r-- | runtime/runtime_image.cc | 153 |
3 files changed, 131 insertions, 33 deletions
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index a21c967f1f..839e01a045 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -255,7 +255,7 @@ ObjPtr<String> String::AllocFromModifiedUtf8(Thread* self, return Alloc(self, length_with_flag, allocator_type, visitor); } -bool String::Equals(ObjPtr<String> that) { +bool String::Equals(mirror::String* that) { if (this == that) { // Quick reference equality test return true; diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 2b0f6f3d19..90eb09211c 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -197,7 +197,14 @@ class MANAGED String final : public Object { bool Equals(const char* modified_utf8) REQUIRES_SHARED(Locks::mutator_lock_); - bool Equals(ObjPtr<String> that) REQUIRES_SHARED(Locks::mutator_lock_); + bool Equals(ObjPtr<mirror::String> that) REQUIRES_SHARED(Locks::mutator_lock_) { + return Equals(that.Ptr()); + } + + // A version that takes a mirror::String pointer instead of ObjPtr as it's being + // called by the runtime app image code which can encode mirror::String at 64bit + // addresses (ObjPtr only works with 32bit pointers). + bool Equals(mirror::String* that) REQUIRES_SHARED(Locks::mutator_lock_); // Create a modified UTF-8 encoded std::string from a java/lang/String object. std::string ToModifiedUtf8() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc index 5efe951118..ffbb10c8bb 100644 --- a/runtime/runtime_image.cc +++ b/runtime/runtime_image.cc @@ -36,6 +36,7 @@ #include "mirror/object_array-alloc-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object_array.h" +#include "mirror/string-inl.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" @@ -51,10 +52,12 @@ class RuntimeImageHelper { boot_image_size_(heap->GetBootImagesSize()), image_begin_(boot_image_begin_ + boot_image_size_), // Note: image relocation considers the image header in the bitmap. - object_section_size_(sizeof(ImageHeader)) {} + object_section_size_(sizeof(ImageHeader)), + intern_table_(InternStringHash(this), InternStringEquals(this)) {} + bool Generate(std::string* error_msg) { - if (!WriteImageRoot(error_msg)) { + if (!WriteObjects(error_msg)) { return false; } @@ -136,6 +139,10 @@ class RuntimeImageHelper { return dex_location_; } + void GenerateInternData(std::vector<uint8_t>& data) const { + intern_table_.WriteToMemory(data.data()); + } + private: bool IsInBootImage(const void* obj) const { return reinterpret_cast<uintptr_t>(obj) - boot_image_begin_ < boot_image_size_; @@ -177,30 +184,100 @@ class RuntimeImageHelper { // Round up to the alignment the string table expects. See HashSet::WriteToMemory. size_t cur_pos = RoundUp(sections[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t)); - sections[ImageHeader::kSectionInternedStrings] = - ImageSection(cur_pos, 0u); + size_t intern_table_bytes = intern_table_.WriteToMemory(nullptr); + sections[ImageHeader::kSectionInternedStrings] = ImageSection(cur_pos, intern_table_bytes); // Obtain the new position and round it up to the appropriate alignment. cur_pos = RoundUp(sections[ImageHeader::kSectionInternedStrings].End(), sizeof(uint64_t)); - - sections[ImageHeader::kSectionClassTable] = - ImageSection(cur_pos, 0u); + sections[ImageHeader::kSectionClassTable] = ImageSection(cur_pos, 0u); // Round up to the alignment of the offsets we are going to store. cur_pos = RoundUp(sections[ImageHeader::kSectionClassTable].End(), sizeof(uint32_t)); - - sections[ImageHeader::kSectionStringReferenceOffsets] = - ImageSection(cur_pos, 0u); + sections[ImageHeader::kSectionStringReferenceOffsets] = ImageSection(cur_pos, 0u); // Round up to the alignment of the offsets we are going to store. cur_pos = RoundUp(sections[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(uint32_t)); - sections[ImageHeader::kSectionMetadata] = - ImageSection(cur_pos, 0u); + sections[ImageHeader::kSectionMetadata] = ImageSection(cur_pos, 0u); + } + + // Returns the copied mirror Object. This is really its content, it should not + // be returned as an `ObjPtr` (as it's not a GC object), nor stored anywhere. + template<typename T> T* FromImageOffsetToRuntimeContent(uint32_t offset) { + uint32_t vector_data_offset = offset - sizeof(ImageHeader) - image_begin_; + return reinterpret_cast<T*>(image_data_.data() + vector_data_offset); + } + + class InternStringHash { + public: + explicit InternStringHash(RuntimeImageHelper* helper) : helper_(helper) {} + + // NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`. + size_t operator()(mirror::String* str) const NO_THREAD_SAFETY_ANALYSIS { + int32_t hash = str->GetStoredHashCode(); + DCHECK_EQ(hash, str->ComputeHashCode()); + // An additional cast to prevent undesired sign extension. + return static_cast<uint32_t>(hash); + } + + size_t operator()(uint32_t entry) const NO_THREAD_SAFETY_ANALYSIS { + return (*this)(helper_->FromImageOffsetToRuntimeContent<mirror::String>(entry)); + } + + private: + RuntimeImageHelper* helper_; + }; + + class InternStringEquals { + public: + explicit InternStringEquals(RuntimeImageHelper* helper) : helper_(helper) {} + + // NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`. + bool operator()(uint32_t entry, mirror::String* other) const NO_THREAD_SAFETY_ANALYSIS { + if (kIsDebugBuild) { + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); + } + return other->Equals(helper_->FromImageOffsetToRuntimeContent<mirror::String>(entry)); + } + + bool operator()(uint32_t entry, uint32_t other) const NO_THREAD_SAFETY_ANALYSIS { + return (*this)(entry, helper_->FromImageOffsetToRuntimeContent<mirror::String>(other)); + } + + private: + RuntimeImageHelper* helper_; + }; + + using InternTableSet = + HashSet<uint32_t, DefaultEmptyFn<uint32_t>, InternStringHash, InternStringEquals>; + + void VisitDexCache(ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + // Currently only copy string objects into the image. Populate the intern + // table with these strings. + for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) { + ObjPtr<mirror::String> str = dex_cache->GetResolvedString(dex::StringIndex(i)); + if (str != nullptr && !IsInBootImage(str.Ptr())) { + uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode()); + DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode())) + << "Dex cache strings should be interned"; + if (intern_table_.FindWithHash(str.Ptr(), hash) == intern_table_.end()) { + uint32_t offset = CopyObject(str); + intern_table_.InsertWithHash(image_begin_ + offset + sizeof(ImageHeader), hash); + } + } + } + } + + void VisitDexCaches(Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array) + REQUIRES_SHARED(Locks::mutator_lock_) { + for (int32_t i = 0; i < dex_cache_array->GetLength(); ++i) { + VisitDexCache(ObjPtr<mirror::DexCache>::DownCast((dex_cache_array->Get(i)))); + } } - bool WriteImageRoot(std::string* error_msg) { + bool WriteObjects(std::string* error_msg) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope handles(soa.Self()); @@ -287,12 +364,17 @@ class RuntimeImageHelper { image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); image_roots->Set(ImageHeader::kAppImageDexChecksums, checksums_array.Get()); - // Now that we have created all objects needed for the `image_roots`, copy - // it into the buffer. Note that this will recursively copy all objects - // contained in `image_roots`. That's acceptable as we don't have cycles, - // nor a deep graph. - ScopedAssertNoThreadSuspension sants("Writing runtime app image"); - CopyObject(image_roots.Get()); + { + // Now that we have created all objects needed for the `image_roots`, copy + // it into the buffer. Note that this will recursively copy all objects + // contained in `image_roots`. That's acceptable as we don't have cycles, + // nor a deep graph. + ScopedAssertNoThreadSuspension sants("Writing runtime app image"); + CopyObject(image_roots.Get()); + } + + // Copy objects stored in the dex caches. + VisitDexCaches(dex_cache_array); return true; } @@ -474,6 +556,9 @@ class RuntimeImageHelper { // The location of the primary APK / dex file. std::string dex_location_; + + // The intern table for strings that we will write to disk. + InternTableSet intern_table_; }; std::string RuntimeImage::GetRuntimeImagePath(const std::string& dex_location) { @@ -521,20 +606,26 @@ bool RuntimeImage::WriteImageToDisk(std::string* error_msg) { return false; } - // Write bitmap at aligned offset. - size_t aligned_offset = RoundUp(sizeof(ImageHeader) + image.GetData().size(), kPageSize); - if (!out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()), - image.GetImageBitmap().Size(), - aligned_offset)) { - *error_msg = "Could not write image bitmap " + temp_path; - out->Unlink(); - return false; + { + // Write intern string set. + auto intern_section = image.GetHeader().GetImageSection(ImageHeader::kSectionInternedStrings); + std::vector<uint8_t> intern_data(intern_section.Size()); + image.GenerateInternData(intern_data); + if (!out->Write(reinterpret_cast<const char*>(intern_data.data()), + intern_section.Size(), + intern_section.Offset())) { + *error_msg = "Could not write intern section " + temp_path; + out->Unlink(); + return false; + } } - // Set the file length page aligned. - size_t total_size = aligned_offset + RoundUp(image.GetImageBitmap().Size(), kPageSize); - if (out->SetLength(total_size) != 0) { - *error_msg = "Could not change size of image " + temp_path; + // Write bitmap. + auto bitmap_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImageBitmap); + if (!out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()), + bitmap_section.Size(), + bitmap_section.Offset())) { + *error_msg = "Could not write image bitmap " + temp_path; out->Unlink(); return false; } |