Dex2oat support for multiple oat file and image file outputs.
Multiple changes to dex2oat and the runtime to support a --multi-image
option. This generates a separate oat file and image file output for
each dex file input.
Change-Id: Ie1d6f0b8afa8aed5790065b8c2eb177990c60129
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 342e1d9..0518911 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -330,7 +330,7 @@
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
- CHECK(!heap->HasImageSpace()) << "Runtime has image. We should use it.";
+ CHECK(!heap->HasBootImageSpace()) << "Runtime has image. We should use it.";
CHECK(!init_done_);
// Use the pointer size from the runtime since we are probably creating the image.
@@ -736,7 +736,7 @@
static void SanityCheckArtMethod(ArtMethod* m,
mirror::Class* expected_class,
- gc::space::ImageSpace* space)
+ std::vector<gc::space::ImageSpace*> spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
@@ -745,18 +745,22 @@
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
}
- if (space != nullptr) {
- auto& header = space->GetImageHeader();
- auto& methods = header.GetMethodsSection();
- auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
- CHECK(methods.Contains(offset)) << m << " not in " << methods;
+ if (!spaces.empty()) {
+ bool contains = false;
+ for (gc::space::ImageSpace* space : spaces) {
+ auto& header = space->GetImageHeader();
+ auto& methods = header.GetMethodsSection();
+ auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
+ contains |= methods.Contains(offset);
+ }
+ CHECK(contains) << m << " not found";
}
}
static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr,
mirror::Class* expected_class,
size_t pointer_size,
- gc::space::ImageSpace* space)
+ std::vector<gc::space::ImageSpace*> spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
@@ -766,11 +770,12 @@
CHECK(method != nullptr);
}
if (method != nullptr) {
- SanityCheckArtMethod(method, expected_class, space);
+ SanityCheckArtMethod(method, expected_class, spaces);
}
}
}
+/* TODO: Modify check to support multiple image spaces and reenable. b/26317072
static void SanityCheckArtMethodPointerArray(
ArtMethod** arr,
size_t size,
@@ -790,6 +795,7 @@
}
}
}
+*/
static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -805,29 +811,30 @@
CHECK_EQ(field.GetDeclaringClass(), klass);
}
auto* runtime = Runtime::Current();
- auto* image_space = runtime->GetHeap()->GetBootImageSpace();
+ auto image_spaces = runtime->GetHeap()->GetBootImageSpaces();
auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetMethods(pointer_size)) {
- SanityCheckArtMethod(&m, klass, image_space);
+ SanityCheckArtMethod(&m, klass, image_spaces);
}
auto* vtable = klass->GetVTable();
if (vtable != nullptr) {
- SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_space);
+ SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_spaces);
}
if (klass->ShouldHaveEmbeddedImtAndVTable()) {
for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
- SanityCheckArtMethod(klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_space);
+ SanityCheckArtMethod(
+ klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_spaces);
}
for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
- SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_space);
+ SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_spaces);
}
}
auto* iftable = klass->GetIfTable();
if (iftable != nullptr) {
for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
if (iftable->GetMethodArrayCount(i) > 0) {
- SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr, pointer_size,
- image_space);
+ SanityCheckArtMethodPointerArray(
+ iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces);
}
}
}
@@ -856,6 +863,33 @@
DISALLOW_COPY_AND_ASSIGN(SetInterpreterEntrypointArtMethodVisitor);
};
+struct TrampolineCheckData {
+ const void* quick_resolution_trampoline;
+ const void* quick_imt_conflict_trampoline;
+ const void* quick_generic_jni_trampoline;
+ const void* quick_to_interpreter_bridge_trampoline;
+ size_t pointer_size;
+ ArtMethod* m;
+ bool error;
+};
+static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS {
+ if (obj->IsClass()) {
+ mirror::Class* klass = obj->AsClass();
+ TrampolineCheckData* d = reinterpret_cast<TrampolineCheckData*>(arg);
+ for (ArtMethod& m : klass->GetMethods(d->pointer_size)) {
+ const void* entrypoint = m.GetEntryPointFromQuickCompiledCodePtrSize(d->pointer_size);
+ if (entrypoint == d->quick_resolution_trampoline ||
+ entrypoint == d->quick_imt_conflict_trampoline ||
+ entrypoint == d->quick_generic_jni_trampoline ||
+ entrypoint == d->quick_to_interpreter_bridge_trampoline) {
+ d->m = &m;
+ d->error = true;
+ return;
+ }
+ }
+ }
+}
+
bool ClassLinker::InitFromImage(std::string* error_msg) {
VLOG(startup) << "ClassLinker::InitFromImage entering";
CHECK(!init_done_);
@@ -863,28 +897,71 @@
Runtime* const runtime = Runtime::Current();
Thread* const self = Thread::Current();
gc::Heap* const heap = runtime->GetHeap();
- gc::space::ImageSpace* const space = heap->GetBootImageSpace();
- CHECK(space != nullptr);
- image_pointer_size_ = space->GetImageHeader().GetPointerSize();
+ std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces();
+ CHECK(!spaces.empty());
+ image_pointer_size_ = spaces[0]->GetImageHeader().GetPointerSize();
dex_cache_boot_image_class_lookup_required_ = true;
- const OatFile* oat_file = runtime->GetOatFileManager().RegisterImageOatFile(space);
- DCHECK(oat_file != nullptr);
- CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
- CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
- const char* image_file_location = oat_file->GetOatHeader().
+ std::vector<const OatFile*> oat_files =
+ runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
+ DCHECK(!oat_files.empty());
+ const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
+ CHECK_EQ(default_oat_header.GetImageFileLocationOatChecksum(), 0U);
+ CHECK_EQ(default_oat_header.GetImageFileLocationOatDataBegin(), 0U);
+ const char* image_file_location = oat_files[0]->GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
- quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline();
- quick_imt_conflict_trampoline_ = oat_file->GetOatHeader().GetQuickImtConflictTrampoline();
- quick_generic_jni_trampoline_ = oat_file->GetOatHeader().GetQuickGenericJniTrampoline();
- quick_to_interpreter_bridge_trampoline_ = oat_file->GetOatHeader().GetQuickToInterpreterBridge();
- StackHandleScope<2> hs(self);
- mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
- Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
- hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
+ quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline();
+ quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline();
+ quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline();
+ quick_to_interpreter_bridge_trampoline_ = default_oat_header.GetQuickToInterpreterBridge();
+ if (kIsDebugBuild) {
+ // Check that the other images use the same trampoline.
+ for (size_t i = 1; i < oat_files.size(); ++i) {
+ const OatHeader& ith_oat_header = oat_files[i]->GetOatHeader();
+ const void* ith_quick_resolution_trampoline =
+ ith_oat_header.GetQuickResolutionTrampoline();
+ const void* ith_quick_imt_conflict_trampoline =
+ ith_oat_header.GetQuickImtConflictTrampoline();
+ const void* ith_quick_generic_jni_trampoline =
+ ith_oat_header.GetQuickGenericJniTrampoline();
+ const void* ith_quick_to_interpreter_bridge_trampoline =
+ ith_oat_header.GetQuickToInterpreterBridge();
+ if (ith_quick_resolution_trampoline != quick_resolution_trampoline_ ||
+ ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ ||
+ ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ ||
+ ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_) {
+ // Make sure that all methods in this image do not contain those trampolines as
+ // entrypoints. Otherwise the class-linker won't be able to work with a single set.
+ TrampolineCheckData data;
+ data.error = false;
+ data.pointer_size = GetImagePointerSize();
+ data.quick_resolution_trampoline = ith_quick_resolution_trampoline;
+ data.quick_imt_conflict_trampoline = ith_quick_imt_conflict_trampoline;
+ data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline;
+ data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline;
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ spaces[i]->GetLiveBitmap()->Walk(CheckTrampolines, &data);
+ if (data.error) {
+ ArtMethod* m = data.m;
+ LOG(ERROR) << "Found a broken ArtMethod: " << PrettyMethod(m);
+ *error_msg = "Found an ArtMethod with a bad entrypoint";
+ return false;
+ }
+ }
+ }
+ }
- Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
- space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
+ StackHandleScopeCollection handles(self);
+ std::vector<Handle<mirror::ObjectArray<mirror::DexCache>>> dex_caches_vector;
+ for (gc::space::ImageSpace* space : spaces) {
+ Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(handles.NewHandle(
+ space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches)->
+ AsObjectArray<mirror::DexCache>()));
+ dex_caches_vector.push_back(dex_caches);
+ }
+
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots(handles.NewHandle(
+ spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
AsObjectArray<mirror::Class>()));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
@@ -896,56 +973,70 @@
java_lang_Object->SetObjectSize(sizeof(mirror::Object));
// Allocate in non-movable so that it's possible to check if a JNI weak global ref has been
// cleared without triggering the read barrier and unintentionally mark the sentinel alive.
- runtime->SetSentinel(heap->AllocNonMovableObject<true>(self,
- java_lang_Object,
- java_lang_Object->GetObjectSize(),
- VoidFunctor()));
+ runtime->SetSentinel(heap->AllocNonMovableObject<true>(
+ self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
- if (oat_file->GetOatHeader().GetDexFileCount() !=
- static_cast<uint32_t>(dex_caches->GetLength())) {
+ uint32_t dex_file_count = 0;
+ for (const OatFile* oat_file : oat_files) {
+ dex_file_count += oat_file->GetOatHeader().GetDexFileCount();
+ }
+ uint32_t dex_caches_count = 0;
+ for (auto dex_caches : dex_caches_vector) {
+ dex_caches_count += dex_caches->GetLength();
+ }
+ if (dex_file_count != dex_caches_count) {
*error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
"image";
return false;
}
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- StackHandleScope<1> hs2(self);
- Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
- const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(),
- nullptr);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Failed finding oat dex file for %s %s",
- oat_file->GetLocation().c_str(),
- dex_file_location.c_str());
- return false;
- }
- std::string inner_error_msg;
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
- if (dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
- dex_file_location.c_str(),
- oat_file->GetLocation().c_str(),
- inner_error_msg.c_str());
- return false;
- }
+ for (auto dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ StackHandleScope<1> hs2(self);
+ Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
+ const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
+ const OatFile::OatDexFile* oat_dex_file = nullptr;
+ for (const OatFile* oat_file : oat_files) {
+ const OatFile::OatDexFile* oat_dex =
+ oat_file->GetOatDexFile(dex_file_location.c_str(), nullptr, false);
+ if (oat_dex != nullptr) {
+ DCHECK(oat_dex_file == nullptr);
+ oat_dex_file = oat_dex;
+ }
+ }
- if (kSanityCheckObjects) {
- SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(),
- dex_cache->NumResolvedMethods(),
- image_pointer_size_,
- space);
- }
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed finding oat dex file for %s",
+ dex_file_location.c_str());
+ return false;
+ }
+ std::string inner_error_msg;
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
+ if (dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file %s error '%s'",
+ dex_file_location.c_str(),
+ inner_error_msg.c_str());
+ return false;
+ }
- if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
- dex_file_location.c_str(),
- dex_file->GetLocationChecksum(),
- oat_dex_file->GetDexFileLocationChecksum());
- return false;
- }
+ // TODO: Modify check to support multiple image spaces and reenable.
+// if (kSanityCheckObjects) {
+// SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(),
+// dex_cache->NumResolvedMethods(),
+// image_pointer_size_,
+// spaces);
+// }
- AppendToBootClassPath(*dex_file.get(), dex_cache);
- opened_dex_files_.push_back(std::move(dex_file));
+ if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
+ dex_file_location.c_str(),
+ dex_file->GetLocationChecksum(),
+ oat_dex_file->GetDexFileLocationChecksum());
+ return false;
+ }
+
+ AppendToBootClassPath(*dex_file.get(), dex_cache);
+ opened_dex_files_.push_back(std::move(dex_file));
+ }
}
if (!ValidPointerSize(image_pointer_size_)) {
@@ -968,12 +1059,14 @@
}
if (kSanityCheckObjects) {
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- auto* dex_cache = dex_caches->Get(i);
- for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
- auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
- if (field != nullptr) {
- CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
+ for (auto dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ auto* dex_cache = dex_caches->Get(i);
+ for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
+ auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
+ if (field != nullptr) {
+ CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
+ }
}
}
}
@@ -982,10 +1075,12 @@
// Set entry point to interpreter if in InterpretOnly mode.
if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
- const ImageHeader& header = space->GetImageHeader();
- const ImageSection& methods = header.GetMethodsSection();
- SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
- methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
+ for (gc::space::ImageSpace* space : spaces) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& methods = header.GetMethodsSection();
+ SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
+ methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
+ }
}
// reinit class_roots_
@@ -1014,13 +1109,15 @@
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
- const ImageHeader& header = space->GetImageHeader();
- const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable);
- if (section.Size() > 0u) {
- WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
- class_table->ReadFromMemory(space->Begin() + section.Offset());
- dex_cache_boot_image_class_lookup_required_ = false;
+ for (gc::space::ImageSpace* space : spaces) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable);
+ if (section.Size() > 0u) {
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
+ class_table->ReadFromMemory(space->Begin() + section.Offset());
+ dex_cache_boot_image_class_lookup_required_ = false;
+ }
}
FinishInit(self);
@@ -1974,7 +2071,7 @@
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted()) {
- if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) {
+ if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) {
return; // OAT file unavailable.
}
}
@@ -2783,23 +2880,27 @@
return result;
}
-static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches(gc::space::ImageSpace* image_space)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- CHECK(image_space != nullptr);
- mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
- DCHECK(root != nullptr);
- return root->AsObjectArray<mirror::DexCache>();
+static std::vector<mirror::ObjectArray<mirror::DexCache>*> GetImageDexCaches(
+ std::vector<gc::space::ImageSpace*> image_spaces) SHARED_REQUIRES(Locks::mutator_lock_) {
+ CHECK(!image_spaces.empty());
+ std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector;
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ DCHECK(root != nullptr);
+ dex_caches_vector.push_back(root->AsObjectArray<mirror::DexCache>());
+ }
+ return dex_caches_vector;
}
void ClassLinker::AddBootImageClassesToClassTable() {
if (dex_cache_boot_image_class_lookup_required_) {
- AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpace(),
+ AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpaces(),
/*class_loader*/nullptr);
dex_cache_boot_image_class_lookup_required_ = false;
}
}
-void ClassLinker::AddImageClassesToClassTable(gc::space::ImageSpace* image_space,
+void ClassLinker::AddImageClassesToClassTable(std::vector<gc::space::ImageSpace*> image_spaces,
mirror::ClassLoader* class_loader) {
Thread* self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2807,25 +2908,28 @@
ClassTable* const class_table = InsertClassTableForClassLoader(class_loader);
- mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(image_space);
std::string temp;
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
- GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
- for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
- mirror::Class* klass = types[j].Read();
- if (klass != nullptr) {
- DCHECK_EQ(klass->GetClassLoader(), class_loader);
- const char* descriptor = klass->GetDescriptor(&temp);
- size_t hash = ComputeModifiedUtf8Hash(descriptor);
- mirror::Class* existing = class_table->Lookup(descriptor, hash);
- if (existing != nullptr) {
- CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
- << PrettyClassAndClassLoader(klass);
- } else {
- class_table->Insert(klass);
- if (log_new_class_table_roots_) {
- new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
+ std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector =
+ GetImageDexCaches(image_spaces);
+ for (mirror::ObjectArray<mirror::DexCache>* dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+ mirror::Class* klass = types[j].Read();
+ if (klass != nullptr) {
+ DCHECK_EQ(klass->GetClassLoader(), class_loader);
+ const char* descriptor = klass->GetDescriptor(&temp);
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ mirror::Class* existing = class_table->Lookup(descriptor, hash);
+ if (existing != nullptr) {
+ CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
+ << PrettyClassAndClassLoader(klass);
+ } else {
+ class_table->Insert(klass);
+ if (log_new_class_table_roots_) {
+ new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
+ }
}
}
}
@@ -2856,18 +2960,20 @@
mirror::Class* ClassLinker::LookupClassFromBootImage(const char* descriptor) {
ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup");
- mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(
- Runtime::Current()->GetHeap()->GetBootImageSpace());
- for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
- const DexFile* dex_file = dex_cache->GetDexFile();
- // Try binary searching the type index by descriptor.
- const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor);
- if (type_id != nullptr) {
- uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
- mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
- if (klass != nullptr) {
- return klass;
+ std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector =
+ GetImageDexCaches(Runtime::Current()->GetHeap()->GetBootImageSpaces());
+ for (mirror::ObjectArray<mirror::DexCache>* dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ // Try binary searching the type index by descriptor.
+ const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor);
+ if (type_id != nullptr) {
+ uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
+ mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
+ if (klass != nullptr) {
+ return klass;
+ }
}
}
}
@@ -3167,7 +3273,7 @@
// the runtime isn't started. On the other hand, app classes can be re-verified even if they are
// already pre-opted, as then the runtime is started.
if (!Runtime::Current()->IsAotCompiler() &&
- !Runtime::Current()->GetHeap()->HasImageSpace() &&
+ !Runtime::Current()->GetHeap()->HasBootImageSpace() &&
klass->GetClassLoader() != nullptr) {
return false;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index f16fe92..9d432c6 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -505,7 +505,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Add image classes to the class table.
- void AddImageClassesToClassTable(gc::space::ImageSpace* image_space,
+ void AddImageClassesToClassTable(std::vector<gc::space::ImageSpace*> image_spaces,
mirror::ClassLoader* class_loader)
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 8f7bb94..d16afd9 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -487,7 +487,9 @@
// Mark all references to the alloc space(s).
void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) {
- auto* image_space = heap_->GetBootImageSpace();
+ // TODO: Needs better support for multi-images? b/26317072
+ space::ImageSpace* image_space =
+ heap_->GetBootImageSpaces().empty() ? nullptr : heap_->GetBootImageSpaces()[0];
// If we don't have an image space, just pass in space_ as the immune space. Pass in the same
// space_ instead of image_space to avoid a null check in ModUnionUpdateObjectReferencesVisitor.
CardBitVisitor bit_visitor(visitor, space_, image_space != nullptr ? image_space : space_,
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index 3be7181..61c67f8 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -167,7 +167,10 @@
uintptr_t* address = &bitmap_begin_[index];
uintptr_t old_word = *address;
if (kSetBit) {
- *address = old_word | mask;
+ if ((old_word & mask) == 0) {
+ // Avoid dirtying the page if possible.
+ *address = old_word | mask;
+ }
} else {
*address = old_word & ~mask;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6d72f31..3e432c7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -233,8 +233,7 @@
backtrace_lock_(nullptr),
seen_backtrace_count_(0u),
unique_backtrace_count_(0u),
- gc_disabled_for_shutdown_(false),
- boot_image_space_(nullptr) {
+ gc_disabled_for_shutdown_(false) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
@@ -260,23 +259,107 @@
CHECK_GE(300 * MB, non_moving_space_capacity);
requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity;
}
+
+ // Load image space(s).
if (!image_file_name.empty()) {
- ATRACE_BEGIN("ImageSpace::Create");
- std::string error_msg;
- boot_image_space_ = space::ImageSpace::Create(image_file_name.c_str(),
- image_instruction_set,
- &error_msg);
- ATRACE_END();
- if (boot_image_space_ != nullptr) {
- AddSpace(boot_image_space_);
- // Oat files referenced by image files immediately follow them in memory, ensure alloc space
- // isn't going to get in the middle
- uint8_t* oat_file_end_addr = boot_image_space_->GetImageHeader().GetOatFileEnd();
- CHECK_GT(oat_file_end_addr, boot_image_space_->End());
- requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
- } else {
- LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
- << "Attempting to fall back to imageless running. Error was: " << error_msg;
+ // For code reuse, handle this like a work queue.
+ std::vector<std::string> image_file_names;
+ image_file_names.push_back(image_file_name);
+
+ for (size_t index = 0; index < image_file_names.size(); ++index) {
+ std::string& image_name = image_file_names[index];
+ ATRACE_BEGIN("ImageSpace::Create");
+ std::string error_msg;
+ space::ImageSpace* boot_image_space = space::ImageSpace::Create(image_name.c_str(),
+ image_instruction_set,
+ index > 0,
+ &error_msg);
+ ATRACE_END();
+ if (boot_image_space != nullptr) {
+ AddSpace(boot_image_space);
+ // Oat files referenced by image files immediately follow them in memory, ensure alloc space
+ // isn't going to get in the middle
+ uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
+ CHECK_GT(oat_file_end_addr, boot_image_space->End());
+ requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
+ boot_image_spaces_.push_back(boot_image_space);
+
+ if (index == 0) {
+ // If this was the first space, check whether there are more images to load.
+ const OatFile* boot_oat_file = boot_image_space->GetOatFile();
+ if (boot_oat_file == nullptr) {
+ continue;
+ }
+
+ const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
+ const char* boot_classpath =
+ boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
+ if (boot_classpath == nullptr) {
+ continue;
+ }
+
+ std::vector<std::string> images;
+ Split(boot_classpath, ':', &images);
+
+ // Add the rest into the list. We have to adjust locations, possibly:
+ //
+ // For example, image_file_name is /a/b/c/d/e.art
+ // images[0] is f/c/d/e.art
+ // ----------------------------------------------
+ // images[1] is g/h/i/j.art -> /a/b/h/i/j.art
+
+ // Derive pattern.
+ std::vector<std::string> left;
+ Split(image_file_name, '/', &left);
+ std::vector<std::string> right;
+ Split(images[0], '/', &right);
+
+ size_t common = 1;
+ while (common < left.size() && common < right.size()) {
+ if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
+ break;
+ }
+ common++;
+ }
+
+ std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
+ std::string common_prefix = Join(prefix_vector, '/');
+ if (!common_prefix.empty() && common_prefix[0] != '/' && image_file_name[0] == '/') {
+ common_prefix = "/" + common_prefix;
+ }
+
+ // Apply pattern to images[1] .. images[n].
+ for (size_t i = 1; i < images.size(); ++i) {
+ std::string image = images[i];
+
+ size_t rslash = std::string::npos;
+ for (size_t j = 0; j < common; ++j) {
+ if (rslash != std::string::npos) {
+ rslash--;
+ }
+
+ rslash = image.rfind('/', rslash);
+ if (rslash == std::string::npos) {
+ rslash = 0;
+ }
+ if (rslash == 0) {
+ break;
+ }
+ }
+ std::string image_part = image.substr(rslash);
+
+ std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
+ image_part;
+ image_file_names.push_back(new_image);
+ }
+ }
+ } else {
+ LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
+ << "Attempting to fall back to imageless running. Error was: " << error_msg
+ << "\nAttempted image: " << image_name;
+ // TODO: Remove already loaded spaces.
+ break;
+ }
}
}
/*
@@ -456,13 +539,15 @@
rb_table_.reset(new accounting::ReadBarrierTable());
DCHECK(rb_table_->IsAllCleared());
}
- if (GetBootImageSpace() != nullptr) {
+ if (HasBootImageSpace()) {
// Don't add the image mod union table if we are running without an image, this can crash if
// we use the CardCache implementation.
- accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace(
- "Image mod-union table", this, GetBootImageSpace());
- CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
- AddModUnionTable(mod_union_table);
+ for (space::ImageSpace* image_space : GetBootImageSpaces()) {
+ accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace(
+ "Image mod-union table", this, image_space);
+ CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
+ AddModUnionTable(mod_union_table);
+ }
}
if (collector::SemiSpace::kUseRememberedSet && non_moving_space_ != main_space_) {
accounting::RememberedSet* non_moving_space_rem_set =
@@ -525,11 +610,12 @@
garbage_collectors_.push_back(mark_compact_collector_);
}
}
- if (GetBootImageSpace() != nullptr && non_moving_space_ != nullptr &&
+ if (!GetBootImageSpaces().empty() && non_moving_space_ != nullptr &&
(is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) {
// Check that there's no gap between the image space and the non moving space so that the
// immune region won't break (eg. due to a large object allocated in the gap). This is only
// required when we're the zygote or using GSS.
+ /* TODO: Modify this check to support multi-images. b/26317072
bool no_gap = MemMap::CheckNoGaps(GetBootImageSpace()->GetMemMap(),
non_moving_space_->GetMemMap());
if (!no_gap) {
@@ -537,6 +623,7 @@
MemMap::DumpMaps(LOG(ERROR), true);
LOG(FATAL) << "There's a gap between the image space and the non-moving space";
}
+ */
}
instrumentation::Instrumentation* const instrumentation = runtime->GetInstrumentation();
if (gc_stress_mode_) {
@@ -1202,8 +1289,8 @@
return FindDiscontinuousSpaceFromObject(obj, fail_ok);
}
-space::ImageSpace* Heap::GetBootImageSpace() const {
- return boot_image_space_;
+std::vector<space::ImageSpace*> Heap::GetBootImageSpaces() const {
+ return boot_image_spaces_;
}
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index e23b1a3..e7ea983 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -580,9 +580,8 @@
// Unbind any bound bitmaps.
void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_);
- // Returns the boot image space. There may be multiple image spaces, but there is only one boot
- // image space.
- space::ImageSpace* GetBootImageSpace() const;
+ // Returns the boot image spaces. There may be multiple boot image spaces.
+ std::vector<space::ImageSpace*> GetBootImageSpaces() const;
// Permenantly disable moving garbage collection.
void DisableMovingGc() REQUIRES(!*gc_complete_lock_);
@@ -660,8 +659,8 @@
void RemoveRememberedSet(space::Space* space);
bool IsCompilingBoot() const;
- bool HasImageSpace() const {
- return boot_image_space_ != nullptr;
+ bool HasBootImageSpace() const {
+ return !boot_image_spaces_.empty();
}
ReferenceProcessor* GetReferenceProcessor() {
@@ -1322,8 +1321,8 @@
// allocating.
bool gc_disabled_for_shutdown_ GUARDED_BY(gc_complete_lock_);
- // Boot image space.
- space::ImageSpace* boot_image_space_;
+ // Boot image spaces.
+ std::vector<space::ImageSpace*> boot_image_spaces_;
friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 8f67c21..952759c 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -43,12 +43,17 @@
Atomic<uint32_t> ImageSpace::bitmap_index_(0);
-ImageSpace::ImageSpace(const std::string& image_filename, const char* image_location,
- MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap,
- uint8_t* end)
+ImageSpace::ImageSpace(const std::string& image_filename,
+ const char* image_location,
+ MemMap* mem_map,
+ accounting::ContinuousSpaceBitmap* live_bitmap,
+ uint8_t* end,
+ MemMap* shadow_map)
: MemMapSpace(image_filename, mem_map, mem_map->Begin(), end, end,
kGcRetentionPolicyNeverCollect),
- image_location_(image_location) {
+ oat_file_non_owned_(nullptr),
+ image_location_(image_location),
+ shadow_map_(shadow_map) {
DCHECK(live_bitmap != nullptr);
live_bitmap_.reset(live_bitmap);
}
@@ -470,6 +475,7 @@
ImageSpace* ImageSpace::Create(const char* image_location,
const InstructionSet image_isa,
+ bool secondary_image,
std::string* error_msg) {
std::string system_filename;
bool has_system = false;
@@ -481,7 +487,7 @@
&has_system, &cache_filename, &dalvik_cache_exists,
&has_cache, &is_global_cache);
- if (Runtime::Current()->IsZygote()) {
+ if (Runtime::Current()->IsZygote() && !secondary_image) {
MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
}
@@ -686,7 +692,7 @@
return nullptr;
}
- if (kIsDebugBuild) {
+ if (VLOG_IS_ON(startup)) {
LOG(INFO) << "Dumping image sections";
for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
const auto section_idx = static_cast<ImageHeader::ImageSections>(i);
@@ -799,11 +805,52 @@
return nullptr;
}
+ // In case of multi-images, the images are spaced apart so that the bitmaps don't overlap. We
+ // need to reserve the slack, as otherwise the large object space might allocate in there.
+ // TODO: Reconsider the multi-image layout. b/26317072
+ std::unique_ptr<MemMap> shadow_map;
+ {
+ uintptr_t image_begin = reinterpret_cast<uintptr_t>(image_header.GetImageBegin());
+ uintptr_t image_end = RoundUp(image_begin + image_header.GetImageSize(), kPageSize);
+ uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_header.GetOatFileBegin());
+ if (image_end < oat_begin) {
+ // There's a gap. Could be multi-image, could be the oat file spaced apart. Go ahead and
+ // dummy-reserve the space covered by the bitmap (which will be a shadow that introduces
+ // a gap to the next image).
+ uintptr_t heap_size = bitmap->HeapSize();
+ uintptr_t bitmap_coverage_end = RoundUp(image_begin + heap_size, kPageSize);
+ if (bitmap_coverage_end > image_end) {
+ VLOG(startup) << "Reserving bitmap shadow ["
+ << std::hex << image_end << ";"
+ << std::hex << bitmap_coverage_end << ";] (oat file begins at "
+ << std::hex << oat_begin;
+ // Note: we cannot use MemMap::Dummy here, as that won't reserve the space in 32-bit mode.
+ shadow_map.reset(MemMap::MapAnonymous("Image bitmap shadow",
+ reinterpret_cast<uint8_t*>(image_end),
+ bitmap_coverage_end - image_end,
+ PROT_NONE,
+ false,
+ false,
+ error_msg));
+ if (shadow_map == nullptr) {
+ return nullptr;
+ }
+ // madvise it away, we don't really want it, just reserve the address space.
+ // TODO: Should we use MadviseDontNeedAndZero? b/26317072
+ madvise(shadow_map->BaseBegin(), shadow_map->BaseSize(), MADV_DONTNEED);
+ }
+ }
+ }
+
// We only want the mirror object, not the ArtFields and ArtMethods.
uint8_t* const image_end =
map->Begin() + image_header.GetImageSection(ImageHeader::kSectionObjects).End();
- std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location,
- map.release(), bitmap.release(), image_end));
+ std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
+ image_location,
+ map.release(),
+ bitmap.release(),
+ image_end,
+ shadow_map.release()));
// VerifyImageAllocations() will be called later in Runtime::Init()
// as some class roots like ArtMethod::java_lang_reflect_ArtMethod_
@@ -826,16 +873,18 @@
Runtime* runtime = Runtime::Current();
runtime->SetInstructionSet(space->oat_file_->GetOatHeader().GetInstructionSet());
- runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
- runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));
- runtime->SetImtUnimplementedMethod(
- image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod));
- runtime->SetCalleeSaveMethod(
- image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll);
- runtime->SetCalleeSaveMethod(
- image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly);
- runtime->SetCalleeSaveMethod(
- image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs);
+ if (!runtime->HasResolutionMethod()) {
+ runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
+ runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));
+ runtime->SetImtUnimplementedMethod(
+ image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod));
+ runtime->SetCalleeSaveMethod(
+ image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll);
+ runtime->SetCalleeSaveMethod(
+ image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly);
+ runtime->SetCalleeSaveMethod(
+ image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs);
+ }
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "ImageSpace::Init exiting (" << PrettyDuration(NanoTime() - start_time)
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index babd672..a54358a 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -43,7 +43,10 @@
// creation of the alloc space. The ReleaseOatFile will later be
// used to transfer ownership of the OatFile to the ClassLinker when
// it is initialized.
- static ImageSpace* Create(const char* image, InstructionSet image_isa, std::string* error_msg)
+ static ImageSpace* Create(const char* image,
+ InstructionSet image_isa,
+ bool secondary_image,
+ std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_);
// Reads the image header from the specified image location for the
@@ -158,8 +161,12 @@
std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap_;
- ImageSpace(const std::string& name, const char* image_location,
- MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap, uint8_t* end);
+ ImageSpace(const std::string& name,
+ const char* image_location,
+ MemMap* mem_map,
+ accounting::ContinuousSpaceBitmap* live_bitmap,
+ uint8_t* end,
+ MemMap* shadow_map = nullptr);
// The OatFile associated with the image during early startup to
// reserve space contiguous to the image. It is later released to
@@ -172,6 +179,10 @@
const std::string image_location_;
+ // A MemMap reserving the space of the bitmap "shadow," so that we don't allocate into it. Only
+ // used in the multi-image case.
+ std::unique_ptr<MemMap> shadow_map_;
+
private:
DISALLOW_COPY_AND_ASSIGN(ImageSpace);
};
diff --git a/runtime/image.cc b/runtime/image.cc
index f8f930b..3856787 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -55,7 +55,6 @@
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
- CHECK_LT(image_begin, image_roots);
CHECK_LT(image_roots, oat_file_begin);
CHECK_LE(oat_file_begin, oat_data_begin);
CHECK_LT(oat_data_begin, oat_data_end);
@@ -100,9 +99,6 @@
if (oat_file_begin_ >= oat_data_begin_) {
return false;
}
- if (image_roots_ <= image_begin_ || oat_file_begin_ <= image_roots_) {
- return false;
- }
if (!IsAligned<kPageSize>(patch_delta_)) {
return false;
}
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index e2e4782..d035f5d 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -187,24 +187,27 @@
if (image_added_to_intern_table_) {
return nullptr;
}
- gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetBootImageSpace();
- if (image == nullptr) {
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ if (image_spaces.empty()) {
return nullptr; // No image present.
}
- mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
- mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
const std::string utf8 = s->ToModifiedUtf8();
- for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
- const DexFile* dex_file = dex_cache->GetDexFile();
- // Binary search the dex file for the string index.
- const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
- if (string_id != nullptr) {
- uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
- // GetResolvedString() contains a RB.
- mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
- if (image_string != nullptr) {
- return image_string;
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
+ for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ // Binary search the dex file for the string index.
+ const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
+ if (string_id != nullptr) {
+ uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
+ // GetResolvedString() contains a RB.
+ mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
+ if (image_string != nullptr) {
+ return image_string;
+ }
}
}
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 5ed1977..13fd6a4 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -39,6 +39,7 @@
static constexpr const char* kPicKey = "pic";
static constexpr const char* kDebuggableKey = "debuggable";
static constexpr const char* kClassPathKey = "classpath";
+ static constexpr const char* kBootClassPath = "bootclasspath";
static constexpr const char kTrueValue[] = "true";
static constexpr const char kFalseValue[] = "false";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 83e594b..e3de14b 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -983,6 +983,7 @@
LOG(WARNING) << "Failed to find OatDexFile for DexFile " << dex_location
<< " ( canonical path " << dex_canonical_location << ")"
<< " with checksum " << checksum << " in OatFile " << GetLocation();
+ /* TODO: Modify for multi-image support and reenable. b/26317072
if (kIsDebugBuild) {
for (const OatDexFile* odf : oat_dex_files_storage_) {
LOG(WARNING) << "OatFile " << GetLocation()
@@ -991,6 +992,7 @@
<< " with checksum 0x" << std::hex << odf->GetDexFileLocationChecksum();
}
}
+ */
}
return nullptr;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0f3a013..cb80cc9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -846,11 +846,7 @@
std::string OatFileAssistant::ImageLocation() {
Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace();
- if (image_space == nullptr) {
- return "";
- }
- return image_space->GetImageLocation();
+ return runtime->GetHeap()->GetBootImageSpaces()[0]->GetImageLocation();
}
const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
@@ -949,12 +945,13 @@
image_info_load_attempted_ = true;
Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace();
- if (image_space != nullptr) {
- cached_image_info_.location = image_space->GetImageLocation();
+ std::vector<gc::space::ImageSpace*> image_spaces = runtime->GetHeap()->GetBootImageSpaces();
+ if (!image_spaces.empty()) {
+ // TODO: Better support multi-images? b/26317072
+ cached_image_info_.location = image_spaces[0]->GetImageLocation();
if (isa_ == kRuntimeISA) {
- const ImageHeader& image_header = image_space->GetImageHeader();
+ const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
cached_image_info_.oat_checksum = image_header.GetOatChecksum();
cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(
image_header.GetOatDataBegin());
@@ -969,7 +966,7 @@
cached_image_info_.patch_delta = image_header->GetPatchDelta();
}
}
- image_info_load_succeeded_ = (image_space != nullptr);
+ image_info_load_succeeded_ = (!image_spaces.empty());
}
return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
}
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 40cd50b..f994f0c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -223,9 +223,10 @@
false, dex_location.c_str(), &error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace();
- ASSERT_TRUE(image_space != nullptr);
- const ImageHeader& image_header = image_space->GetImageHeader();
+ const std::vector<gc::space::ImageSpace*> image_spaces =
+ runtime->GetHeap()->GetBootImageSpaces();
+ ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
+ const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
const OatHeader& oat_header = odex_file->GetOatHeader();
EXPECT_FALSE(odex_file->IsPic());
EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum());
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index ea6d3ff..36a967f 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -78,17 +78,23 @@
return nullptr;
}
-const OatFile* OatFileManager::GetBootOatFile() const {
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- return (image_space == nullptr) ? nullptr : image_space->GetOatFile();
+std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const {
+ std::vector<const OatFile*> oat_files;
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ oat_files.push_back(image_space->GetOatFile());
+ }
+ return oat_files;
}
const OatFile* OatFileManager::GetPrimaryOatFile() const {
ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
- const OatFile* boot_oat_file = GetBootOatFile();
- if (boot_oat_file != nullptr) {
+ std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
+ if (!boot_oat_files.empty()) {
for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
- if (oat_file.get() != boot_oat_file) {
+ if (std::find(boot_oat_files.begin(), boot_oat_files.end(), oat_file.get()) ==
+ boot_oat_files.end()) {
return oat_file.get();
}
}
@@ -102,8 +108,13 @@
oat_files_.clear();
}
-const OatFile* OatFileManager::RegisterImageOatFile(gc::space::ImageSpace* space) {
- return RegisterOatFile(space->ReleaseOatFile());
+std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles(
+ std::vector<gc::space::ImageSpace*> spaces) {
+ std::vector<const OatFile*> oat_files;
+ for (gc::space::ImageSpace* space : spaces) {
+ oat_files.push_back(RegisterOatFile(space->ReleaseOatFile()));
+ }
+ return oat_files;
}
class DexFileAndClassPair : ValueObject {
@@ -213,7 +224,7 @@
std::priority_queue<DexFileAndClassPair> queue;
// Add dex files from already loaded oat files, but skip boot.
- const OatFile* boot_oat = GetBootOatFile();
+ std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
// The same OatFile can be loaded multiple times at different addresses. In this case, we don't
// need to check both against each other since they would have resolved the same way at compile
// time.
@@ -221,8 +232,8 @@
for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) {
DCHECK_NE(loaded_oat_file.get(), oat_file);
const std::string& location = loaded_oat_file->GetLocation();
- if (loaded_oat_file.get() != boot_oat &&
- location != oat_file->GetLocation() &&
+ if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) ==
+ boot_oat_files.end() && location != oat_file->GetLocation() &&
unique_locations.find(location) == unique_locations.end()) {
unique_locations.insert(location);
AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue);
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index af7efb4..4690e45 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -73,15 +73,15 @@
return have_non_pic_oat_file_;
}
- // Returns the boot image oat file.
- const OatFile* GetBootOatFile() const;
+ // Returns the boot image oat files.
+ std::vector<const OatFile*> GetBootOatFiles() const;
// Returns the first non-image oat file in the class path.
const OatFile* GetPrimaryOatFile() const REQUIRES(!Locks::oat_file_manager_lock_);
- // Return the oat file for an image, registers the oat file. Takes ownership of the imagespace's
- // underlying oat file.
- const OatFile* RegisterImageOatFile(gc::space::ImageSpace* space)
+ // Returns the oat files for the images, registers the oat files.
+ // Takes ownership of the imagespace's underlying oat files.
+ std::vector<const OatFile*> RegisterImageOatFiles(std::vector<gc::space::ImageSpace*> spaces)
REQUIRES(!Locks::oat_file_manager_lock_);
// Finds or creates the oat file holding dex_location. Then loads and returns
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 03cad08..5643739 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -44,7 +44,8 @@
uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr);
uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_);
DCHECK(IsAlignedParam(code, GetInstructionSetAlignment(kRuntimeISA)) ||
- IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)));
+ IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)))
+ << std::hex << code << " " << std::hex << header;
return reinterpret_cast<OatQuickMethodHeader*>(header);
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index eeaadd4..be64bff 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -547,15 +547,15 @@
// Use !IsAotCompiler so that we get test coverage, tests are never the zygote.
if (!IsAotCompiler()) {
ScopedObjectAccess soa(self);
- gc::space::ImageSpace* image_space = heap_->GetBootImageSpace();
- if (image_space != nullptr) {
+ std::vector<gc::space::ImageSpace*> image_spaces = heap_->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
ATRACE_BEGIN("AddImageStringsToTable");
GetInternTable()->AddImageStringsToTable(image_space);
ATRACE_END();
- ATRACE_BEGIN("MoveImageClassesToClassTable");
- GetClassLinker()->AddBootImageClassesToClassTable();
- ATRACE_END();
}
+ ATRACE_BEGIN("MoveImageClassesToClassTable");
+ GetClassLinker()->AddBootImageClassesToClassTable();
+ ATRACE_END();
}
// If we are the zygote then we need to wait until after forking to create the code cache
@@ -564,7 +564,7 @@
CreateJit();
}
- if (!IsImageDex2OatEnabled() || !GetHeap()->HasImageSpace()) {
+ if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(soa.Self());
auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
@@ -754,61 +754,96 @@
VLOG(startup) << "Runtime::StartDaemonThreads exiting";
}
+// Attempts to open dex files from image(s). Given the image location, try to find the oat file
+// and open it to get the stored dex file. If the image is the first for a multi-image boot
+// classpath, go on and also open the other images.
static bool OpenDexFilesFromImage(const std::string& image_location,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
size_t* failures) {
DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is nullptr";
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename_unused;
- bool dalvik_cache_exists_unused;
- bool has_cache_unused;
- bool is_global_cache_unused;
- bool found_image = gc::space::ImageSpace::FindImageFilename(image_location.c_str(),
- kRuntimeISA,
- &system_filename,
- &has_system,
- &cache_filename_unused,
- &dalvik_cache_exists_unused,
- &has_cache_unused,
- &is_global_cache_unused);
- *failures = 0;
- if (!found_image || !has_system) {
- return false;
- }
- std::string error_msg;
- // We are falling back to non-executable use of the oat file because patching failed, presumably
- // due to lack of space.
- std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str());
- std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location.c_str());
- std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str()));
- if (file.get() == nullptr) {
- return false;
- }
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
- if (elf_file.get() == nullptr) {
- return false;
- }
- std::unique_ptr<const OatFile> oat_file(
- OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg));
- if (oat_file == nullptr) {
- LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg;
- return false;
- }
- for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
- if (oat_dex_file == nullptr) {
- *failures += 1;
- continue;
+ // Use a work-list approach, so that we can easily reuse the opening code.
+ std::vector<std::string> image_locations;
+ image_locations.push_back(image_location);
+
+ for (size_t index = 0; index < image_locations.size(); ++index) {
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename_unused;
+ bool dalvik_cache_exists_unused;
+ bool has_cache_unused;
+ bool is_global_cache_unused;
+ bool found_image = gc::space::ImageSpace::FindImageFilename(image_locations[index].c_str(),
+ kRuntimeISA,
+ &system_filename,
+ &has_system,
+ &cache_filename_unused,
+ &dalvik_cache_exists_unused,
+ &has_cache_unused,
+ &is_global_cache_unused);
+
+ if (!found_image || !has_system) {
+ return false;
}
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file.get() == nullptr) {
- *failures += 1;
- } else {
- dex_files->push_back(std::move(dex_file));
+
+ // We are falling back to non-executable use of the oat file because patching failed, presumably
+ // due to lack of space.
+ std::string oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str());
+ std::string oat_location =
+ ImageHeader::GetOatLocationFromImageLocation(image_locations[index].c_str());
+ // Note: in the multi-image case, the image location may end in ".jar," and not ".art." Handle
+ // that here.
+ if (EndsWith(oat_location, ".jar")) {
+ oat_location.replace(oat_location.length() - 3, 3, "oat");
}
+
+ std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str()));
+ if (file.get() == nullptr) {
+ return false;
+ }
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
+ if (elf_file.get() == nullptr) {
+ return false;
+ }
+ std::unique_ptr<const OatFile> oat_file(
+ OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg));
+ if (oat_file == nullptr) {
+ LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg;
+ return false;
+ }
+
+ for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
+ if (oat_dex_file == nullptr) {
+ *failures += 1;
+ continue;
+ }
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
+ *failures += 1;
+ } else {
+ dex_files->push_back(std::move(dex_file));
+ }
+ }
+
+ if (index == 0) {
+ // First file. See if this is a multi-image environment, and if so, enqueue the other images.
+ const OatHeader& boot_oat_header = oat_file->GetOatHeader();
+ const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
+ if (boot_cp != nullptr) {
+ std::vector<std::string> cp;
+ Split(boot_cp, ':', &cp);
+
+ if (cp.size() > 1) {
+ // More images, enqueue (skipping the first).
+ image_locations.insert(image_locations.end(), cp.begin() + 1, cp.end());
+ }
+ }
+ }
+
+ Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file));
}
- Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file));
return true;
}
@@ -946,7 +981,7 @@
runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
ATRACE_END();
- if (heap_->GetBootImageSpace() == nullptr && !allow_dex_file_fallback_) {
+ if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) {
LOG(ERROR) << "Dex file fallback disabled, cannot continue without image.";
ATRACE_END();
return false;
@@ -1054,7 +1089,7 @@
CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
class_linker_ = new ClassLinker(intern_table_);
- if (GetHeap()->HasImageSpace()) {
+ if (GetHeap()->HasBootImageSpace()) {
ATRACE_BEGIN("InitFromImage");
std::string error_msg;
bool result = class_linker_->InitFromImage(&error_msg);
@@ -1063,9 +1098,13 @@
LOG(ERROR) << "Could not initialize from image: " << error_msg;
return false;
}
+ /* TODO: Modify check to support multiple image spaces and reenable. b/26317072
if (kIsDebugBuild) {
- GetHeap()->GetBootImageSpace()->VerifyImageAllocations();
+ for (auto image_space : GetHeap()->GetBootImageSpaces()) {
+ image_space->VerifyImageAllocations();
+ }
}
+ */
if (boot_class_path_string_.empty()) {
// The bootclasspath is not explicitly specified: construct it from the loaded dex files.
const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();