Support running without a boot image.
Bug: 17000769
Change-Id: I6404d5050c8a2f4ee6e70d58532eb25ee9de248e
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8fc1d07..ab4bf41 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -195,10 +195,13 @@
// To set a value for generic JNI. May be necessary in compiler tests.
extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
+extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
-void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class_path) {
+void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) {
VLOG(startup) << "ClassLinker::Init";
- CHECK(Runtime::Current()->IsCompiler());
+ CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it.";
CHECK(!init_done_);
@@ -370,6 +373,12 @@
// Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
// we do not need friend classes or a publicly exposed setter.
quick_generic_jni_trampoline_ = reinterpret_cast<void*>(art_quick_generic_jni_trampoline);
+ if (!runtime->IsCompiler()) {
+ // We need to set up the generic trampolines since we don't have an image.
+ quick_resolution_trampoline_ = reinterpret_cast<void*>(art_quick_resolution_trampoline);
+ quick_imt_conflict_trampoline_ = reinterpret_cast<void*>(art_quick_imt_conflict_trampoline);
+ quick_to_interpreter_bridge_trampoline_ = reinterpret_cast<void*>(art_quick_to_interpreter_bridge);
+ }
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
@@ -589,6 +598,13 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
std::string boot_image_option("--boot-image=");
+ if (heap->GetImageSpace() == nullptr) {
+ // TODO If we get a dex2dex compiler working we could maybe use that, OTOH since we are likely
+ // out of space anyway it might not matter.
+ *error_msg = StringPrintf("Cannot create oat file for '%s' because we are running "
+ "without an image.", dex_filename);
+ return false;
+ }
boot_image_option += heap->GetImageSpace()->GetImageLocation();
std::string dex_file_option("--dex-file=");
@@ -879,16 +895,17 @@
oat_location = cache_location.c_str();
}
+ bool has_flock = true;
// Definitely need to lock now.
if (!scoped_flock.HasFile()) {
std::string error_msg;
if (!scoped_flock.Init(oat_location, &error_msg)) {
error_msgs->push_back(error_msg);
- return false;
+ has_flock = false;
}
}
- if (Runtime::Current()->IsDex2OatEnabled()) {
+ if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
// Create the oat file.
open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
oat_location, error_msgs));
@@ -1003,6 +1020,9 @@
const InstructionSet instruction_set) {
Runtime* runtime = Runtime::Current();
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (image_space == nullptr) {
+ return false;
+ }
uint32_t image_oat_checksum = 0;
if (instruction_set == kRuntimeISA) {
const ImageHeader& image_header = image_space->GetImageHeader();
@@ -1020,6 +1040,10 @@
std::string* error_msg) {
Runtime* runtime = Runtime::Current();
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (image_space == nullptr) {
+ *error_msg = "No image space for verification against";
+ return false;
+ }
// If the requested instruction set is the same as the current runtime,
// we can use the checksums directly. If it isn't, we'll have to read the
@@ -1270,13 +1294,15 @@
std::string error_msg;
if (runtime->CanRelocate()) {
// Run relocation
- const std::string& image_location =
- Runtime::Current()->GetHeap()->GetImageSpace()->GetImageLocation();
- if (odex_checksum_verified && should_patch_system) {
- ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
- } else if (cache_checksum_verified && should_patch_cache) {
- CHECK(have_dalvik_cache);
- ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
+ gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace();
+ if (space != nullptr) {
+ const std::string& image_location = space->GetImageLocation();
+ if (odex_checksum_verified && should_patch_system) {
+ ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
+ } else if (cache_checksum_verified && should_patch_cache) {
+ CHECK(have_dalvik_cache);
+ ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
+ }
}
}
if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
@@ -1340,6 +1366,12 @@
const std::string& image_location,
InstructionSet isa,
std::string* error_msg) {
+ if (!Runtime::Current()->GetHeap()->HasImageSpace()) {
+ // We don't have an image space so there is no point in trying to patchoat.
+ LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
+ << "running without an image. Attempting to use oat file for interpretation.";
+ return GetInterpretedOnlyOat(input_oat, isa, error_msg);
+ }
if (!Runtime::Current()->IsDex2OatEnabled()) {
// We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
// input_oat but make sure we only do interpretation on it's dex files.
@@ -1402,6 +1434,7 @@
Runtime* runtime = Runtime::Current();
int32_t real_patch_delta;
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ CHECK(image_space != nullptr);
if (isa == Runtime::Current()->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
real_patch_delta = image_header.GetPatchDelta();
@@ -1423,6 +1456,10 @@
void* real_image_oat_offset;
int32_t real_patch_delta;
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (image_space == nullptr) {
+ *error_msg = "No image space present";
+ return false;
+ }
if (isa == Runtime::Current()->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
real_image_checksum = image_header.GetOatChecksum();
@@ -2275,8 +2312,11 @@
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) {
- return; // OAT file unavailable.
+ if (runtime->IsCompiler() || runtime->GetHeap()->HasImageSpace()) {
+ return; // OAT file unavailable.
+ }
}
+
const DexFile& dex_file = klass->GetDexFile();
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
CHECK(dex_class_def != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5694149..71ce468 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -60,8 +60,8 @@
explicit ClassLinker(InternTable* intern_table);
~ClassLinker();
- // Initialize class linker by bootstraping from dex files
- void InitFromCompiler(const std::vector<const DexFile*>& boot_class_path)
+ // Initialize class linker by bootstraping from dex files.
+ void InitWithoutImage(const std::vector<const DexFile*>& boot_class_path)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Initialize class linker from one or more images.
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 9a22c15..49df62d 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -38,7 +38,8 @@
} else {
result = instrumentation->GetQuickCodeFor(method);
}
- DCHECK(result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
+ DCHECK((result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline())
+ || !Runtime::Current()->GetHeap()->HasImageSpace());
bool interpreter_entry = (result == GetQuickToInterpreterBridge());
instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object,
method, lr, interpreter_entry);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f0b7685..cd1cdd3 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -202,15 +202,21 @@
// Requested begin for the alloc space, to follow the mapped image and oat files
byte* requested_alloc_space_begin = nullptr;
if (!image_file_name.empty()) {
+ std::string error_msg;
space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
- image_instruction_set);
- CHECK(image_space != nullptr) << "Failed to create space for " << image_file_name;
- AddSpace(image_space);
- // Oat files referenced by image files immediately follow them in memory, ensure alloc space
- // isn't going to get in the middle
- byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
- CHECK_GT(oat_file_end_addr, image_space->End());
- requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
+ image_instruction_set,
+ &error_msg);
+ if (image_space != nullptr) {
+ AddSpace(image_space);
+ // Oat files referenced by image files immediately follow them in memory, ensure alloc space
+ // isn't going to get in the middle
+ byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
+ CHECK_GT(oat_file_end_addr, image_space->End());
+ requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
+ } else {
+ LOG(WARNING) << "Could not create image space with image file '" << image_file_name << "'. "
+ << "Attempting to fall back to imageless running. Error was: " << error_msg;
+ }
}
/*
requested_alloc_space_begin -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
@@ -579,6 +585,9 @@
}
bool Heap::IsCompilingBoot() const {
+ if (!Runtime::Current()->IsCompiler()) {
+ return false;
+ }
for (const auto& space : continuous_spaces_) {
if (space->IsImageSpace() || space->IsZygoteSpace()) {
return false;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1d10af2..0139ecc 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -283,8 +283,8 @@
}
ImageSpace* ImageSpace::Create(const char* image_location,
- const InstructionSet image_isa) {
- std::string error_msg;
+ const InstructionSet image_isa,
+ std::string* error_msg) {
std::string system_filename;
bool has_system = false;
std::string cache_filename;
@@ -296,14 +296,18 @@
ImageSpace* space;
bool relocate = Runtime::Current()->ShouldRelocate();
+ bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
if (found_image) {
const std::string* image_filename;
bool is_system = false;
bool relocated_version_used = false;
if (relocate) {
- CHECK(dalvik_cache_exists) << "Requiring relocation for image " << image_location << " "
- << "at " << system_filename << " but we do not have any "
- << "dalvik_cache to find/place it in.";
+ if (!dalvik_cache_exists) {
+ *error_msg = StringPrintf("Requiring relocation for image '%s' at '%s' but we do not have "
+ "any dalvik_cache to find/place it in.",
+ image_location, system_filename.c_str());
+ return nullptr;
+ }
if (has_system) {
if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
// We already have a relocated version
@@ -311,14 +315,20 @@
relocated_version_used = true;
} else {
// We cannot have a relocated version, Relocate the system one and use it.
- if (RelocateImage(image_location, cache_filename.c_str(), image_isa,
- &error_msg)) {
+ if (can_compile && RelocateImage(image_location, cache_filename.c_str(), image_isa,
+ error_msg)) {
relocated_version_used = true;
image_filename = &cache_filename;
} else {
- LOG(FATAL) << "Unable to relocate image " << image_location << " "
- << "from " << system_filename << " to " << cache_filename << ": "
- << error_msg;
+ std::string reason;
+ if (can_compile) {
+ reason = StringPrintf(": %s", error_msg->c_str());
+ } else {
+ reason = " because image dex2oat is disabled.";
+ }
+ *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s'%s",
+ image_location, system_filename.c_str(),
+ cache_filename.c_str(), reason.c_str());
return nullptr;
}
}
@@ -351,7 +361,7 @@
// descriptor (and the associated exclusive lock) to be released when
// we leave Create.
ScopedFlock image_lock;
- image_lock.Init(image_filename->c_str(), &error_msg);
+ image_lock.Init(image_filename->c_str(), error_msg);
LOG(INFO) << "Using image file " << image_filename->c_str() << " for image location "
<< image_location;
// If we are in /system we can assume the image is good. We can also
@@ -359,7 +369,7 @@
// matches) since this is only different by the offset. We need this to
// make sure that host tests continue to work.
space = ImageSpace::Init(image_filename->c_str(), image_location,
- !(is_system || relocated_version_used), &error_msg);
+ !(is_system || relocated_version_used), error_msg);
}
if (space != nullptr) {
return space;
@@ -374,29 +384,38 @@
<< "but image failed to load: " << error_msg;
return nullptr;
} else if (is_system) {
- LOG(FATAL) << "Failed to load /system image '" << *image_filename << "': " << error_msg;
+ *error_msg = StringPrintf("Failed to load /system image '%s': %s",
+ image_filename->c_str(), error_msg->c_str());
return nullptr;
} else {
- LOG(WARNING) << error_msg;
+ LOG(WARNING) << *error_msg;
}
}
- CHECK(dalvik_cache_exists) << "No place to put generated image.";
- CHECK(GenerateImage(cache_filename, &error_msg))
- << "Failed to generate image '" << cache_filename << "': " << error_msg;
- {
+ if (!can_compile) {
+ *error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";
+ return nullptr;
+ } else if (!dalvik_cache_exists) {
+ *error_msg = StringPrintf("No place to put generated image.");
+ return nullptr;
+ } else if (!GenerateImage(cache_filename, error_msg)) {
+ *error_msg = StringPrintf("Failed to generate image '%s': %s",
+ cache_filename.c_str(), error_msg->c_str());
+ return nullptr;
+ } else {
// Note that we must not use the file descriptor associated with
// ScopedFlock::GetFile to Init the image file. We want the file
// descriptor (and the associated exclusive lock) to be released when
// we leave Create.
ScopedFlock image_lock;
- image_lock.Init(cache_filename.c_str(), &error_msg);
- space = ImageSpace::Init(cache_filename.c_str(), image_location, true, &error_msg);
+ image_lock.Init(cache_filename.c_str(), error_msg);
+ space = ImageSpace::Init(cache_filename.c_str(), image_location, true, error_msg);
+ if (space == nullptr) {
+ *error_msg = StringPrintf("Failed to load generated image '%s': %s",
+ cache_filename.c_str(), error_msg->c_str());
+ }
+ return space;
}
- if (space == nullptr) {
- LOG(FATAL) << "Failed to load generated image '" << cache_filename << "': " << error_msg;
- }
- return space;
}
void ImageSpace::VerifyImageAllocations() {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 6be3b8f..28ebca6 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -43,7 +43,7 @@
// 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)
+ static ImageSpace* Create(const char* image, InstructionSet image_isa, std::string* error_msg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Reads the image header from the specified image location for the
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 26360d7..9ed5e7b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -224,6 +224,7 @@
is_zygote_ = false;
must_relocate_ = kDefaultMustRelocate;
dex2oat_enabled_ = true;
+ image_dex2oat_enabled_ = true;
if (kPoisonHeapReferences) {
// kPoisonHeapReferences currently works only with the interpreter only.
// TODO: make it work with the compiler.
@@ -434,6 +435,10 @@
dex2oat_enabled_ = false;
} else if (option == "-Xdex2oat") {
dex2oat_enabled_ = true;
+ } else if (option == "-Xnoimage-dex2oat") {
+ image_dex2oat_enabled_ = false;
+ } else if (option == "-Ximage-dex2oat") {
+ image_dex2oat_enabled_ = true;
} else if (option == "-Xint") {
interpreter_only_ = true;
} else if (StartsWith(option, "-Xgc:")) {
@@ -790,6 +795,7 @@
UsageMessage(stream, " -Xpatchoat:filename\n");
UsageMessage(stream, " -X[no]relocate\n");
UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n");
+ UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 1afd610..ba1b848 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -51,6 +51,7 @@
bool is_zygote_;
bool must_relocate_;
bool dex2oat_enabled_;
+ bool image_dex2oat_enabled_;
std::string patchoat_executable_;
bool interpreter_only_;
bool is_explicit_gc_disabled_;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index ba53c43..233c9d4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -102,6 +102,7 @@
is_concurrent_gc_enabled_(true),
is_explicit_gc_disabled_(false),
dex2oat_enabled_(true),
+ image_dex2oat_enabled_(true),
default_stack_size_(0),
heap_(nullptr),
max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
@@ -396,10 +397,18 @@
// Restore main thread state to kNative as expected by native code.
Thread* self = Thread::Current();
+
self->TransitionFromRunnableToSuspended(kNative);
started_ = true;
+ if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
+ class_linker_->EnsureInitialized(klass, true, true);
+ }
+
// InitNativeMethods needs to be after started_ so that the classes
// it touches will have methods linked to the oat file if necessary.
InitNativeMethods();
@@ -535,6 +544,24 @@
VLOG(startup) << "Runtime::StartDaemonThreads exiting";
}
+static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
+ std::vector<const DexFile*>& dex_files) {
+ size_t failure_count = 0;
+ for (size_t i = 0; i < dex_filenames.size(); i++) {
+ const char* dex_filename = dex_filenames[i].c_str();
+ std::string error_msg;
+ if (!OS::FileExists(dex_filename)) {
+ LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
+ continue;
+ }
+ if (!DexFile::Open(dex_filename, dex_filename, &error_msg, &dex_files)) {
+ LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
+ ++failure_count;
+ }
+ }
+ return failure_count;
+}
+
bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
@@ -559,6 +586,7 @@
is_zygote_ = options->is_zygote_;
is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
dex2oat_enabled_ = options->dex2oat_enabled_;
+ image_dex2oat_enabled_ = options->image_dex2oat_enabled_;
vfprintf_ = options->hook_vfprintf_;
exit_ = options->hook_exit_;
@@ -677,10 +705,24 @@
if (kIsDebugBuild) {
GetHeap()->GetImageSpace()->VerifyImageAllocations();
}
+ } else if (!IsCompiler() || !image_dex2oat_enabled_) {
+ std::vector<std::string> dex_filenames;
+ Split(boot_class_path_string_, ':', dex_filenames);
+ std::vector<const DexFile*> boot_class_path;
+ OpenDexFiles(dex_filenames, boot_class_path);
+ class_linker_->InitWithoutImage(boot_class_path);
+ // TODO: Should we move the following to InitWithoutImage?
+ SetInstructionSet(kRuntimeISA);
+ for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+ Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
+ if (!HasCalleeSaveMethod(type)) {
+ SetCalleeSaveMethod(CreateCalleeSaveMethod(type), type);
+ }
+ }
} else {
CHECK(options->boot_class_path_ != NULL);
CHECK_NE(options->boot_class_path_->size(), 0U);
- class_linker_->InitFromCompiler(*options->boot_class_path_);
+ class_linker_->InitWithoutImage(*options->boot_class_path_);
}
CHECK(class_linker_ != NULL);
verifier::MethodVerifier::Init();
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 34ccdcb..784241e 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -107,7 +107,11 @@
}
bool IsDex2OatEnabled() const {
- return dex2oat_enabled_;
+ return dex2oat_enabled_ && IsImageDex2OatEnabled();
+ }
+
+ bool IsImageDex2OatEnabled() const {
+ return image_dex2oat_enabled_;
}
CompilerCallbacks* GetCompilerCallbacks() {
@@ -509,6 +513,7 @@
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
bool dex2oat_enabled_;
+ bool image_dex2oat_enabled_;
std::string compiler_executable_;
std::string patchoat_executable_;