/** *Thenativedatastructuresthatwestoreintheimage.
*/ enumclass NativeRelocationKind {
kArtFieldArray,
kArtMethodArray,
kArtMethod,
kImTable, // For dex cache arrays which can stay in memory even after startup. Those are // dex cache arrays whose size is below a given threshold, defined by // DexCache::ShouldAllocateFullArray.
kFullNativeDexCacheArray, // For dex cache arrays which we will want to release after app startup.
kStartupNativeDexCacheArray,
};
bool Generate(std::string* error_msg) { if (!WriteObjects(error_msg)) { returnfalse;
}
// Generate the sections information stored in the header.
CreateImageSections();
// Now that all sections have been created and we know their offset and // size, relocate native pointers inside classes and ImTables.
RelocateNativePointers();
// Generate the bitmap section, stored kElfSegmentAlignment-aligned after the sections data and // of size `object_section_size_` rounded up to kCardSize to match the bitmap size expected by // Loader::Init at art::gc::space::ImageSpace.
size_t sections_end = sections_[ImageHeader::kSectionMetadata].End();
image_bitmap_ = gc::accounting::ContinuousSpaceBitmap::Create( "image bitmap", reinterpret_cast<uint8_t*>(image_begin_),
RoundUp(object_section_size_, gc::accounting::CardTable::kCardSize)); for (uint32_t offset : object_offsets_) {
DCHECK(IsAligned<kObjectAlignment>(image_begin_ + sizeof(ImageHeader) + offset));
image_bitmap_.Set( reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset));
} const size_t bitmap_bytes = image_bitmap_.Size(); auto* bitmap_section = §ions_[ImageHeader::kSectionImageBitmap]; // The offset of the bitmap section should be aligned to kElfSegmentAlignment to enable mapping // the section from file to memory. However the section size doesn't have to be rounded up as // it is located at the end of the file. When mapping file contents to memory, if the last page // of the mapping is only partially filled with data, the rest will be zero-filled.
*bitmap_section = ImageSection(RoundUp(sections_end, kElfSegmentAlignment), bitmap_bytes);
// Compute boot image checksum and boot image components, to be stored in // the header.
gc::Heap* const heap = Runtime::Current()->GetHeap();
uint32_t boot_image_components = 0u;
uint32_t boot_image_checksums = 0u; const std::vector<gc::space::ImageSpace*>& image_spaces = heap->GetBootImageSpaces(); for (size_t i = 0u, size = image_spaces.size(); i != size; ) { const ImageHeader& header = image_spaces[i]->GetImageHeader();
boot_image_components += header.GetComponentCount();
boot_image_checksums ^= header.GetImageChecksum();
DCHECK_LE(header.GetImageSpaceCount(), size - i);
i += header.GetImageSpaceCount();
}
// Data size includes everything except the bitmap and the header.
header_.data_size_ = sections_end - sizeof(ImageHeader);
// Write image methods - needs to happen after creation of the header.
WriteImageMethods();
returntrue;
}
void FillData(std::vector<uint8_t>& data) { // Note we don't put the header, we only have it reserved in `data` as // Image::WriteData expects the object section to contain the image header. auto compute_dest = [&](const ImageSection& section) { return data.data() + section.Offset();
};
auto objects_section = header_.GetImageSection(ImageHeader::kSectionObjects);
memcpy(compute_dest(objects_section) + sizeof(ImageHeader), objects_.data(), objects_.size());
auto fields_section = header_.GetImageSection(ImageHeader::kSectionArtFields);
memcpy(compute_dest(fields_section), art_fields_.data(), fields_section.Size());
auto methods_section = header_.GetImageSection(ImageHeader::kSectionArtMethods);
memcpy(compute_dest(methods_section), art_methods_.data(), methods_section.Size());
auto im_tables_section = header_.GetImageSection(ImageHeader::kSectionImTables);
memcpy(compute_dest(im_tables_section), im_tables_.data(), im_tables_section.Size());
auto intern_section = header_.GetImageSection(ImageHeader::kSectionInternedStrings);
intern_table_.WriteToMemory(compute_dest(intern_section));
auto class_table_section = header_.GetImageSection(ImageHeader::kSectionClassTable);
class_table_.WriteToMemory(compute_dest(class_table_section));
auto string_offsets_section =
header_.GetImageSection(ImageHeader::kSectionStringReferenceOffsets);
memcpy(compute_dest(string_offsets_section),
string_reference_offsets_.data(),
string_offsets_section.Size());
auto dex_cache_section = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays);
memcpy(compute_dest(dex_cache_section), dex_cache_arrays_.data(), dex_cache_section.Size());
auto metadata_section = header_.GetImageSection(ImageHeader::kSectionMetadata);
memcpy(compute_dest(metadata_section), metadata_.data(), metadata_section.Size());
// Returns the image contents for `cls`. If `cls` is in the boot image, the // method just returns it.
mirror::Class* GetClassContent(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { if (cls == nullptr || IsInBootImage(cls.Ptr())) { return cls.Ptr();
} const dex::ClassDef* class_def = cls->GetClassDef();
DCHECK(class_def != nullptr) << cls->PrettyClass(); auto it = classes_.find(class_def);
DCHECK(it != classes_.end()) << cls->PrettyClass();
mirror::Class* result = reinterpret_cast<mirror::Class*>(objects_.data() + it->second);
DCHECK(result->GetClass()->IsClass()); return result;
}
// Returns a pointer that can be stored in `objects_`: // - The pointer itself for boot image objects, // - The offset in the image for all other objects. template <typename T> T* GetOrComputeImageAddress(ObjPtr<T> object)
REQUIRES_SHARED(Locks::mutator_lock_) { if (object == nullptr || IsInBootImage(object.Ptr())) {
DCHECK(object == nullptr || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(object)); return object.Ptr();
}
if (object->IsClassLoader()) { // DexCache and Class point to class loaders. For runtime-generated app // images, we don't encode the class loader. It will be set when the // runtime is loading the image. return nullptr;
}
if (object->GetClass() == GetClassRoot<mirror::ClassExt>()) { // No need to encode `ClassExt`. If needed, it will be reconstructed at // runtime. return nullptr;
}
// Round up to the alignment for ArtMethod.
static_assert(IsAligned<sizeof(void*)>(ArtMethod::Size(kRuntimePointerSize)));
size_t cur_pos = RoundUp(sections_[ImageHeader::kSectionArtFields].End(), sizeof(void*));
sections_[ImageHeader::kSectionArtMethods] = ImageSection(cur_pos, art_methods_.size());
// Round up to the alignment for ImTables.
cur_pos = RoundUp(sections_[ImageHeader::kSectionArtMethods].End(), sizeof(void*));
sections_[ImageHeader::kSectionImTables] = ImageSection(cur_pos, im_tables_.size());
// Round up to the alignment for conflict tables.
cur_pos = RoundUp(sections_[ImageHeader::kSectionImTables].End(), sizeof(void*));
sections_[ImageHeader::kSectionIMTConflictTables] = ImageSection(cur_pos, 0u);
// Round up to the alignment the string table expects. See HashSet::WriteToMemory.
cur_pos = RoundUp(sections_[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t));
// Obtain the new position and round it up to the appropriate alignment.
cur_pos = RoundUp(sections_[ImageHeader::kSectionInternedStrings].End(), sizeof(uint64_t));
// 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, string_reference_offsets_.size() * sizeof(string_reference_offsets_[0]));
// Round up to the alignment dex caches arrays expects.
cur_pos =
RoundUp(sections_[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(void*));
sections_[ImageHeader::kSectionDexCacheArrays] =
ImageSection(cur_pos, dex_cache_arrays_.size());
// Round up to the alignment expected for the metadata, which holds dex // cache arrays.
cur_pos = RoundUp(sections_[ImageHeader::kSectionDexCacheArrays].End(), sizeof(void*));
sections_[ImageHeader::kSectionMetadata] = ImageSection(cur_pos, metadata_.size());
}
// Returns the copied mirror Object if in the image, or the object directly if // in the boot image. For the copy, 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) { if (offset == 0u || IsInBootImage(reinterpret_cast<constvoid*>(offset))) { returnreinterpret_cast<T*>(offset);
}
uint32_t vector_data_offset = FromImageOffsetToVectorOffset(offset); returnreinterpret_cast<T*>(objects_.data() + vector_data_offset);
}
class InternStringEquals { public: explicit InternStringEquals(RuntimeImageHelper* helper) : helper_(helper) {}
// NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`. booloperator()(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));
}
class ClassDescriptorEquals { public:
ClassDescriptorEquals() {}
booloperator()(const ClassTable::TableSlot& a, const ClassTable::TableSlot& b) const NO_THREAD_SAFETY_ANALYSIS { // No need to fetch the descriptor: we know the classes we are inserting // in the ClassTable are unique. return a.Data() == b.Data();
}
};
using ClassTableSet = HashSet<ClassTable::TableSlot,
ClassTable::TableSlotEmptyFn,
ClassDescriptorHash,
ClassDescriptorEquals>;
// Helper class to collect classes that we will generate in the image. class ClassTableVisitor { public:
ClassTableVisitor(Handle<mirror::ClassLoader> loader, VariableSizedHandleScope& handles)
: loader_(loader), handles_(handles) {}
booloperator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { // Record app classes and boot classpath classes: app classes will be // generated in the image and put in the class table, boot classpath // classes will be put in the class table.
ObjPtr<mirror::ClassLoader> class_loader = klass->GetClassLoader(); if (klass->IsResolved() && (class_loader == loader_.Get() || class_loader == nullptr)) {
handles_.NewHandle(klass);
} returntrue;
}
// Helper class visitor to filter out classes we cannot emit. class PruneVisitor { public:
PruneVisitor(Thread* self,
RuntimeImageHelper* helper, const ArenaSet<const DexFile*>& dex_files,
ArenaVector<Handle<mirror::Class>>& classes,
ArenaAllocator& allocator)
: self_(self),
helper_(helper),
dex_files_(dex_files),
visited_(allocator.Adapter()),
classes_to_write_(classes) {}
bool CanEmitHelper(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { // If the class comes from a dex file which is not part of the primary // APK, don't encode it. if (!ContainsElement(dex_files_, &cls->GetDexFile())) { returnfalse;
}
// Ensure pointers to classes in `cls` can also be emitted.
StackHandleScope<1> hs(self_);
MutableHandle<mirror::Class> other_class = hs.NewHandle(cls->GetSuperClass()); if (!CanEmit(other_class)) { returnfalse;
}
other_class.Assign(cls->GetComponentType()); if (!CanEmit(other_class)) { returnfalse;
}
for (size_t i = 0, num_interfaces = cls->NumDirectInterfaces(); i < num_interfaces; ++i) {
other_class.Assign(cls->GetDirectInterface(i));
DCHECK(other_class != nullptr); if (!CanEmit(other_class)) { returnfalse;
}
} returntrue;
}
bool CanEmit(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { if (cls == nullptr) { returntrue;
}
DCHECK(cls->IsResolved()); // Only emit classes that are resolved and not erroneous. if (cls->IsErroneous()) { returnfalse;
}
// Proxy classes are generated at runtime, so don't emit them. if (cls->IsProxyClass()) { returnfalse;
}
// Classes in the boot image can be trivially encoded directly. if (helper_->IsInBootImage(cls.Get())) { returntrue;
}
if (cls->IsBootStrapClassLoaded()) { // We cannot encode classes that are part of the boot classpath. returnfalse;
}
DCHECK(!cls->IsPrimitive());
if (cls->IsArrayClass()) { if (cls->IsBootStrapClassLoaded()) { // For boot classpath arrays, we can only emit them if they are // in the boot image already. return helper_->IsInBootImage(cls.Get());
}
ObjPtr<mirror::Class> temp = cls.Get(); while ((temp = temp->GetComponentType())->IsArrayClass()) {}
StackHandleScope<1> hs(self_);
Handle<mirror::Class> other_class = hs.NewHandle(temp); return CanEmit(other_class);
} const dex::ClassDef* class_def = cls->GetClassDef();
DCHECK_NE(class_def, nullptr); auto existing = visited_.find(class_def); if (existing != visited_.end()) { // Already processed; return existing->second == VisitState::kCanEmit;
}
// Relocate the type array entries. We do this now before creating image // sections because we may add new boot image classes into our // `class_table`_. for (auto entry : dex_caches_) { const DexFile& dex_file = *entry.first;
mirror::DexCache* cache = reinterpret_cast<mirror::DexCache*>(&objects_[entry.second]);
mirror::GcRootArray<mirror::Class>* old_types_array = cache->GetResolvedTypesArray(); if (HasNativeRelocation(old_types_array)) { auto reloc_it = native_relocations_.find(old_types_array);
DCHECK(reloc_it != native_relocations_.end());
ArenaVector<uint8_t>& data =
(reloc_it->second.first == NativeRelocationKind::kFullNativeDexCacheArray)
? dex_cache_arrays_ : metadata_;
mirror::GcRootArray<mirror::Class>* content_array = reinterpret_cast<mirror::GcRootArray<mirror::Class>*>(
data.data() + reloc_it->second.second); for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
ObjPtr<mirror::Class> cls = old_types_array->Get(i); if (cls == nullptr) {
content_array->Set(i, nullptr);
} elseif (IsInBootImage(cls.Ptr())) { if (!cls->IsPrimitive()) { // The dex cache is concurrently updated by the app. If the class // collection logic in `PruneVisitor` did not see this class, insert it now. // Note that application class tables do not contain primitive // classes.
uint32_t hash = cls->DescriptorHash();
class_table_.InsertWithHash(ClassTable::TableSlot(cls.Ptr(), hash), hash);
}
content_array->Set(i, cls.Ptr());
} elseif (cls->IsArrayClass()) {
std::string class_name;
cls->GetDescriptor(&class_name); auto class_it = array_classes_.find(class_name); if (class_it == array_classes_.end()) {
content_array->Set(i, nullptr);
} else {
mirror::Class* ptr = reinterpret_cast<mirror::Class*>(
image_begin_ + sizeof(ImageHeader) + class_it->second);
content_array->Set(i, ptr);
}
} else {
DCHECK(!cls->IsPrimitive());
DCHECK(!cls->IsProxyClass()); const dex::ClassDef* class_def = cls->GetClassDef();
DCHECK_NE(class_def, nullptr); auto class_it = classes_.find(class_def); if (class_it == classes_.end()) {
content_array->Set(i, nullptr);
} else {
mirror::Class* ptr = reinterpret_cast<mirror::Class*>(
image_begin_ + sizeof(ImageHeader) + class_it->second);
content_array->Set(i, ptr);
}
}
}
}
}
}
// Helper visitor returning the location of a native pointer in the image. class NativePointerVisitor { public: explicit NativePointerVisitor(RuntimeImageHelper* helper) : helper_(helper) {}
auto it = native_relocations_.find(ptr); if (it == native_relocations_.end()) {
DCHECK(!must_have_relocation); return nullptr;
} switch (it->second.first) { case NativeRelocationKind::kArtMethod: case NativeRelocationKind::kArtMethodArray: {
uint32_t offset = sections_[ImageHeader::kSectionArtMethods].Offset(); returnreinterpret_cast<T*>(image_begin_ + offset + it->second.second);
} case NativeRelocationKind::kArtFieldArray: {
uint32_t offset = sections_[ImageHeader::kSectionArtFields].Offset(); returnreinterpret_cast<T*>(image_begin_ + offset + it->second.second);
} case NativeRelocationKind::kImTable: {
uint32_t offset = sections_[ImageHeader::kSectionImTables].Offset(); returnreinterpret_cast<T*>(image_begin_ + offset + it->second.second);
} case NativeRelocationKind::kStartupNativeDexCacheArray: {
uint32_t offset = sections_[ImageHeader::kSectionMetadata].Offset(); returnreinterpret_cast<T*>(image_begin_ + offset + it->second.second);
} case NativeRelocationKind::kFullNativeDexCacheArray: {
uint32_t offset = sections_[ImageHeader::kSectionDexCacheArrays].Offset(); returnreinterpret_cast<T*>(image_begin_ + offset + it->second.second);
}
}
}
template <typename Visitor> void RelocateMethodPointerArrays(mirror::Class* klass, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) { // A bit of magic here: we cast contents from our buffer to mirror::Class, // and do pointer comparison between 1) these classes, and 2) boot image objects. // Both kinds do not move.
// See if we need to fixup the vtable field.
mirror::Class* super = FromImageOffsetToRuntimeContent<mirror::Class>(
reinterpret_cast32<uint32_t>(
klass->GetSuperClass<kVerifyNone, kWithoutReadBarrier>().Ptr()));
DCHECK(super != nullptr) << "j.l.Object should never be in an app runtime image";
mirror::PointerArray* vtable = FromImageOffsetToRuntimeContent<mirror::PointerArray>(
reinterpret_cast32<uint32_t>(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
mirror::PointerArray* super_vtable = FromImageOffsetToRuntimeContent<mirror::PointerArray>(
reinterpret_cast32<uint32_t>(super->GetVTable<kVerifyNone, kWithoutReadBarrier>().Ptr())); if (vtable != nullptr && vtable != super_vtable) {
DCHECK(!IsInBootImage(vtable));
vtable->Fixup(vtable, kRuntimePointerSize, visitor);
}
// See if we need to fixup entries in the IfTable.
mirror::IfTable* iftable = FromImageOffsetToRuntimeContent<mirror::IfTable>(
reinterpret_cast32<uint32_t>(
klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
mirror::IfTable* super_iftable = FromImageOffsetToRuntimeContent<mirror::IfTable>(
reinterpret_cast32<uint32_t>(
super->GetIfTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
int32_t iftable_count = iftable->Count();
int32_t super_iftable_count = super_iftable->Count(); for (int32_t i = 0; i < iftable_count; ++i) {
mirror::PointerArray* methods = FromImageOffsetToRuntimeContent<mirror::PointerArray>(
reinterpret_cast32<uint32_t>(
iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i).Ptr()));
mirror::PointerArray* super_methods = (i < super_iftable_count)
? FromImageOffsetToRuntimeContent<mirror::PointerArray>(
reinterpret_cast32<uint32_t>(
super_iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i).Ptr()))
: nullptr; if (methods != super_methods) {
DCHECK(!IsInBootImage(methods));
methods->Fixup(methods, kRuntimePointerSize, visitor);
}
}
}
auto it = native_relocations_.find(old_method_array);
DCHECK(it != native_relocations_.end());
ArenaVector<uint8_t>& data =
(it->second.first == NativeRelocationKind::kFullNativeDexCacheArray)
? dex_cache_arrays_ : metadata_;
mirror::NativeArray<T>* content_array = reinterpret_cast<mirror::NativeArray<T>*>(data.data() + it->second.second); for (uint32_t i = 0; i < num_ids; ++i) { // We may not have relocations for some entries, in which case we'll // just store null.
content_array->Set(i, visitor(content_array->Get(i), /* must_have_relocation= */ false));
}
}
// Update the class pointer of individual fields. for (size_t i = 0; i != number_of_fields; ++i) {
dest_array->At(i).GetDeclaringClassAddressWithoutBarrier()->Assign( reinterpret_cast<mirror::Class*>(class_image_address));
}
}
uint16_t hotness_threshold = jit::Jit::GetInitialHotnessThreshold(); for (size_t i = 0; i != number_of_methods; ++i) {
ArtMethod* method = &cur_methods->At(i);
ArtMethod* copy = &dest_array->At(i);
// Update the class pointer.
ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass(); if (declaring_class == cls) {
copy->GetDeclaringClassAddressWithoutBarrier()->Assign( reinterpret_cast<mirror::Class*>(class_image_address));
} else {
DCHECK(method->IsCopied()); if (!IsInBootImage(declaring_class.Ptr())) {
DCHECK(classes_.find(declaring_class->GetClassDef()) != classes_.end());
copy->GetDeclaringClassAddressWithoutBarrier()->Assign( reinterpret_cast<mirror::Class*>(
image_begin_ + sizeof(ImageHeader) +
classes_.Get(declaring_class->GetClassDef())));
}
}
// Record the native relocation of the method.
uintptr_t copy_offset = reinterpret_cast<uintptr_t>(copy) - reinterpret_cast<uintptr_t>(art_methods_.data());
native_relocations_.Put(method,
std::make_pair(NativeRelocationKind::kArtMethod, copy_offset));
// Ignore the single-implementation info for abstract method. if (method->IsAbstract()) {
copy->SetHasSingleImplementation(false);
copy->SetSingleImplementation(nullptr, kRuntimePointerSize);
}
// Set the entrypoint and data pointer of the method.
StubType stub; if (method->IsNative()) {
stub = StubType::kQuickGenericJNITrampoline;
} elseif (!cls->IsVerified()) {
stub = StubType::kQuickToInterpreterBridge;
} elseif (!is_class_initialized && method->NeedsClinitCheckBeforeCall()) {
stub = StubType::kQuickResolutionTrampoline;
} elseif (interpreter::IsNterpSupported() && CanMethodUseNterp(method)) {
stub = StubType::kNterpTrampoline;
} else {
stub = StubType::kQuickToInterpreterBridge;
} const std::vector<gc::space::ImageSpace*>& image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
DCHECK(!image_spaces.empty()); const OatFile* oat_file = image_spaces[0]->GetOatFile();
DCHECK(oat_file != nullptr); const OatHeader& header = oat_file->GetOatHeader(); constvoid* entrypoint = header.GetOatAddress(stub); if (method->IsNative() && (is_class_initialized || !method->NeedsClinitCheckBeforeCall())) { // Use boot JNI stub if found.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); constvoid* boot_jni_stub = class_linker->FindBootJniStub(method); if (boot_jni_stub != nullptr) {
entrypoint = boot_jni_stub;
}
}
copy->SetEntryPointFromQuickCompiledCode(entrypoint);
// Lock the file, it could be concurrently updated by the system. Don't block // as this is app startup sensitive.
std::string error;
ScopedFlock profile =
LockedFile::Open(profile_file.c_str(), O_RDONLY, /*block=*/false, &error);
if (!profile_info.Load(profile->Fd())) {
LOG(DEBUG) << "Could not load profile file"; return;
}
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> class_loader =
hs.NewHandle<mirror::ClassLoader>(dex_caches[0]->GetClassLoader());
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ScopedTrace loading_classes("Loading classes from profile"); for (auto dex_cache : dex_caches) { const DexFile* dex_file = dex_cache->GetDexFile(); const ArenaSet<dex::TypeIndex>* class_types = profile_info.GetClasses(*dex_file); if (class_types == nullptr) { // This means the profile file did not reference the dex file, which is the case // if there's no classes and methods of that dex file in the profile. continue;
}
for (dex::TypeIndex idx : *class_types) { // The index is greater or equal to NumTypeIds if the type is an extra // descriptor, not referenced by the dex file. if (idx.index_ < dex_file->NumTypeIds()) {
ObjPtr<mirror::Class> klass = class_linker->ResolveType(idx, dex_cache, class_loader); if (klass == nullptr) {
self->ClearException();
LOG(DEBUG) << "Failed to preload " << dex_file->PrettyType(idx); continue;
}
}
}
}
}
if (image_roots == nullptr) {
DCHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
*error_msg = "Out of memory when trying to generate a runtime app image"; returnfalse;
}
// Find the dex files that will be used for generating the app image.
dchecked_vector<Handle<mirror::DexCache>> dex_caches;
FindDexCaches(soa.Self(), dex_caches, handles);
if (dex_caches.size() == 0) {
*error_msg = "Did not find dex caches to generate an app image"; returnfalse;
} const OatDexFile* oat_dex_file = dex_caches[0]->GetDexFile()->GetOatDexFile();
VdexFile* vdex_file = oat_dex_file->GetOatFile()->GetVdexFile(); // The first entry in `dex_caches` contains the location of the primary APK.
dex_location_ = oat_dex_file->GetDexFileLocation();
size_t number_of_dex_files = vdex_file->GetNumberOfDexFiles(); if (number_of_dex_files != dex_caches.size()) { // This means some dex files haven't been executed. For simplicity, just // register them and recollect dex caches.
Handle<mirror::ClassLoader> loader = handles.NewHandle(dex_caches[0]->GetClassLoader());
VisitClassLoaderDexFiles(soa.Self(), loader, [&](const art::DexFile* dex_file)
REQUIRES_SHARED(Locks::mutator_lock_) {
class_linker->RegisterDexFile(*dex_file, dex_caches[0]->GetClassLoader()); returntrue; // Continue with other dex files.
});
dex_caches.clear();
FindDexCaches(soa.Self(), dex_caches, handles); if (number_of_dex_files != dex_caches.size()) {
*error_msg = "Number of dex caches does not match number of dex files in the primary APK"; returnfalse;
}
}
// If classes referenced in the reference profile are not loaded, preload // them. This makes sure we generate a good runtime app image, even if this // current app run did not load all startup classes.
LoadClassesFromReferenceProfile(soa.Self(), dex_caches);
// We store the checksums of the dex files used at runtime. These can be // different compared to the vdex checksums due to compact dex.
std::vector<uint32_t> checksums(number_of_dex_files);
uint32_t checksum_index = 0; for (const OatDexFile* current_oat_dex_file : oat_dex_file->GetOatFile()->GetOatDexFiles()) { const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(current_oat_dex_file->GetDexFilePointer());
checksums[checksum_index++] = header->checksum_;
}
DCHECK_EQ(checksum_index, number_of_dex_files);
// Create the fake OatHeader to store the dependencies of the image.
SafeMap<std::string, std::string> key_value_store;
Runtime* runtime = Runtime::Current(); // For runtime images, there is no oat code so we don't need to add // kEnableProfileCode here. We also omit the check when loading the images.
key_value_store.Put(OatHeader::kApexVersionsKey, runtime->GetApexVersions());
key_value_store.Put(OatHeader::kBootClassPathKey,
android::base::Join(runtime->GetBootClassPathLocations(), ':'));
key_value_store.Put(OatHeader::kBootClassPathChecksumsKey,
runtime->GetBootClassPathChecksums());
key_value_store.Put(OatHeader::kClassPathKey,
oat_dex_file->GetOatFile()->GetClassLoaderContext());
key_value_store.Put(OatHeader::kConcurrentCopying,
gUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
// Create the byte array containing the oat header and dex checksums.
uint32_t checksums_size = checksums.size() * sizeof(uint32_t);
Handle<mirror::ByteArray> header_data = handles.NewHandle(
mirror::ByteArray::Alloc(soa.Self(), oat_header->GetHeaderSize() + checksums_size));
if (header_data == nullptr) {
DCHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
*error_msg = "Out of memory when trying to generate a runtime app image"; returnfalse;
}
// Create and populate the dex caches aray.
Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array = handles.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(
soa.Self(), object_array_class.Get(), dex_caches.size()));
if (dex_cache_array == nullptr) {
DCHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
*error_msg = "Out of memory when trying to generate a runtime app image"; returnfalse;
}
for (uint32_t i = 0; i < dex_caches.size(); ++i) {
dex_cache_array->Set(i, dex_caches[i].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());
}
// Emit classes defined in the app class loader (which will also indirectly // emit dex caches and their arrays).
EmitClasses(soa.Self(), dex_cache_array);
// We do not visit native roots. These are handled with other logic. void VisitRootIfNonNull(
[[maybe_unused]] mirror::CompressedReference<mirror::Object>* root) const {
LOG(FATAL) << "UNREACHABLE";
} void VisitRoot([[maybe_unused]] mirror::CompressedReference<mirror::Object>* root) const {
LOG(FATAL) << "UNREACHABLE";
}
voidoperator()(ObjPtr<mirror::Object> obj,
MemberOffset offset, bool is_static) const
REQUIRES_SHARED(Locks::mutator_lock_) { // We don't copy static fields, they are being handled when we try to // initialize the class.
ObjPtr<mirror::Object> ref =
is_static ? nullptr : obj->GetFieldObject<mirror::Object>(offset);
mirror::Object* address = image_->GetOrComputeImageAddress(ref);
mirror::Object* copy = reinterpret_cast<mirror::Object*>(image_->objects_.data() + copy_offset_);
copy->GetFieldObjectReferenceAddr<kVerifyNone>(offset)->Assign(address);
}
size_t size = num_entries * sizeof(void*); // We need to reserve space to store `num_entries` because ImageSpace doesn't have // access to the dex files when relocating dex caches.
size_t offset = RoundUp(data.size(), sizeof(void*)) + sizeof(uintptr_t);
data.resize(RoundUp(data.size(), sizeof(void*)) + sizeof(uintptr_t) + size); reinterpret_cast<uintptr_t*>(data.data() + offset)[-1] = num_entries;
// Copy each entry individually. We cannot use memcpy, as the entries may be // updated concurrently by other mutator threads.
mirror::NativeArray<T>* copy = reinterpret_cast<mirror::NativeArray<T>*>(data.data() + offset); for (uint32_t i = 0; i < num_entries; ++i) {
copy->Set(i, array->Get(i));
}
native_relocations_.Put(array, std::make_pair(relocation_kind, offset));
}
template <typename T>
mirror::GcRootArray<T>* CreateGcRootDexCacheArray(uint32_t num_entries,
uint32_t max_entries,
mirror::GcRootArray<T>* array) { if (array == nullptr) { return nullptr;
} bool only_startup = !mirror::DexCache::ShouldAllocateFullArray(num_entries, max_entries);
ArenaVector<uint8_t>& data = only_startup ? metadata_ : dex_cache_arrays_;
NativeRelocationKind relocation_kind = only_startup
? NativeRelocationKind::kStartupNativeDexCacheArray
: NativeRelocationKind::kFullNativeDexCacheArray;
size_t size = num_entries * sizeof(GcRoot<T>); // We need to reserve space to store `num_entries` because ImageSpace doesn't have // access to the dex files when relocating dex caches.
static_assert(sizeof(GcRoot<T>) == sizeof(uint32_t));
size_t offset = data.size() + sizeof(uint32_t);
data.resize(data.size() + sizeof(uint32_t) + size); reinterpret_cast<uint32_t*>(data.data() + offset)[-1] = num_entries;
native_relocations_.Put(array, std::make_pair(relocation_kind, offset));
returnreinterpret_cast<mirror::GcRootArray<T>*>(data.data() + offset);
} staticbool EmitDexCacheArrays() { // We need to treat dex cache arrays specially in an image for userfaultfd. // Disable for now. See b/270936884. return !gUseUserfaultfd;
}
uint32_t CopyDexCache(ObjPtr<mirror::DexCache> cache) REQUIRES_SHARED(Locks::mutator_lock_) { auto it = dex_caches_.find(cache->GetDexFile()); if (it != dex_caches_.end()) { return it->second;
}
uint32_t offset = CopyObject(cache);
dex_caches_.Put(cache->GetDexFile(), offset); // For dex caches, clear pointers to data that will be set at runtime.
mirror::Object* copy = reinterpret_cast<mirror::Object*>(objects_.data() + offset); reinterpret_cast<mirror::DexCache*>(copy)->ResetNativeArrays(); reinterpret_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr);
if (!EmitDexCacheArrays()) { return offset;
}
// Copy the ArtMethod array.
mirror::NativeArray<ArtMethod>* resolved_methods = cache->GetResolvedMethodsArray();
CopyNativeDexCacheArray(cache->GetDexFile()->NumMethodIds(),
mirror::DexCache::kDexCacheMethodCacheSize,
resolved_methods); // Store the array pointer in the dex cache, which will be relocated at the end. reinterpret_cast<mirror::DexCache*>(copy)->SetResolvedMethodsArray(resolved_methods);
// Copy the ArtField array.
mirror::NativeArray<ArtField>* resolved_fields = cache->GetResolvedFieldsArray();
CopyNativeDexCacheArray(cache->GetDexFile()->NumFieldIds(),
mirror::DexCache::kDexCacheFieldCacheSize,
resolved_fields); // Store the array pointer in the dex cache, which will be relocated at the end. reinterpret_cast<mirror::DexCache*>(copy)->SetResolvedFieldsArray(resolved_fields);
// Copy the type array.
mirror::GcRootArray<mirror::Class>* resolved_types = cache->GetResolvedTypesArray();
CreateGcRootDexCacheArray(cache->GetDexFile()->NumTypeIds(),
mirror::DexCache::kDexCacheTypeCacheSize,
resolved_types); // Store the array pointer in the dex cache, which will be relocated at the end. reinterpret_cast<mirror::DexCache*>(copy)->SetResolvedTypesArray(resolved_types);
// Copy the string array.
mirror::GcRootArray<mirror::String>* strings = cache->GetStringsArray(); // Note: `new_strings` points to temporary data, and is only valid here.
mirror::GcRootArray<mirror::String>* new_strings =
CreateGcRootDexCacheArray(cache->GetDexFile()->NumStringIds(),
mirror::DexCache::kDexCacheStringCacheSize,
strings); // Store the array pointer in the dex cache, which will be relocated at the end. reinterpret_cast<mirror::DexCache*>(copy)->SetStringsArray(strings);
// The code below copies new objects, so invalidate the address we have for // `copy`.
copy = nullptr; if (strings != nullptr) { for (uint32_t i = 0; i < cache->GetDexFile()->NumStringIds(); ++i) {
ObjPtr<mirror::String> str = strings->Get(i); if (str == nullptr || IsInBootImage(str.Ptr())) {
new_strings->Set(i, str.Ptr());
} else {
uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode());
DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode()))
<< "Dex cache strings should be interned"; auto it2 = intern_table_.FindWithHash(str.Ptr(), hash); if (it2 == intern_table_.end()) {
uint32_t string_offset = CopyObject(str);
uint32_t address = image_begin_ + string_offset + sizeof(ImageHeader);
intern_table_.InsertWithHash(address, hash);
new_strings->Set(i, reinterpret_cast<mirror::String*>(address));
} else {
new_strings->Set(i, reinterpret_cast<mirror::String*>(*it2));
} // To not confuse string references from the dex cache object and // string references from the array, we put an offset bigger than the // size of a DexCache object. ClassLinker::VisitInternedStringReferences // knows how to decode this offset.
string_reference_offsets_.emplace_back( sizeof(ImageHeader) + offset, sizeof(mirror::DexCache) + i);
}
}
}
return offset;
}
bool IsInitialized(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) { if (IsInBootImage(cls)) { const OatDexFile* oat_dex_file = cls->GetDexFile().GetOatDexFile();
DCHECK(oat_dex_file != nullptr) << "We should always have an .oat file for a boot image";
uint16_t class_def_index = cls->GetDexClassDefIndex();
ClassStatus oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus(); return oat_file_class_status == ClassStatus::kVisiblyInitialized;
} else { return cls->IsVisiblyInitialized<kVerifyNone>();
}
} // Try to initialize `copy`. Note that `cls` may not be initialized. // This is called after the image generation logic has visited super classes // and super interfaces, so we can just check those directly. bool TryInitializeClass(mirror::Class* copy, ObjPtr<mirror::Class> cls, uint32_t class_offset)
REQUIRES_SHARED(Locks::mutator_lock_) { if (!cls->IsVerified()) { returnfalse;
} if (cls->IsArrayClass()) { returntrue;
}
// Check if we have been able to initialize the super class.
mirror::Class* super = GetClassContent(cls->GetSuperClass());
DCHECK(super != nullptr)
<< "App image classes should always have a super class: " << cls->PrettyClass(); if (!IsInitialized(super)) { returnfalse;
}
// We won't initialize class with class initializers. if (cls->FindClassInitializer(kRuntimePointerSize) != nullptr) { returnfalse;
}
// For non-interface classes, we require all implemented interfaces to be // initialized. if (!cls->IsInterface()) { for (size_t i = 0; i < cls->NumDirectInterfaces(); i++) {
mirror::Class* itf = GetClassContent(cls->GetDirectInterface(i)); if (!IsInitialized(itf)) { returnfalse;
}
}
}
// Trivial case: no static fields. if (!cls->HasStaticFields()) { returntrue;
}
// Go over all static fields and try to initialize them.
EncodedStaticFieldValueIterator it(cls->GetDexFile(), *cls->GetClassDef()); if (!it.HasNext()) { returntrue;
}
// Temporary string offsets in case we failed to initialize the class. We // will add the offsets at the end of this method if we are successful.
ArenaVector<AppImageReferenceOffsetInfo> string_offsets(allocator_.Adapter());
ClassLinker* linker = Runtime::Current()->GetClassLinker();
ClassAccessor accessor(cls->GetDexFile(), *cls->GetClassDef()); for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { if (!it.HasNext()) { break;
}
ArtField* art_field = linker->LookupResolvedField(field.GetIndex(),
cls->GetDexCache(),
cls->GetClassLoader(), /* is_static= */ true);
DCHECK_NE(art_field, nullptr);
MemberOffset offset(art_field->GetOffset()); switch (it.GetValueType()) { case EncodedArrayValueIterator::ValueType::kBoolean:
copy->SetFieldBoolean<false>(offset, it.GetJavaValue().z); break; case EncodedArrayValueIterator::ValueType::kByte:
copy->SetFieldByte<false>(offset, it.GetJavaValue().b); break; case EncodedArrayValueIterator::ValueType::kShort:
copy->SetFieldShort<false>(offset, it.GetJavaValue().s); break; case EncodedArrayValueIterator::ValueType::kChar:
copy->SetFieldChar<false>(offset, it.GetJavaValue().c); break; case EncodedArrayValueIterator::ValueType::kInt:
copy->SetField32<false>(offset, it.GetJavaValue().i); break; case EncodedArrayValueIterator::ValueType::kLong:
copy->SetField64<false>(offset, it.GetJavaValue().j); break; case EncodedArrayValueIterator::ValueType::kFloat:
copy->SetField32<false>(offset, it.GetJavaValue().i); break; case EncodedArrayValueIterator::ValueType::kDouble:
copy->SetField64<false>(offset, it.GetJavaValue().j); break; case EncodedArrayValueIterator::ValueType::kNull:
copy->SetFieldObject<false>(offset, nullptr); break; case EncodedArrayValueIterator::ValueType::kString: {
ObjPtr<mirror::String> str =
linker->LookupString(dex::StringIndex(it.GetJavaValue().i), cls->GetDexCache());
mirror::String* str_copy = nullptr; if (str == nullptr) { // String wasn't created yet. returnfalse;
} elseif (IsInBootImage(str.Ptr())) {
str_copy = str.Ptr();
} else {
uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode());
DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode()))
<< "Dex cache strings should be interned"; auto string_it = intern_table_.FindWithHash(str.Ptr(), hash); if (string_it == intern_table_.end()) { // The string must be interned.
uint32_t string_offset = CopyObject(str); // Reload the class copy after having copied the string.
copy = reinterpret_cast<mirror::Class*>(objects_.data() + class_offset);
uint32_t address = image_begin_ + string_offset + sizeof(ImageHeader);
intern_table_.InsertWithHash(address, hash);
str_copy = reinterpret_cast<mirror::String*>(address);
} else {
str_copy = reinterpret_cast<mirror::String*>(*string_it);
}
string_offsets.emplace_back(sizeof(ImageHeader) + class_offset, offset.Int32Value());
}
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(copy) + offset.Int32Value();
mirror::HeapReference<mirror::Object>* objref_addr = reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_addr);
objref_addr->Assign</* kIsVolatile= */ false>(str_copy); break;
} case EncodedArrayValueIterator::ValueType::kType: { // Note that it may be that the referenced type hasn't been processed // yet by the image generation logic. In this case we bail out for // simplicity.
ObjPtr<mirror::Class> type =
linker->LookupResolvedType(dex::TypeIndex(it.GetJavaValue().i), cls);
mirror::Class* type_copy = nullptr; if (type == nullptr) { // Class wasn't resolved yet. returnfalse;
} elseif (IsInBootImage(type.Ptr())) { // Make sure the type is in our class table.
uint32_t hash = type->DescriptorHash();
class_table_.InsertWithHash(ClassTable::TableSlot(type.Ptr(), hash), hash);
type_copy = type.Ptr();
} elseif (type->IsArrayClass()) {
std::string class_name;
type->GetDescriptor(&class_name); auto class_it = array_classes_.find(class_name); if (class_it == array_classes_.end()) { returnfalse;
}
type_copy = reinterpret_cast<mirror::Class*>(
image_begin_ + sizeof(ImageHeader) + class_it->second);
} else { const dex::ClassDef* class_def = type->GetClassDef();
DCHECK_NE(class_def, nullptr); auto class_it = classes_.find(class_def); if (class_it == classes_.end()) { returnfalse;
}
type_copy = reinterpret_cast<mirror::Class*>(
image_begin_ + sizeof(ImageHeader) + class_it->second);
}
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(copy) + offset.Int32Value();
mirror::HeapReference<mirror::Object>* objref_addr = reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_addr);
objref_addr->Assign</* kIsVolatile= */ false>(type_copy); break;
} default:
LOG(FATAL) << "Unreachable";
}
it.Next();
} // We have successfully initialized the class, we can now record the string // offsets.
string_reference_offsets_.insert(
string_reference_offsets_.end(), string_offsets.begin(), string_offsets.end()); returntrue;
}
uint32_t CopyClass(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!cls->IsBootStrapClassLoaded());
uint32_t offset = 0u; if (cls->IsArrayClass()) {
std::string class_name;
cls->GetDescriptor(&class_name); auto it = array_classes_.find(class_name); if (it != array_classes_.end()) { return it->second;
}
offset = CopyObject(cls);
array_classes_.Put(class_name, offset);
} else { const dex::ClassDef* class_def = cls->GetClassDef(); auto it = classes_.find(class_def); if (it != classes_.end()) { return it->second;
}
offset = CopyObject(cls);
classes_.Put(class_def, offset);
}
uint32_t hash = cls->DescriptorHash(); // Save the hash, the `HashSet` implementation requires to find it.
class_hashes_.Put(offset, hash);
uint32_t class_image_address = image_begin_ + sizeof(ImageHeader) + offset; bool inserted =
class_table_.InsertWithHash(ClassTable::TableSlot(class_image_address, hash), hash).second;
DCHECK(inserted) << "Class " << cls->PrettyDescriptor()
<< " (" << cls.Ptr() << ") already inserted";
// Clear static field values. auto clear_class = [&] () REQUIRES_SHARED(Locks::mutator_lock_) {
MemberOffset static_offset = cls->GetFirstReferenceStaticFieldOffset(kRuntimePointerSize);
uint32_t ref_offsets = cls->GetReferenceInstanceOffsets();
size_t size = cls->GetClassSize() - static_offset.Uint32Value(); // Adjust for overflow instance-offset bitmap, which is after the static // fields. if ((ref_offsets & mirror::Class::kVisitReferencesSlowpathMask) != 0) {
ref_offsets &= ~mirror::Class::kVisitReferencesSlowpathMask;
size -= ref_offsets * sizeof(uint32_t);
}
memset(objects_.data() + offset + static_offset.Uint32Value(), 0, size);
};
clear_class();
bool is_class_initialized = TryInitializeClass(copy, cls, offset); // Reload the copy, it may have moved after `TryInitializeClass`.
copy = reinterpret_cast<mirror::Class*>(objects_.data() + offset); if (is_class_initialized) {
copy->SetStatusInternal(ClassStatus::kVisiblyInitialized); if (!cls->IsArrayClass() && !cls->IsFinalizable()) {
copy->SetObjectSizeAllocFastPath(RoundUp(cls->GetObjectSize(), kObjectAlignment));
} if (cls->IsInterface()) {
copy->SetAccessFlags(copy->GetAccessFlags() | kAccRecursivelyInitialized);
}
} else { // If we fail to initialize, remove initialization related flags and // clear again.
copy->SetObjectSizeAllocFastPath(std::numeric_limits<uint32_t>::max());
copy->SetAccessFlags(copy->GetAccessFlags() & ~kAccRecursivelyInitialized);
clear_class();
}
CopyFieldArrays(cls, class_image_address);
CopyMethodArrays(cls, class_image_address, is_class_initialized); if (cls->ShouldHaveImt()) {
CopyImTable(cls);
}
return offset;
}
// Copy `obj` in `objects_` and relocate references. Returns the offset // within our buffer.
uint32_t CopyObject(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) { // Copy the object in `objects_`.
size_t object_size = obj->SizeOf();
size_t offset = objects_.size();
DCHECK(IsAligned<kObjectAlignment>(offset));
object_offsets_.push_back(offset);
objects_.resize(RoundUp(offset + object_size, kObjectAlignment));
if (obj->IsString()) { // Ensure a string always has a hashcode stored. This is checked at // runtime because boot images don't want strings dirtied due to hashcode. reinterpret_cast<mirror::String*>(copy)->GetHashCode();
}
// Find the primary APK.
AppInfo* app_info = Runtime::Current()->GetAppInfo(); for (Handle<mirror::DexCache> cache : visitor.GetDexCaches()) { if (app_info->GetRegisteredCodeType(cache->GetDexFile()->GetLocation()) ==
AppInfo::CodeType::kPrimaryApk) {
dex_caches.push_back(handles.NewHandle(cache.Get())); break;
}
}
if (dex_caches.empty()) { return;
}
const OatDexFile* oat_dex_file = dex_caches[0]->GetDexFile()->GetOatDexFile(); if (oat_dex_file == nullptr) { // We need a .oat file for loading an app image;
dex_caches.clear(); return;
}
// Store the dex caches in the order in which their corresponding dex files // are stored in the oat file. When we check for checksums at the point of // loading the image, we rely on this order. for (const OatDexFile* current : oat_dex_file->GetOatFile()->GetOatDexFiles()) { if (current != oat_dex_file) { for (Handle<mirror::DexCache> cache : visitor.GetDexCaches()) { if (cache->GetDexFile()->GetOatDexFile() == current) {
dex_caches.push_back(handles.NewHandle(cache.Get()));
}
}
}
}
}
// Bitmap of live objects in `objects_`. Populated from `object_offsets_` // once we know `object_section_size`.
gc::accounting::ContinuousSpaceBitmap image_bitmap_;
// Sections stored in the header.
ArenaVector<ImageSection> sections_;
// A list of offsets in `objects_` where objects begin.
ArenaVector<uint32_t> object_offsets_;
std::string RuntimeImage::GetRuntimeImageDir(const std::string& app_data_dir) { if (app_data_dir.empty()) { // The data directory is empty for tests. return"";
} return app_data_dir + "/cache/oat_primary/";
}
// Note: this may return a relative path for tests.
std::string RuntimeImage::GetRuntimeImagePath(const std::string& app_data_dir, const std::string& dex_location, const std::string& isa) {
std::string basename = android::base::Basename(dex_location);
std::string filename = ReplaceFileExtension(basename, kArtExtension);
return GetRuntimeImageDir(app_data_dir) + isa + "/" + filename;
}
// We first generate the app image in a temporary file, which we will then // move to `path`. const std::string temp_path = ReplaceFileExtension(path, std::to_string(getpid()) + ".tmp");
ImageFileGuard image_file;
image_file.reset(OS::CreateEmptyFileWriteOnly(temp_path.c_str()));
if (image_file == nullptr) {
*error_msg = "Could not open " + temp_path + " for writing"; returnfalse;
}
// Specify default block size of 512K to enable parallel image decompression. static constexpr size_t kMaxImageBlockSize = 524288; // Use LZ4 as good compromise between CPU time and compression. LZ4HC // empirically takes 10x more time compressing. static constexpr ImageHeader::StorageMode kImageStorageMode = ImageHeader::kStorageModeLZ4; // Note: no need to update the checksum of the runtime app image: we have no // use for it, and computing it takes CPU time. if (!image->GetHeader()->WriteData(
image_file,
full_data.data(), reinterpret_cast<const uint8_t*>(image->GetImageBitmap().Begin()),
kImageStorageMode,
kMaxImageBlockSize, /* update_checksum= */ false,
error_msg)) { returnfalse;
}
if (!image_file.WriteHeaderAndClose(temp_path, image->GetHeader(), error_msg)) { returnfalse;
}
if (rename(temp_path.c_str(), path.c_str()) != 0) {
*error_msg = "Failed to move runtime app image to " + path + ": " + std::string(strerror(errno)); // Unlink directly: we cannot use `out` as we have closed it.
unlink(temp_path.c_str()); returnfalse;
}
returntrue;
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.