summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/Android.common_test.mk10
-rw-r--r--build/Android.gtest.mk32
-rw-r--r--cmdline/cmdline_parser_test.cc7
-rw-r--r--cmdline/cmdline_types.h2
-rw-r--r--runtime/Android.mk1
-rw-r--r--runtime/base/logging.h1
-rw-r--r--runtime/class_linker.cc928
-rw-r--r--runtime/class_linker.h107
-rw-r--r--runtime/common_runtime_test.cc11
-rw-r--r--runtime/common_runtime_test.h8
-rw-r--r--runtime/native/dalvik_system_DexFile.cc360
-rw-r--r--runtime/oat_file_assistant.cc952
-rw-r--r--runtime/oat_file_assistant.h431
-rw-r--r--runtime/oat_file_assistant_test.cc874
-rw-r--r--runtime/utils.cc17
-rw-r--r--runtime/utils.h6
-rw-r--r--runtime/utils_test.cc7
-rw-r--r--test/MultiDex/Main.java22
-rw-r--r--test/MultiDex/Second.java21
-rw-r--r--test/MultiDex/main.list1
20 files changed, 2471 insertions, 1327 deletions
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index b536fe4a4d..c750399387 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -160,6 +160,10 @@ endef
# $(4): additional dependencies
# $(5): a make variable used to collate target dependencies, e.g ART_TEST_TARGET_OAT_HelloWorld_DEX
# $(6): a make variable used to collate host dependencies, e.g ART_TEST_HOST_OAT_HelloWorld_DEX
+#
+# If the input test directory contains a file called main.list, then a
+# multi-dex file is created passing main.list as the --main-dex-list argument
+# to dx.
define build-art-test-dex
ifeq ($(ART_BUILD_TARGET),true)
include $(CLEAR_VARS)
@@ -172,6 +176,9 @@ define build-art-test-dex
LOCAL_JAVA_LIBRARIES := $(TARGET_CORE_JARS)
LOCAL_MODULE_PATH := $(3)
LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
+ ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
+ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
+ endif
include $(BUILD_JAVA_LIBRARY)
$(5) := $$(LOCAL_INSTALLED_MODULE)
endif
@@ -184,6 +191,9 @@ define build-art-test-dex
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_test.mk $(4)
LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS)
LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
+ ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
+ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
+ endif
include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
$(6) := $$(LOCAL_INSTALLED_MODULE)
endif
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 7ab4d64d45..6967808b8e 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -28,6 +28,7 @@ GTEST_DEX_DIRECTORIES := \
GetMethodSignature \
Interfaces \
Main \
+ MultiDex \
MyClass \
MyClassNatives \
Nested \
@@ -45,6 +46,19 @@ $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,art-gte
$(ART_TARGET_NATIVETEST_OUT),art/build/Android.gtest.mk,ART_TEST_TARGET_GTEST_$(dir)_DEX, \
ART_TEST_HOST_GTEST_$(dir)_DEX)))
+# Create rules for MainStripped, a copy of Main with the classes.dex stripped
+# for the oat file assistant tests.
+ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
+$(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX)
+ cp $< $@
+ $(call dexpreopt-remove-classes.dex,$@)
+
+$(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX)
+ cp $< $@
+ $(call dexpreopt-remove-classes.dex,$@)
+
# Dex file dependencies for each gtest.
ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MyClass Nested Statics StaticsFromCode
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod
@@ -52,6 +66,7 @@ ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
+ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_proxy_test_DEX_DEPS := Interfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
@@ -62,6 +77,9 @@ ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_oat_file_assistant_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
+
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
@@ -141,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \
runtime/mirror/object_test.cc \
runtime/monitor_pool_test.cc \
runtime/monitor_test.cc \
+ runtime/oat_file_assistant_test.cc \
runtime/parsed_options_test.cc \
runtime/reference_table_test.cc \
runtime/thread_pool_test.cc \
@@ -462,12 +481,12 @@ valgrind-test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_VALGRIND_GTEST
endef # define-art-gtest
ifeq ($(ART_BUILD_TARGET),true)
- $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,)))
- $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler)))
+ $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,libbacktrace)))
+ $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler libbacktrace)))
endif
ifeq ($(ART_BUILD_HOST),true)
- $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,)))
- $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler)))
+ $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,libbacktrace)))
+ $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler libbacktrace)))
endif
# Used outside the art project to get a list of the current tests
@@ -559,6 +578,9 @@ ART_GTEST_elf_writer_test_HOST_DEPS :=
ART_GTEST_elf_writer_test_TARGET_DEPS :=
ART_GTEST_jni_compiler_test_DEX_DEPS :=
ART_GTEST_jni_internal_test_DEX_DEPS :=
+ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
+ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
ART_GTEST_object_test_DEX_DEPS :=
ART_GTEST_proxy_test_DEX_DEPS :=
ART_GTEST_reflection_test_DEX_DEPS :=
@@ -567,5 +589,7 @@ ART_GTEST_transaction_test_DEX_DEPS :=
ART_VALGRIND_DEPENDENCIES :=
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
+ART_TEST_HOST_GTEST_MainStripped_DEX :=
+ART_TEST_TARGET_GTEST_MainStripped_DEX :=
GTEST_DEX_DIRECTORIES :=
LOCAL_PATH :=
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 130eed2807..9f873b321a 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -260,6 +260,13 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) {
}
EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage); // invalid verbose opt
+
+ {
+ const char* log_args = "-verbose:oat";
+ LogVerbosity log_verbosity = LogVerbosity();
+ log_verbosity.oat = true;
+ EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+ }
} // TEST_F
// TODO: Enable this b/19274810
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index de99278389..03165ed5a6 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -591,6 +591,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
log_verbosity.jni = true;
} else if (verbose_options[j] == "monitor") {
log_verbosity.monitor = true;
+ } else if (verbose_options[j] == "oat") {
+ log_verbosity.oat = true;
} else if (verbose_options[j] == "profiler") {
log_verbosity.profiler = true;
} else if (verbose_options[j] == "signals") {
diff --git a/runtime/Android.mk b/runtime/Android.mk
index c5cf89014e..6490434c96 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -135,6 +135,7 @@ LIBART_COMMON_SRC_FILES := \
native/sun_misc_Unsafe.cc \
oat.cc \
oat_file.cc \
+ oat_file_assistant.cc \
object_lock.cc \
offsets.cc \
os_linux.cc \
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 3d007ba1e5..014f4ab5bf 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -45,6 +45,7 @@ struct LogVerbosity {
bool jit;
bool jni;
bool monitor;
+ bool oat;
bool profiler;
bool signals;
bool startup;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f0c8819a52..785adf5e7e 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -48,6 +48,7 @@
#include "leb128.h"
#include "oat.h"
#include "oat_file.h"
+#include "oat_file_assistant.h"
#include "object_lock.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
@@ -662,77 +663,6 @@ void ClassLinker::RunRootClinits() {
}
}
-bool ClassLinker::GenerateOatFile(const char* dex_filename,
- int oat_fd,
- const char* oat_cache_filename,
- std::string* error_msg) {
- Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
- std::string dex2oat(Runtime::Current()->GetCompilerExecutable());
-
- 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=");
- dex_file_option += dex_filename;
-
- std::string oat_fd_option("--oat-fd=");
- StringAppendF(&oat_fd_option, "%d", oat_fd);
-
- std::string oat_location_option("--oat-location=");
- oat_location_option += oat_cache_filename;
-
- std::vector<std::string> argv;
- argv.push_back(dex2oat);
- argv.push_back("--runtime-arg");
- argv.push_back("-classpath");
- argv.push_back("--runtime-arg");
- argv.push_back(Runtime::Current()->GetClassPathString());
-
- Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
-
- if (!Runtime::Current()->IsVerificationEnabled()) {
- argv.push_back("--compiler-filter=verify-none");
- }
-
- if (Runtime::Current()->MustRelocateIfPossible()) {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xrelocate");
- } else {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xnorelocate");
- }
-
- if (!kIsTargetBuild) {
- argv.push_back("--host");
- }
-
- argv.push_back(boot_image_option);
- argv.push_back(dex_file_option);
- argv.push_back(oat_fd_option);
- argv.push_back(oat_location_option);
- const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
- for (size_t i = 0; i < compiler_options.size(); ++i) {
- argv.push_back(compiler_options[i].c_str());
- }
-
- if (!Exec(argv, error_msg)) {
- // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
- // died. Ignore unlink failure, propagate the original error.
- TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
- return false;
- }
-
- return true;
-}
-
const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
WriterMutexLock mu(Thread::Current(), dex_lock_);
if (kIsDebugBuild) {
@@ -782,504 +712,81 @@ const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_loc
return nullptr;
}
+std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
+ const char* dex_location, const char* oat_location,
+ std::vector<std::string>* error_msgs) {
+ CHECK(error_msgs != nullptr);
-// Loads all multi dex files from the given oat file returning true on success.
-//
-// Parameters:
-// oat_file - the oat file to load from
-// dex_location - the dex location used to generate the oat file
-// dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
-// generated - whether or not the oat_file existed before or was just (re)generated
-// error_msgs - any error messages will be appended here
-// dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
-static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
- const char* dex_location,
- const uint32_t* dex_location_checksum,
- bool generated,
- std::vector<std::string>* error_msgs,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- if (oat_file == nullptr) {
- return false;
- }
-
- size_t old_size = dex_files->size(); // To rollback on error.
-
- bool success = true;
- for (size_t i = 0; success; ++i) {
- std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
- const char* next_name = next_name_str.c_str();
-
- uint32_t next_location_checksum;
- uint32_t* next_location_checksum_pointer = &next_location_checksum;
- std::string error_msg;
- if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
- // When i=0 the multidex name should be the same as the location name. We already have the
- // checksum it so we don't need to recompute it.
- if (dex_location_checksum == nullptr) {
- next_location_checksum_pointer = nullptr;
- } else {
- next_location_checksum = *dex_location_checksum;
- }
- } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
- DCHECK_EQ(false, i == 0 && generated);
- next_location_checksum_pointer = nullptr;
- }
+ // Verify we aren't holding the mutator lock, which could starve GC if we
+ // have to generate or relocate an oat file.
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current());
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
+ OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
+ !Runtime::Current()->IsAotCompiler());
- if (oat_dex_file == nullptr) {
- if (i == 0 && generated) {
- error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
- " file'%s'", dex_location, next_location_checksum,
- oat_file->GetLocation().c_str());
- error_msgs->push_back(error_msg);
- }
- break; // Not found, done.
- }
-
- // Checksum test. Test must succeed when generated.
- success = !generated;
- if (next_location_checksum_pointer != nullptr) {
- success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
- }
-
- if (success) {
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file.get() == nullptr) {
- success = false;
- error_msgs->push_back(error_msg);
- } else {
- dex_files->push_back(std::move(dex_file));
- }
- }
-
- // When we generated the file, we expect success, or something is terribly wrong.
- CHECK_EQ(false, generated && !success)
- << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
- << std::hex << " dex_location_checksum=" << next_location_checksum
- << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
- }
-
- if (dex_files->size() == old_size) {
- success = false; // We did not even find classes.dex
- }
-
- if (success) {
- return true;
- } else {
- dex_files->erase(dex_files->begin() + old_size, dex_files->end());
- return false;
+ // Lock the target oat location to avoid races generating and loading the
+ // oat file.
+ std::string error_msg;
+ if (!oat_file_assistant.Lock(&error_msg)) {
+ // Don't worry too much if this fails. If it does fail, it's unlikely we
+ // can generate an oat file anyway.
+ VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
}
-}
-
-// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This
-// complicates the loading process, as we should not use an iterative loading process, because that
-// would register the oat file and dex files that come before the broken one. Instead, check all
-// multidex ahead of time.
-bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
- std::vector<std::string>* error_msgs,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- // 1) Check whether we have an open oat file.
- // This requires a dex checksum, use the "primary" one.
- uint32_t dex_location_checksum;
- uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
- bool have_checksum = true;
- std::string checksum_error_msg;
- if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
- // This happens for pre-opted files since the corresponding dex files are no longer on disk.
- dex_location_checksum_pointer = nullptr;
- have_checksum = false;
- }
-
- bool needs_registering = false;
-
- const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
- dex_location_checksum_pointer);
- std::unique_ptr<const OatFile> open_oat_file(
- oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);
-
- // 2) If we do not have an open one, maybe there's one on disk already.
-
- // In case the oat file is not open, we play a locking game here so
- // that if two different processes race to load and register or generate
- // (or worse, one tries to open a partial generated file) we will be okay.
- // This is actually common with apps that use DexClassLoader to work
- // around the dex method reference limit and that have a background
- // service running in a separate process.
- ScopedFlock scoped_flock;
-
- if (open_oat_file.get() == nullptr) {
- if (oat_location != nullptr) {
- // Can only do this if we have a checksum, else error.
- if (!have_checksum) {
- error_msgs->push_back(checksum_error_msg);
- return false;
- }
- std::string error_msg;
-
- // We are loading or creating one in the future. Time to set up the file lock.
- if (!scoped_flock.Init(oat_location, &error_msg)) {
- error_msgs->push_back(error_msg);
- return false;
- }
-
- // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
- open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
- oat_location, &error_msg));
-
- if (open_oat_file.get() == nullptr) {
- std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
- dex_location, oat_location, error_msg.c_str());
- VLOG(class_linker) << compound_msg;
- error_msgs->push_back(compound_msg);
- }
- } else {
- // TODO: What to lock here?
- bool obsolete_file_cleanup_failed;
- open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location,
- dex_location_checksum_pointer,
- kRuntimeISA, error_msgs,
- &obsolete_file_cleanup_failed));
- // There's no point in going forward and eventually try to regenerate the
- // file if we couldn't remove the obsolete one. Mostly likely we will fail
- // with the same error when trying to write the new file.
- // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
- if (obsolete_file_cleanup_failed) {
- return false;
+ // Check if we already have an up-to-date oat file open.
+ const OatFile* source_oat_file = nullptr;
+ {
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
+ for (const OatFile* oat_file : oat_files_) {
+ CHECK(oat_file != nullptr);
+ if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
+ source_oat_file = oat_file;
+ break;
}
}
- needs_registering = true;
}
- // 3) If we have an oat file, check all contained multidex files for our dex_location.
- // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
- bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
- dex_location_checksum_pointer,
- false, error_msgs, dex_files);
- if (success) {
- const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it.
- if (needs_registering) {
- // We opened the oat file, so we must register it.
- RegisterOatFile(oat_file);
+ // If we didn't have an up-to-date oat file open, try to load one from disk.
+ if (source_oat_file == nullptr) {
+ // Update the oat file on disk if we can. This may fail, but that's okay.
+ // Best effort is all that matters here.
+ if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
+ LOG(WARNING) << error_msg;
}
- // If the file isn't executable we failed patchoat but did manage to get the dex files.
- return oat_file->IsExecutable();
- } else {
- if (needs_registering) {
- // We opened it, delete it.
- open_oat_file.reset();
- } else {
- open_oat_file.release(); // Do not delete open oat files.
- }
- }
-
- // 4) If it's not the case (either no oat file or mismatches), regenerate and load.
- // Need a checksum, fail else.
- if (!have_checksum) {
- error_msgs->push_back(checksum_error_msg);
- return false;
+ // Get the oat file on disk.
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ if (oat_file.get() != nullptr) {
+ source_oat_file = oat_file.release();
+ RegisterOatFile(source_oat_file);
+ }
}
- // Look in cache location if no oat_location is given.
- std::string cache_location;
- if (oat_location == nullptr) {
- // Use the dalvik cache.
- const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
- cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str());
- oat_location = cache_location.c_str();
- }
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
- 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);
- has_flock = false;
+ // Load the dex files from the oat file.
+ if (source_oat_file != nullptr) {
+ dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
+ if (dex_files.empty()) {
+ error_msgs->push_back("Failed to open dex files from "
+ + source_oat_file->GetLocation());
}
}
- 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));
- }
-
- // Failed, bail.
- if (open_oat_file.get() == nullptr) {
- // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
+ // Fall back to running out of the original dex file if we couldn't load any
+ // dex_files from the oat file.
+ if (dex_files.empty()) {
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- std::string error_msg;
- if (!DexFile::Open(dex_location, dex_location, &error_msg, dex_files)) {
- error_msgs->push_back(error_msg);
+ if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
+ LOG(WARNING) << error_msg;
+ error_msgs->push_back("Failed to open dex files from "
+ + std::string(dex_location));
}
} else {
error_msgs->push_back("Fallback mode disabled, skipping dex files.");
}
- return false;
- }
-
- // Try to load again, but stronger checks.
- success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
- dex_location_checksum_pointer,
- true, error_msgs, dex_files);
- if (success) {
- RegisterOatFile(open_oat_file.release());
- return true;
- } else {
- return false;
- }
-}
-
-const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location,
- uint32_t dex_location_checksum,
- const char* oat_location,
- std::string* error_msg) {
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsAotCompiler(), error_msg));
- if (oat_file.get() == nullptr) {
- *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
- error_msg->c_str());
- return nullptr;
- }
- Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- if (image_space != nullptr) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- uint32_t expected_image_oat_checksum = image_header.GetOatChecksum();
- uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum();
- if (expected_image_oat_checksum != actual_image_oat_checksum) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of "
- "0x%x, found 0x%x", oat_location, expected_image_oat_checksum,
- actual_image_oat_checksum);
- return nullptr;
- }
-
- uintptr_t expected_image_oat_offset = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
- uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin();
- if (expected_image_oat_offset != actual_image_oat_offset) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %"
- PRIuPTR ", found %ud", oat_location, expected_image_oat_offset,
- actual_image_oat_offset);
- return nullptr;
- }
- int32_t expected_patch_delta = image_header.GetPatchDelta();
- int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
- if (expected_patch_delta != actual_patch_delta) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
- " found %d", oat_location, expected_patch_delta, actual_patch_delta);
- return nullptr;
- }
- }
-
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
- &dex_location_checksum);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location,
- dex_location);
- return nullptr;
- }
- uint32_t expected_dex_checksum = dex_location_checksum;
- uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum();
- if (expected_dex_checksum != actual_dex_checksum) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, "
- "found 0x%x", oat_location, expected_dex_checksum,
- actual_dex_checksum);
- return nullptr;
- }
- std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg));
- if (dex_file.get() != nullptr) {
- return oat_file.release();
- } else {
- return nullptr;
- }
-}
-
-const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location,
- int fd, const char* oat_location,
- std::vector<std::string>* error_msgs) {
- // Generate the output oat file for the dex file
- VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
- std::string error_msg;
- if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) {
- CHECK(!error_msg.empty());
- error_msgs->push_back(error_msg);
- return nullptr;
- }
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsAotCompiler(),
- &error_msg));
- if (oat_file.get() == nullptr) {
- std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
- oat_location, error_msg.c_str());
- error_msgs->push_back(compound_msg);
- return nullptr;
- }
-
- return oat_file.release();
-}
-
-bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file,
- 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();
- image_oat_checksum = image_header.GetOatChecksum();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), instruction_set));
- image_oat_checksum = image_header->GetOatChecksum();
- }
- return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum;
-}
-
-bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file,
- const InstructionSet instruction_set,
- 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
- // image header from the image for the right instruction set.
- uint32_t image_oat_checksum = 0;
- uintptr_t image_oat_data_begin = 0;
- int32_t image_patch_delta = 0;
- if (instruction_set == runtime->GetInstructionSet()) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- image_oat_checksum = image_header.GetOatChecksum();
- image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
- image_patch_delta = image_header.GetPatchDelta();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), instruction_set));
- image_oat_checksum = image_header->GetOatChecksum();
- image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
- image_patch_delta = image_header->GetPatchDelta();
- }
- const OatHeader& oat_header = oat_file->GetOatHeader();
- bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum);
-
- // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks.
- if (!oat_file->IsPic()) {
- ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta)
- && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin);
- }
- if (!ret) {
- *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
- oat_file->GetLocation().c_str(),
- oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
- oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
- oat_file->GetOatHeader().GetImagePatchDelta(),
- image_oat_checksum, image_oat_data_begin, image_patch_delta);
- }
- return ret;
-}
-
-bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file,
- const char* dex_location,
- uint32_t dex_location_checksum,
- const InstructionSet instruction_set,
- std::string* error_msg) {
- if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) {
- return false;
- }
-
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
- &dex_location_checksum);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
- oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
- for (const OatFile::OatDexFile* oat_dex_file_in : oat_file->GetOatDexFiles()) {
- *error_msg += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x",
- oat_file->GetLocation().c_str(),
- oat_dex_file_in->GetDexFileLocation().c_str(),
- oat_dex_file_in->GetDexFileLocationChecksum());
- }
- return false;
- }
-
- DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
- return true;
-}
-
-bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
- const char* dex_location,
- const uint32_t* dex_location_checksum,
- std::string* error_msg) {
- CHECK(oat_file != nullptr);
- CHECK(dex_location != nullptr);
- std::unique_ptr<const DexFile> dex_file;
- if (dex_location_checksum == nullptr) {
- // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
- // up-to-date. This is the common case in user builds for jar's and apk's in the /system
- // directory.
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, nullptr);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
- "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
- error_msg->c_str());
- return false;
- }
- dex_file = oat_dex_file->OpenDexFile(error_msg);
- } else {
- bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
- kRuntimeISA, error_msg);
- if (!verified) {
- return false;
- }
- dex_file = oat_file->GetOatDexFile(dex_location,
- dex_location_checksum)->OpenDexFile(error_msg);
- }
- return dex_file.get() != nullptr;
-}
-
-const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
- const char* dex_location,
- const uint32_t* dex_location_checksum,
- InstructionSet isa,
- std::vector<std::string>* error_msgs,
- bool* obsolete_file_cleanup_failed) {
- *obsolete_file_cleanup_failed = false;
- bool already_opened = false;
- std::string dex_location_str(dex_location);
- std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
- &already_opened,
- obsolete_file_cleanup_failed,
- error_msgs));
- std::string error_msg;
- if (oat_file.get() == nullptr) {
- error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
- dex_location));
- return nullptr;
- } else if (oat_file->IsExecutable() &&
- !VerifyOatWithDexFile(oat_file.get(), dex_location,
- dex_location_checksum, &error_msg)) {
- error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
- "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
- error_msg.c_str()));
- return nullptr;
- } else if (!oat_file->IsExecutable() &&
- Runtime::Current()->GetHeap()->HasImageSpace() &&
- !VerifyOatImageChecksum(oat_file.get(), isa)) {
- error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for "
- "dex location '%s'. Image checksum incorrect.",
- oat_file->GetLocation().c_str(), dex_location));
- return nullptr;
- } else {
- return oat_file.release();
}
+ return dex_files;
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
@@ -1294,335 +801,6 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string&
return nullptr;
}
-const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
- InstructionSet isa,
- bool *already_opened,
- bool *obsolete_file_cleanup_failed,
- std::vector<std::string>* error_msgs) {
- // Find out if we've already opened the file
- const OatFile* ret = nullptr;
- std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
- ret = FindOpenedOatFileFromOatLocation(odex_filename);
- if (ret != nullptr) {
- *already_opened = true;
- return ret;
- }
-
- std::string dalvik_cache;
- bool have_android_data = false;
- bool have_dalvik_cache = false;
- bool is_global_cache = false;
- GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
- &have_android_data, &have_dalvik_cache, &is_global_cache);
- std::string cache_filename;
- if (have_dalvik_cache) {
- cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
- ret = FindOpenedOatFileFromOatLocation(cache_filename);
- if (ret != nullptr) {
- *already_opened = true;
- return ret;
- }
- } else {
- // If we need to relocate we should just place odex back where it started.
- cache_filename = odex_filename;
- }
-
- ret = nullptr;
-
- // We know that neither the odex nor the cache'd version is already in use, if it even exists.
- //
- // Now we do the following:
- // 1) Try and open the odex version
- // 2) If present, checksum-verified & relocated correctly return it
- // 3) Close the odex version to free up its address space.
- // 4) Try and open the cache version
- // 5) If present, checksum-verified & relocated correctly return it
- // 6) Close the cache version to free up its address space.
- // 7) If we should relocate:
- // a) If we have opened and checksum-verified the odex version relocate it to
- // 'cache_filename' and return it
- // b) If we have opened and checksum-verified the cache version relocate it in place and return
- // it. This should not happen often (I think only the run-test's will hit this case).
- // 8) If the cache-version was present we should delete it since it must be obsolete if we get to
- // this point.
- // 9) Return nullptr
-
- *already_opened = false;
- const Runtime* runtime = Runtime::Current();
- CHECK(runtime != nullptr);
- bool executable = !runtime->IsAotCompiler();
-
- std::string odex_error_msg;
- bool should_patch_system = false;
- bool odex_checksum_verified = false;
- bool have_system_odex = false;
- {
- // There is a high probability that both these oat files map similar/the same address
- // spaces so we must scope them like this so they each gets its turn.
- std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
- nullptr,
- executable, &odex_error_msg));
- if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
- &odex_checksum_verified,
- &odex_error_msg)) {
- return odex_oat_file.release();
- } else {
- if (odex_checksum_verified) {
- // We can just relocate
- should_patch_system = true;
- odex_error_msg = "Image Patches are incorrect";
- }
- if (odex_oat_file.get() != nullptr) {
- have_system_odex = true;
- }
- }
- }
-
- std::string cache_error_msg;
- bool should_patch_cache = false;
- bool cache_checksum_verified = false;
- if (have_dalvik_cache) {
- std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
- nullptr,
- executable, &cache_error_msg));
- if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
- &cache_checksum_verified,
- &cache_error_msg)) {
- return cache_oat_file.release();
- } else if (cache_checksum_verified) {
- // We can just relocate
- should_patch_cache = true;
- cache_error_msg = "Image Patches are incorrect";
- }
- } else if (have_android_data) {
- // dalvik_cache does not exist but android data does. This means we should be able to create
- // it, so we should try.
- GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
- }
-
- ret = nullptr;
- std::string error_msg;
- if (runtime->CanRelocate()) {
- // Run relocation
- 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);
- }
- } else if (have_system_odex) {
- ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg);
- }
- }
- if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
- // implicitly: were able to fine where the cached version is but we were unable to use it,
- // either as a destination for relocation or to open a file. We should delete it if it is
- // there.
- if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
- std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
- "searching for dex file %s: %s",
- cache_filename.c_str(), dex_location.c_str(),
- strerror(errno));
- error_msgs->push_back(rm_error_msg);
- VLOG(class_linker) << rm_error_msg;
- // Let the caller know that we couldn't remove the obsolete file.
- // This is a good indication that further writes may fail as well.
- *obsolete_file_cleanup_failed = true;
- }
- }
- if (ret == nullptr) {
- VLOG(class_linker) << error_msg;
- error_msgs->push_back(error_msg);
- std::string relocation_msg;
- if (runtime->CanRelocate()) {
- relocation_msg = StringPrintf(" and relocation failed");
- }
- if (have_dalvik_cache && cache_checksum_verified) {
- error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
- "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
- cache_filename.c_str(), cache_error_msg.c_str(),
- relocation_msg.c_str());
- } else {
- error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
- "dalvik_cache availible)%s.", odex_filename.c_str(),
- odex_error_msg.c_str(), relocation_msg.c_str());
- }
- VLOG(class_linker) << error_msg;
- error_msgs->push_back(error_msg);
- }
- return ret;
-}
-
-const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path,
- InstructionSet isa,
- std::string* error_msg) {
- // We open it non-executable
- std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
- if (output.get() == nullptr) {
- return nullptr;
- }
- if (!Runtime::Current()->GetHeap()->HasImageSpace() ||
- VerifyOatImageChecksum(output.get(), isa)) {
- return output.release();
- } else {
- *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
- oat_path.c_str());
- return nullptr;
- }
-}
-
-const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
- const std::string& output_oat,
- const std::string& image_location,
- InstructionSet isa,
- std::string* error_msg) {
- Runtime* runtime = Runtime::Current();
- DCHECK(runtime != nullptr);
- if (!runtime->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->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.
- LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
- << "disabled. Attempting to use oat file for interpretation";
- return GetInterpretedOnlyOat(input_oat, isa, error_msg);
- }
- Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
- std::string patchoat(runtime->GetPatchoatExecutable());
-
- std::string isa_arg("--instruction-set=");
- isa_arg += GetInstructionSetString(isa);
- std::string input_oat_filename_arg("--input-oat-file=");
- input_oat_filename_arg += input_oat;
- std::string output_oat_filename_arg("--output-oat-file=");
- output_oat_filename_arg += output_oat;
- std::string patched_image_arg("--patched-image-location=");
- patched_image_arg += image_location;
-
- std::vector<std::string> argv;
- argv.push_back(patchoat);
- argv.push_back(isa_arg);
- argv.push_back(input_oat_filename_arg);
- argv.push_back(output_oat_filename_arg);
- argv.push_back(patched_image_arg);
-
- std::string command_line(Join(argv, ' '));
- LOG(INFO) << "Relocate Oat File: " << command_line;
- bool success = Exec(argv, error_msg);
- if (success) {
- std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
- !runtime->IsAotCompiler(), error_msg));
- bool checksum_verified = false;
- if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
- error_msg)) {
- return output.release();
- } else if (output.get() != nullptr) {
- *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
- "but output file '%s' failed verifcation: %s",
- input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
- } else {
- *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
- "but was unable to open output file '%s': %s",
- input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
- }
- } else if (!runtime->IsAotCompiler()) {
- // patchoat failed which means we probably don't have enough room to place the output oat file,
- // instead of failing we should just run the interpreter from the dex files in the input oat.
- LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
- << "for interpretation. patchoat failure was: " << *error_msg;
- return GetInterpretedOnlyOat(input_oat, isa, error_msg);
- } else {
- *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
- "failed: %s", input_oat.c_str(), output_oat.c_str(),
- error_msg->c_str());
- }
- return nullptr;
-}
-
-bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
- bool* checksum_verified,
- std::string* error_msg) {
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- if (image_space == nullptr) {
- *error_msg = "No image space present";
- return false;
- }
- uint32_t real_image_checksum;
- void* real_image_oat_offset;
- int32_t real_patch_delta;
- if (isa == runtime->GetInstructionSet()) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- real_image_checksum = image_header.GetOatChecksum();
- real_image_oat_offset = image_header.GetOatDataBegin();
- real_patch_delta = image_header.GetPatchDelta();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), isa));
- real_image_checksum = image_header->GetOatChecksum();
- real_image_oat_offset = image_header->GetOatDataBegin();
- real_patch_delta = image_header->GetPatchDelta();
- }
-
- const OatHeader& oat_header = oat_file->GetOatHeader();
- std::string compound_msg;
-
- uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
- *checksum_verified = oat_image_checksum == real_image_checksum;
- if (!*checksum_verified) {
- StringAppendF(&compound_msg, " Oat Image Checksum Incorrect (expected 0x%x, received 0x%x)",
- real_image_checksum, oat_image_checksum);
- }
-
- bool offset_verified;
- bool patch_delta_verified;
-
- if (!oat_file->IsPic()) {
- // If an oat file is not PIC, we need to check that the image is at the expected location and
- // patched in the same way.
- void* oat_image_oat_offset =
- reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
- offset_verified = oat_image_oat_offset == real_image_oat_offset;
- if (!offset_verified) {
- StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
- real_image_oat_offset, oat_image_oat_offset);
- }
-
- int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
- patch_delta_verified = oat_patch_delta == real_patch_delta;
- if (!patch_delta_verified) {
- StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, "
- "received 0x%x)", real_patch_delta, oat_patch_delta);
- }
- } else {
- // If an oat file is PIC, we ignore offset and patching delta.
- offset_verified = true;
- patch_delta_verified = true;
- }
-
- bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
- if (!ret) {
- *error_msg = "Oat file failed to verify:" + compound_msg;
- }
- return ret;
-}
-
-const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
- std::string* error_msg) {
- const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
- if (oat_file != nullptr) {
- return oat_file;
- }
- return OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsAotCompiler(), error_msg);
-}
-
void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
DCHECK(obj != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 6570c5f02a..75fbdf3f59 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -313,33 +313,25 @@ class ClassLinker {
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Generate an oat file from a dex file
- bool GenerateOatFile(const char* dex_filename,
- int oat_fd,
- const char* oat_cache_filename,
- std::string* error_msg)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
-
- // Find or create the oat file holding dex_location. Then load all corresponding dex files
- // (if multidex) into the given vector.
- bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
- std::vector<std::string>* error_msgs,
- std::vector<std::unique_ptr<const DexFile>>* dex_files)
+ // Finds or creates the oat file holding dex_location. Then loads and returns
+ // all corresponding dex files (there may be more than one dex file loaded
+ // in the case of multidex).
+ // This may return the original, unquickened dex files if the oat file could
+ // not be generated.
+ //
+ // Returns an empty vector if the dex files could not be loaded. In this
+ // case, there will be at least one error message returned describing why no
+ // dex files could not be loaded. The 'error_msgs' argument must not be
+ // null, regardless of whether there is an error or not.
+ //
+ // This method should not be called with the mutator_lock_ held, because it
+ // could end up starving GC if we need to generate or relocate any oat
+ // files.
+ std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
+ const char* dex_location, const char* oat_location,
+ std::vector<std::string>* error_msgs)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
- // Returns true if the given oat file has the same image checksum as the image it is paired with.
- static bool VerifyOatImageChecksum(const OatFile* oat_file, const InstructionSet instruction_set);
- // Returns true if the oat file checksums match with the image and the offsets are such that it
- // could be loaded with it.
- static bool VerifyOatChecksums(const OatFile* oat_file, const InstructionSet instruction_set,
- std::string* error_msg);
- // Returns true if oat file contains the dex file with the given location and checksum.
- static bool VerifyOatAndDexFileChecksums(const OatFile* oat_file,
- const char* dex_location,
- uint32_t dex_location_checksum,
- InstructionSet instruction_set,
- std::string* error_msg);
-
// Allocate an instance of a java.lang.Object.
mirror::Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -612,73 +604,9 @@ class ClassLinker {
const uint32_t* dex_location_checksum)
LOCKS_EXCLUDED(dex_lock_);
- // Will open the oat file directly without relocating, even if we could/should do relocation.
- const OatFile* FindOatFileFromOatLocation(const std::string& oat_location,
- std::string* error_msg)
- LOCKS_EXCLUDED(dex_lock_);
-
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
LOCKS_EXCLUDED(dex_lock_);
- const OatFile* OpenOatFileFromDexLocation(const std::string& dex_location,
- InstructionSet isa,
- bool* already_opened,
- bool* obsolete_file_cleanup_failed,
- std::vector<std::string>* error_msg)
- LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
-
- const OatFile* GetInterpretedOnlyOat(const std::string& oat_path,
- InstructionSet isa,
- std::string* error_msg);
-
- const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
- const std::string& image_location, InstructionSet isa,
- std::string* error_msg)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
-
- bool CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
- bool* checksum_verified, std::string* error_msg);
-
- // Note: will not register the oat file.
- const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
- uint32_t dex_location_checksum,
- const char* oat_location,
- std::string* error_msg)
- LOCKS_EXCLUDED(dex_lock_);
-
- // Creates the oat file from the dex_location to the oat_location. Needs a file descriptor for
- // the file to be written, which is assumed to be under a lock.
- const OatFile* CreateOatFileForDexLocation(const char* dex_location,
- int fd, const char* oat_location,
- std::vector<std::string>* error_msgs)
- LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
-
- // Finds an OatFile that contains a DexFile for the given a DexFile location.
- //
- // Note 1: this will not check open oat files, which are assumed to be stale when this is run.
- // Note 2: Does not register the oat file. It is the caller's job to register if the file is to
- // be kept.
- const OatFile* FindOatFileContainingDexFileFromDexLocation(const char* dex_location,
- const uint32_t* dex_location_checksum,
- InstructionSet isa,
- std::vector<std::string>* error_msgs,
- bool* obsolete_file_cleanup_failed)
- LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
-
- // Verifies:
- // - that the oat file contains the dex file (with a matching checksum, which may be null if the
- // file was pre-opted)
- // - the checksums of the oat file (against the image space)
- // - the checksum of the dex file against dex_location_checksum
- // - that the dex file can be opened
- // Returns true iff all verification succeed.
- //
- // The dex_location is the dex location as stored in the oat file header.
- // (see DexFile::GetDexCanonicalLocation for a description of location conventions)
- bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
- const uint32_t* dex_location_checksum,
- std::string* error_msg);
-
mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass,
mirror::Class* proxy_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -803,7 +731,6 @@ class ClassLinker {
friend class ImageWriter; // for GetClassRoots
friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
- friend class ElfPatcher; // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub
friend class NoDex2OatTest; // for FindOpenedOatFileForDexFile
friend class NoPatchoatTest; // for FindOpenedOatFileForDexFile
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index b7ffd609b7..1c8a8925d8 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -36,6 +36,7 @@
#include "gtest/gtest.h"
#include "jni_internal.h"
#include "mirror/class_loader.h"
+#include "mem_map.h"
#include "noop_compiler_callbacks.h"
#include "os.h"
#include "runtime-inl.h"
@@ -194,6 +195,7 @@ std::string CommonRuntimeTest::GetCoreOatLocation() {
std::unique_ptr<const DexFile> CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
+ MemMap::Init();
if (!DexFile::Open(location, location, &error_msg, &dex_files)) {
LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
UNREACHABLE();
@@ -225,10 +227,12 @@ void CommonRuntimeTest::SetUp() {
options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
SetUpRuntimeOptions(&options);
+ PreRuntimeCreate();
if (!Runtime::Create(options, false)) {
LOG(FATAL) << "Failed to create runtime";
return;
}
+ PostRuntimeCreate();
runtime_.reset(Runtime::Current());
class_linker_ = runtime_->GetClassLinker();
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
@@ -335,7 +339,7 @@ std::string CommonRuntimeTest::GetTestAndroidRoot() {
#define ART_TARGET_NATIVETEST_DIR_STRING ""
#endif
-std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+std::string CommonRuntimeTest::GetTestDexFileName(const char* name) {
CHECK(name != nullptr);
std::string filename;
if (IsHost()) {
@@ -347,6 +351,11 @@ std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(
filename += "art-gtest-";
filename += name;
filename += ".jar";
+ return filename;
+}
+
+std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+ std::string filename = GetTestDexFileName(name);
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files);
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 9efea84743..cce8485a42 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -101,11 +101,19 @@ class CommonRuntimeTest : public testing::Test {
virtual void TearDown();
+ // Called before the runtime is created.
+ virtual void PreRuntimeCreate() {}
+
+ // Called after the runtime is created.
+ virtual void PostRuntimeCreate() {}
+
// Gets the path of the specified dex file for host or target.
static std::string GetDexFileName(const std::string& jar_prefix);
std::string GetTestAndroidRoot();
+ std::string GetTestDexFileName(const char* name);
+
std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index e1fe3eb918..c182a4d9ad 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -16,31 +16,17 @@
#include "dalvik_system_DexFile.h"
-#include <algorithm>
-#include <set>
-#include <fcntl.h>
-#ifdef __linux__
-#include <sys/sendfile.h>
-#else
-#include <sys/socket.h>
-#endif
-#include <sys/stat.h>
-#include <unistd.h>
-
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
#include "class_linker.h"
#include "common_throws.h"
#include "dex_file-inl.h"
-#include "gc/space/image_space.h"
-#include "gc/space/space-inl.h"
-#include "image.h"
#include "jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "mirror/string.h"
-#include "oat.h"
+#include "oat_file_assistant.h"
#include "os.h"
#include "profiler.h"
#include "runtime.h"
@@ -51,11 +37,6 @@
#include "well_known_classes.h"
#include "zip_archive.h"
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wshadow"
-#include "ScopedFd.h"
-#pragma GCC diagnostic pop
-
namespace art {
static std::unique_ptr<std::vector<const DexFile*>>
@@ -182,10 +163,9 @@ static jobject DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSource
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
- bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
- &dex_files);
+ dex_files = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs);
- if (success || !dex_files.empty()) {
+ if (!dex_files.empty()) {
jlongArray array = ConvertNativeToJavaArray(env, dex_files);
if (array == nullptr) {
ScopedObjectAccess soa(env);
@@ -197,9 +177,6 @@ static jobject DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSource
}
return array;
} else {
- // The vector should be empty after a failed loading attempt.
- DCHECK_EQ(0U, dex_files.size());
-
ScopedObjectAccess soa(env);
CHECK(!error_msgs.empty());
// The most important message is at the end. So set up nesting by going forward, which will
@@ -320,40 +297,6 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie
return result;
}
-static void CopyProfileFile(const char* oldfile, const char* newfile) {
- ScopedFd src(open(oldfile, O_RDONLY));
- if (src.get() == -1) {
- PLOG(ERROR) << "Failed to open profile file " << oldfile
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
- struct stat stat_src;
- if (fstat(src.get(), &stat_src) == -1) {
- PLOG(ERROR) << "Failed to get stats for profile file " << oldfile
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
- // Create the copy with rw------- (only accessible by system)
- ScopedFd dst(open(newfile, O_WRONLY|O_CREAT|O_TRUNC, 0600));
- if (dst.get() == -1) {
- PLOG(ERROR) << "Failed to create/write prev profile file " << newfile
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
-#ifdef __linux__
- if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
-#else
- off_t len;
- if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
-#endif
- PLOG(ERROR) << "Failed to copy profile file " << oldfile << " to " << newfile
- << ". My uid:gid is " << getuid() << ":" << getgid();
- }
-}
-
// Java: dalvik.system.DexFile.UP_TO_DATE
static const jbyte kUpToDate = 0;
// Java: dalvik.system.DexFile.DEXOPT_NEEDED
@@ -361,102 +304,8 @@ static const jbyte kPatchoatNeeded = 1;
// Java: dalvik.system.DexFile.PATCHOAT_NEEDED
static const jbyte kDexoptNeeded = 2;
-template <const bool kVerboseLogging, const bool kReasonLogging>
-static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
- InstructionSet target_instruction_set,
- bool* oat_is_pic) {
- std::string error_msg;
- std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
- nullptr,
- false, &error_msg));
- if (oat_file.get() == nullptr) {
- // Note that even though this is kDexoptNeeded, we use
- // kVerboseLogging instead of the usual kReasonLogging since it is
- // the common case on first boot and very spammy.
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename
- << "' for file location '" << filename << "': " << error_msg;
- }
- error_msg.clear();
- return kDexoptNeeded;
- }
-
- // Pass-up the information about if this is PIC.
- // TODO: Refactor this function to be less complicated.
- *oat_is_pic = oat_file->IsPic();
-
- bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
- uint32_t location_checksum = 0;
- const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
- kReasonLogging);
- if (oat_dex_file != nullptr) {
- // If its not possible to read the classes.dex assume up-to-date as we won't be able to
- // compile it anyway.
- if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded found precompiled stripped file: "
- << filename << " for " << oat_filename << ": " << error_msg;
- }
- if (ClassLinker::VerifyOatChecksums(oat_file.get(), target_instruction_set, &error_msg)) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " is up-to-date for " << filename;
- }
- return kUpToDate;
- } else if (should_relocate_if_possible &&
- ClassLinker::VerifyOatImageChecksum(oat_file.get(), target_instruction_set)) {
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " needs to be relocated for " << filename;
- }
- return kPatchoatNeeded;
- } else {
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " is out of date for " << filename;
- }
- return kDexoptNeeded;
- }
- // If we get here the file is out of date and we should use the system one to relocate.
- } else {
- if (ClassLinker::VerifyOatAndDexFileChecksums(oat_file.get(), filename, location_checksum,
- target_instruction_set, &error_msg)) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " is up-to-date for " << filename;
- }
- return kUpToDate;
- } else if (location_checksum == oat_dex_file->GetDexFileLocationChecksum()
- && should_relocate_if_possible
- && ClassLinker::VerifyOatImageChecksum(oat_file.get(), target_instruction_set)) {
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " needs to be relocated for " << filename;
- }
- return kPatchoatNeeded;
- } else {
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " is out of date for " << filename;
- }
- return kDexoptNeeded;
- }
- }
- } else {
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
- << " does not contain " << filename;
- }
- return kDexoptNeeded;
- }
-}
-
static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename,
const char* pkgname, const char* instruction_set, const jboolean defer) {
- // Spammy logging for kUpToDate
- const bool kVerboseLogging = false;
- // Logging of reason for returning kDexoptNeeded or kPatchoatNeeded.
- const bool kReasonLogging = true;
if ((filename == nullptr) || !OS::FileExists(filename)) {
LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
@@ -466,117 +315,6 @@ static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename,
return kUpToDate;
}
- // Always treat elements of the bootclasspath as up-to-date. The
- // fact that code is running at all means that this should be true.
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- // TODO: We're assuming that the 64 and 32 bit runtimes have identical
- // class paths. isDexOptNeeded will not necessarily be called on a runtime
- // that has the same instruction set as the file being dexopted.
- const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath();
- for (size_t i = 0; i < boot_class_path.size(); i++) {
- if (boot_class_path[i]->GetLocation() == filename) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename;
- }
- return kUpToDate;
- }
- }
-
- bool force_system_only = false;
- bool require_system_version = false;
-
- // Check the profile file. We need to rerun dex2oat if the profile has changed significantly
- // since the last time, or it's new.
- // If the 'defer' argument is true then this will be retried later. In this case we
- // need to make sure that the profile file copy is not made so that we will get the
- // same result second time.
- std::string profile_file;
- std::string prev_profile_file;
- bool should_copy_profile = false;
- if (Runtime::Current()->GetProfilerOptions().IsEnabled() && (pkgname != nullptr)) {
- profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */)
- + std::string("/") + pkgname;
- prev_profile_file = profile_file + std::string("@old");
-
- struct stat profstat, prevstat;
- int e1 = stat(profile_file.c_str(), &profstat);
- int e1_errno = errno;
- int e2 = stat(prev_profile_file.c_str(), &prevstat);
- int e2_errno = errno;
- if (e1 < 0) {
- if (e1_errno != EACCES) {
- // No profile file, need to run dex2oat, unless we find a file in system
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal profile file " << profile_file << " doesn't exist. "
- << "Will check odex to see if we can find a working version.";
- }
- // Force it to only accept system files/files with versions in system.
- require_system_version = true;
- } else {
- LOG(INFO) << "DexFile_isDexOptNeededInternal recieved EACCES trying to stat profile file "
- << profile_file;
- }
- } else if (e2 == 0) {
- // There is a previous profile file. Check if the profile has changed significantly.
- // A change in profile is considered significant if X% (change_thr property) of the top K%
- // (compile_thr property) samples has changed.
- double top_k_threshold = Runtime::Current()->GetProfilerOptions().GetTopKThreshold();
- double change_threshold = Runtime::Current()->GetProfilerOptions().GetTopKChangeThreshold();
- double change_percent = 0.0;
- ProfileFile new_profile, old_profile;
- bool new_ok = new_profile.LoadFile(profile_file);
- bool old_ok = old_profile.LoadFile(prev_profile_file);
- if (!new_ok || !old_ok) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal Ignoring invalid profiles: "
- << (new_ok ? "" : profile_file) << " " << (old_ok ? "" : prev_profile_file);
- }
- } else {
- std::set<std::string> new_top_k, old_top_k;
- new_profile.GetTopKSamples(new_top_k, top_k_threshold);
- old_profile.GetTopKSamples(old_top_k, top_k_threshold);
- if (new_top_k.empty()) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal empty profile: " << profile_file;
- }
- // If the new topK is empty we shouldn't optimize so we leave the change_percent at 0.0.
- } else {
- std::set<std::string> diff;
- std::set_difference(new_top_k.begin(), new_top_k.end(), old_top_k.begin(), old_top_k.end(),
- std::inserter(diff, diff.end()));
- // TODO: consider using the usedPercentage instead of the plain diff count.
- change_percent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(new_top_k.size());
- if (kVerboseLogging) {
- std::set<std::string>::iterator end = diff.end();
- for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal new in topK: " << *it;
- }
- }
- }
- }
-
- if (change_percent > change_threshold) {
- if (kReasonLogging) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal size of new profile file " << profile_file <<
- " is significantly different from old profile file " << prev_profile_file << " (top "
- << top_k_threshold << "% samples changed in proportion of " << change_percent << "%)";
- }
- should_copy_profile = !defer;
- // Force us to only accept system files.
- force_system_only = true;
- }
- } else if (e2_errno == ENOENT) {
- // Previous profile does not exist. Make a copy of the current one.
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal previous profile doesn't exist: " << prev_profile_file;
- }
- should_copy_profile = !defer;
- } else {
- PLOG(INFO) << "Unable to stat previous profile file " << prev_profile_file;
- }
- }
-
const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
if (target_instruction_set == kNone) {
ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
@@ -585,75 +323,43 @@ static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename,
return 0;
}
- // Get the filename for odex file next to the dex file.
- std::string odex_filename(DexFilenameToOdexFilename(filename, target_instruction_set));
- // Get the filename for the dalvik-cache file
- std::string cache_dir;
- bool have_android_data = false;
- bool dalvik_cache_exists = false;
- bool is_global_cache = false;
- GetDalvikCache(instruction_set, false, &cache_dir, &have_android_data, &dalvik_cache_exists,
- &is_global_cache);
- std::string cache_filename; // was cache_location
- bool have_cache_filename = false;
- if (dalvik_cache_exists) {
- std::string error_msg;
- have_cache_filename = GetDalvikCacheFilename(filename, cache_dir.c_str(), &cache_filename,
- &error_msg);
- if (!have_cache_filename && kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeededInternal failed to find cache file for dex file " << filename
- << ": " << error_msg;
- }
- }
-
- bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
-
- jbyte dalvik_cache_decision = -1;
- // Lets try the cache first (since we want to load from there since thats where the relocated
- // versions will be).
- if (have_cache_filename && !force_system_only) {
- bool oat_is_pic;
- // We can use the dalvik-cache if we find a good file.
- dalvik_cache_decision =
- IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
- target_instruction_set, &oat_is_pic);
-
- // Apps that are compiled with --compile-pic never need to be patchoat-d
- if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
- dalvik_cache_decision = kUpToDate;
- }
- // We will only return DexOptNeeded if both the cache and system return it.
- if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
- CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
- << "May not return PatchoatNeeded when patching is disabled.";
- return dalvik_cache_decision;
- }
- // We couldn't find one thats easy. We should now try the system.
- }
+ // TODO: Verify the dex location is well formed, and throw an IOException if
+ // not?
- bool oat_is_pic;
- jbyte system_decision =
- IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
- target_instruction_set, &oat_is_pic);
- CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
- << "May not return PatchoatNeeded when patching is disabled.";
+ OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname);
- // Apps that are compiled with --compile-pic never need to be patchoat-d
- if (oat_is_pic && system_decision == kPatchoatNeeded) {
- system_decision = kUpToDate;
+ // Always treat elements of the bootclasspath as up-to-date.
+ if (oat_file_assistant.IsInBootClassPath()) {
+ return kUpToDate;
}
- if (require_system_version && system_decision == kPatchoatNeeded
- && dalvik_cache_decision == kUpToDate) {
- // We have a version from system relocated to the cache. Return it.
- return dalvik_cache_decision;
+ // TODO: Checking the profile should probably be done in the GetStatus()
+ // function. We have it here because GetStatus() should not be copying
+ // profile files. But who should be copying profile files?
+ if (oat_file_assistant.OdexFileIsOutOfDate()) {
+ // Needs recompile if profile has changed significantly.
+ if (Runtime::Current()->GetProfilerOptions().IsEnabled()) {
+ if (oat_file_assistant.IsProfileChangeSignificant()) {
+ if (!defer) {
+ oat_file_assistant.CopyProfileFile();
+ }
+ return kDexoptNeeded;
+ } else if (oat_file_assistant.ProfileExists()
+ && !oat_file_assistant.OldProfileExists()) {
+ if (!defer) {
+ oat_file_assistant.CopyProfileFile();
+ }
+ }
+ }
}
- if (should_copy_profile && system_decision == kDexoptNeeded) {
- CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
+ OatFileAssistant::Status status = oat_file_assistant.GetStatus();
+ switch (status) {
+ case OatFileAssistant::kUpToDate: return kUpToDate;
+ case OatFileAssistant::kNeedsRelocation: return kPatchoatNeeded;
+ case OatFileAssistant::kOutOfDate: return kDexoptNeeded;
}
-
- return system_decision;
+ UNREACHABLE();
}
static jbyte DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
new file mode 100644
index 0000000000..f87fa4f8f4
--- /dev/null
+++ b/runtime/oat_file_assistant.cc
@@ -0,0 +1,952 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "oat_file_assistant.h"
+
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/sendfile.h>
+#else
+#include <sys/socket.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <set>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "class_linker.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "image.h"
+#include "oat.h"
+#include "os.h"
+#include "profiler.h"
+#include "runtime.h"
+#include "ScopedFd.h"
+#include "utils.h"
+
+namespace art {
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+ const InstructionSet isa,
+ bool load_executable)
+ : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { }
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+ const char* oat_location,
+ const InstructionSet isa,
+ bool load_executable)
+ : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { }
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+ const InstructionSet isa,
+ bool load_executable,
+ const char* package_name)
+ : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { }
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+ const char* oat_location,
+ const InstructionSet isa,
+ bool load_executable,
+ const char* package_name)
+ : dex_location_(dex_location), isa_(isa),
+ package_name_(package_name), load_executable_(load_executable) {
+ if (load_executable_ && isa != kRuntimeISA) {
+ LOG(WARNING) << "OatFileAssistant: Load executable specified, "
+ << "but isa is not kRuntimeISA. Will not attempt to load executable.";
+ load_executable_ = false;
+ }
+
+ // If the user gave a target oat location, save that as the cached oat
+ // location now so we won't try to construct the default location later.
+ if (oat_location != nullptr) {
+ cached_oat_file_name_ = std::string(oat_location);
+ cached_oat_file_name_attempted_ = true;
+ cached_oat_file_name_found_ = true;
+ }
+
+ // If there is no package name given, we will not be able to find any
+ // profiles associated with this dex location. Preemptively mark that to
+ // be the case, rather than trying to find and load the profiles later.
+ // Similarly, if profiling is disabled.
+ if (package_name == nullptr
+ || !Runtime::Current()->GetProfilerOptions().IsEnabled()) {
+ profile_load_attempted_ = true;
+ profile_load_succeeded_ = false;
+ old_profile_load_attempted_ = true;
+ old_profile_load_succeeded_ = false;
+ }
+}
+
+OatFileAssistant::~OatFileAssistant() {
+ // Clean up the lock file.
+ if (lock_file_.get() != nullptr) {
+ lock_file_->Erase();
+ TEMP_FAILURE_RETRY(unlink(lock_file_->GetPath().c_str()));
+ }
+}
+
+bool OatFileAssistant::IsInBootClassPath() {
+ // Note: We check the current boot class path, regardless of the ISA
+ // specified by the user. This is okay, because the boot class path should
+ // be the same for all ISAs.
+ // TODO: Can we verify the boot class path is the same for all ISAs?
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const auto& boot_class_path = class_linker->GetBootClassPath();
+ for (size_t i = 0; i < boot_class_path.size(); i++) {
+ if (boot_class_path[i]->GetLocation() == std::string(dex_location_)) {
+ VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path";
+ return true;
+ }
+ }
+ return false;
+}
+
+bool OatFileAssistant::Lock(std::string* error_msg) {
+ CHECK(error_msg != nullptr);
+ CHECK(lock_file_.get() == nullptr) << "OatFileAssistant::Lock already acquired";
+
+ if (OatFileName() == nullptr) {
+ *error_msg = "Failed to determine lock file";
+ return false;
+ }
+ std::string lock_file_name = *OatFileName() + ".flock";
+
+ lock_file_.reset(OS::CreateEmptyFile(lock_file_name.c_str()));
+ if (lock_file_.get() == nullptr) {
+ *error_msg = "Failed to create lock file " + lock_file_name;
+ return false;
+ }
+
+ if (!flock_.Init(lock_file_.get(), error_msg)) {
+ TEMP_FAILURE_RETRY(unlink(lock_file_name.c_str()));
+ return false;
+ }
+ return true;
+}
+
+OatFileAssistant::Status OatFileAssistant::GetStatus() {
+ // TODO: If the profiling code is ever restored, it's worth considering
+ // whether we should check to see if the profile is out of date here.
+
+ if (OdexFileIsOutOfDate()) {
+ // The DEX file is not pre-compiled.
+ // TODO: What if the oat file is not out of date? Could we relocate it
+ // from itself?
+ return OatFileIsUpToDate() ? kUpToDate : kOutOfDate;
+ } else {
+ // The DEX file is pre-compiled. If the oat file isn't up to date, we can
+ // patch the pre-compiled version rather than recompiling.
+ if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
+ return kUpToDate;
+ } else {
+ return kNeedsRelocation;
+ }
+ }
+}
+
+bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
+ switch (GetStatus()) {
+ case kUpToDate: return true;
+ case kNeedsRelocation: return RelocateOatFile(error_msg);
+ case kOutOfDate: return GenerateOatFile(error_msg);
+ }
+ UNREACHABLE();
+}
+
+std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
+ if (OatFileIsUpToDate()) {
+ oat_file_released_ = true;
+ return std::move(cached_oat_file_);
+ }
+
+ if (OdexFileIsUpToDate()) {
+ oat_file_released_ = true;
+ return std::move(cached_odex_file_);
+ }
+
+ if (load_executable_) {
+ VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
+ << " attempting to fall back to interpreting oat file instead.";
+
+ if (!OatFileIsOutOfDate()) {
+ load_executable_ = false;
+ ClearOatFileCache();
+ if (!OatFileIsOutOfDate()) {
+ oat_file_released_ = true;
+ return std::move(cached_oat_file_);
+ }
+ }
+
+ if (!OdexFileIsOutOfDate()) {
+ load_executable_ = false;
+ ClearOdexFileCache();
+ if (!OdexFileIsOutOfDate()) {
+ oat_file_released_ = true;
+ return std::move(cached_odex_file_);
+ }
+ }
+ }
+
+ return std::unique_ptr<OatFile>();
+}
+
+std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
+ const OatFile& oat_file, const char* dex_location) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+
+ // Load the primary dex file.
+ std::string error_msg;
+ const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
+ dex_location, nullptr, false);
+ if (oat_dex_file == nullptr) {
+ LOG(WARNING) << "Attempt to load out-of-date oat file "
+ << oat_file.GetLocation() << " for dex location " << dex_location;
+ return std::vector<std::unique_ptr<const DexFile>>();
+ }
+
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
+ return std::vector<std::unique_ptr<const DexFile>>();
+ }
+ dex_files.push_back(std::move(dex_file));
+
+ // Load secondary multidex files
+ for (int i = 1; ; i++) {
+ std::string secondary_dex_location = DexFile::GetMultiDexClassesDexName(i, dex_location);
+ oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr, false);
+ if (oat_dex_file == NULL) {
+ // There are no more secondary dex files to load.
+ break;
+ }
+
+ dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
+ return std::vector<std::unique_ptr<const DexFile>>();
+ }
+ dex_files.push_back(std::move(dex_file));
+ }
+ return dex_files;
+}
+
+const std::string* OatFileAssistant::OdexFileName() {
+ if (!cached_odex_file_name_attempted_) {
+ CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
+ cached_odex_file_name_attempted_ = true;
+
+ std::string error_msg;
+ cached_odex_file_name_found_ = DexFilenameToOdexFilename(
+ dex_location_, isa_, &cached_odex_file_name_, &error_msg);
+ if (!cached_odex_file_name_found_) {
+ // If we can't figure out the odex file, we treat it as if the odex
+ // file was inaccessible.
+ LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
+ }
+ }
+ return cached_odex_file_name_found_ ? &cached_odex_file_name_ : nullptr;
+}
+
+bool OatFileAssistant::OdexFileExists() {
+ return GetOdexFile() != nullptr;
+}
+
+OatFileAssistant::Status OatFileAssistant::OdexFileStatus() {
+ if (OdexFileIsOutOfDate()) {
+ return kOutOfDate;
+ }
+ if (OdexFileIsUpToDate()) {
+ return kUpToDate;
+ }
+ return kNeedsRelocation;
+}
+
+bool OatFileAssistant::OdexFileIsOutOfDate() {
+ if (!odex_file_is_out_of_date_attempted_) {
+ odex_file_is_out_of_date_attempted_ = true;
+ const OatFile* odex_file = GetOdexFile();
+ if (odex_file == nullptr) {
+ cached_odex_file_is_out_of_date_ = true;
+ } else {
+ cached_odex_file_is_out_of_date_ = GivenOatFileIsOutOfDate(*odex_file);
+ }
+ }
+ return cached_odex_file_is_out_of_date_;
+}
+
+bool OatFileAssistant::OdexFileNeedsRelocation() {
+ return OdexFileStatus() == kNeedsRelocation;
+}
+
+bool OatFileAssistant::OdexFileIsUpToDate() {
+ if (!odex_file_is_up_to_date_attempted_) {
+ odex_file_is_up_to_date_attempted_ = true;
+ const OatFile* odex_file = GetOdexFile();
+ if (odex_file == nullptr) {
+ cached_odex_file_is_up_to_date_ = false;
+ } else {
+ cached_odex_file_is_up_to_date_ = GivenOatFileIsUpToDate(*odex_file);
+ }
+ }
+ return cached_odex_file_is_up_to_date_;
+}
+
+const std::string* OatFileAssistant::OatFileName() {
+ if (!cached_oat_file_name_attempted_) {
+ cached_oat_file_name_attempted_ = true;
+
+ // Compute the oat file name from the dex location.
+ CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
+
+ // TODO: The oat file assistant should be the definitive place for
+ // determining the oat file name from the dex location, not
+ // GetDalvikCacheFilename.
+ std::string cache_dir = StringPrintf("%s%s",
+ DalvikCacheDirectory().c_str(), GetInstructionSetString(isa_));
+ std::string error_msg;
+ cached_oat_file_name_found_ = GetDalvikCacheFilename(dex_location_,
+ cache_dir.c_str(), &cached_oat_file_name_, &error_msg);
+ if (!cached_oat_file_name_found_) {
+ // If we can't determine the oat file name, we treat the oat file as
+ // inaccessible.
+ LOG(WARNING) << "Failed to determine oat file name for dex location "
+ << dex_location_ << ": " << error_msg;
+ }
+ }
+ return cached_oat_file_name_found_ ? &cached_oat_file_name_ : nullptr;
+}
+
+bool OatFileAssistant::OatFileExists() {
+ return GetOatFile() != nullptr;
+}
+
+OatFileAssistant::Status OatFileAssistant::OatFileStatus() {
+ if (OatFileIsOutOfDate()) {
+ return kOutOfDate;
+ }
+ if (OatFileIsUpToDate()) {
+ return kUpToDate;
+ }
+ return kNeedsRelocation;
+}
+
+bool OatFileAssistant::OatFileIsOutOfDate() {
+ if (!oat_file_is_out_of_date_attempted_) {
+ oat_file_is_out_of_date_attempted_ = true;
+ const OatFile* oat_file = GetOatFile();
+ if (oat_file == nullptr) {
+ cached_oat_file_is_out_of_date_ = true;
+ } else {
+ cached_oat_file_is_out_of_date_ = GivenOatFileIsOutOfDate(*oat_file);
+ }
+ }
+ return cached_oat_file_is_out_of_date_;
+}
+
+bool OatFileAssistant::OatFileNeedsRelocation() {
+ return OatFileStatus() == kNeedsRelocation;
+}
+
+bool OatFileAssistant::OatFileIsUpToDate() {
+ if (!oat_file_is_up_to_date_attempted_) {
+ oat_file_is_up_to_date_attempted_ = true;
+ const OatFile* oat_file = GetOatFile();
+ if (oat_file == nullptr) {
+ cached_oat_file_is_up_to_date_ = false;
+ } else {
+ cached_oat_file_is_up_to_date_ = GivenOatFileIsUpToDate(*oat_file);
+ }
+ }
+ return cached_oat_file_is_up_to_date_;
+}
+
+OatFileAssistant::Status OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
+ // TODO: This could cause GivenOatFileIsOutOfDate to be called twice, which
+ // is more work than we need to do. If performance becomes a concern, and
+ // this method is actually called, this should be fixed.
+ if (GivenOatFileIsOutOfDate(file)) {
+ return kOutOfDate;
+ }
+ if (GivenOatFileIsUpToDate(file)) {
+ return kUpToDate;
+ }
+ return kNeedsRelocation;
+}
+
+bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
+ // Verify the dex checksum.
+ // Note: GetOatDexFile will return NULL if the dex checksum doesn't match
+ // what we provide, which verifies the primary dex checksum for us.
+ const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
+ const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(
+ dex_location_, dex_checksum_pointer, false);
+ if (oat_dex_file == NULL) {
+ return true;
+ }
+
+ // Verify the dex checksums for any secondary multidex files
+ for (int i = 1; ; i++) {
+ std::string secondary_dex_location
+ = DexFile::GetMultiDexClassesDexName(i, dex_location_);
+ const OatFile::OatDexFile* secondary_oat_dex_file
+ = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr, false);
+ if (secondary_oat_dex_file == NULL) {
+ // There are no more secondary dex files to check.
+ break;
+ }
+
+ std::string error_msg;
+ uint32_t expected_secondary_checksum = 0;
+ if (DexFile::GetChecksum(secondary_dex_location.c_str(),
+ &expected_secondary_checksum, &error_msg)) {
+ uint32_t actual_secondary_checksum
+ = secondary_oat_dex_file->GetDexFileLocationChecksum();
+ if (expected_secondary_checksum != actual_secondary_checksum) {
+ VLOG(oat) << "Dex checksum does not match for secondary dex: "
+ << secondary_dex_location
+ << ". Expected: " << expected_secondary_checksum
+ << ", Actual: " << actual_secondary_checksum;
+ return false;
+ }
+ } else {
+ // If we can't get the checksum for the secondary location, we assume
+ // the dex checksum is up to date for this and all other secondary dex
+ // files.
+ break;
+ }
+ }
+
+ // Verify the image checksum
+ const ImageInfo* image_info = GetImageInfo();
+ if (image_info == nullptr) {
+ VLOG(oat) << "No image for oat image checksum to match against.";
+ return true;
+ }
+
+ if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) {
+ VLOG(oat) << "Oat image checksum does not match image checksum.";
+ return true;
+ }
+
+ // The checksums are all good; the dex file is not out of date.
+ return false;
+}
+
+bool OatFileAssistant::GivenOatFileNeedsRelocation(const OatFile& file) {
+ return GivenOatFileStatus(file) == kNeedsRelocation;
+}
+
+bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) {
+ if (GivenOatFileIsOutOfDate(file)) {
+ return false;
+ }
+
+ if (file.IsPic()) {
+ return true;
+ }
+
+ const ImageInfo* image_info = GetImageInfo();
+ if (image_info == nullptr) {
+ VLOG(oat) << "No image for to check oat relocation against.";
+ return false;
+ }
+
+ // Verify the oat_data_begin recorded for the image in the oat file matches
+ // the actual oat_data_begin for boot.oat in the image.
+ const OatHeader& oat_header = file.GetOatHeader();
+ uintptr_t oat_data_begin = oat_header.GetImageFileLocationOatDataBegin();
+ if (oat_data_begin != image_info->oat_data_begin) {
+ VLOG(oat) << file.GetLocation() <<
+ ": Oat file image oat_data_begin (" << oat_data_begin << ")"
+ << " does not match actual image oat_data_begin ("
+ << image_info->oat_data_begin << ")";
+ return false;
+ }
+
+ // Verify the oat_patch_delta recorded for the image in the oat file matches
+ // the actual oat_patch_delta for the image.
+ int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+ if (oat_patch_delta != image_info->patch_delta) {
+ VLOG(oat) << file.GetLocation() <<
+ ": Oat file image patch delta (" << oat_patch_delta << ")"
+ << " does not match actual image patch delta ("
+ << image_info->patch_delta << ")";
+ return false;
+ }
+ return true;
+}
+
+bool OatFileAssistant::ProfileExists() {
+ return GetProfile() != nullptr;
+}
+
+bool OatFileAssistant::OldProfileExists() {
+ return GetOldProfile() != nullptr;
+}
+
+// TODO: The IsProfileChangeSignificant implementation was copied from likely
+// bit-rotted code.
+bool OatFileAssistant::IsProfileChangeSignificant() {
+ ProfileFile* profile = GetProfile();
+ if (profile == nullptr) {
+ return false;
+ }
+
+ ProfileFile* old_profile = GetOldProfile();
+ if (old_profile == nullptr) {
+ return false;
+ }
+
+ // TODO: The following code to compare two profile files should live with
+ // the rest of the profiler code, not the oat file assistant code.
+
+ // A change in profile is considered significant if X% (change_thr property)
+ // of the top K% (compile_thr property) samples has changed.
+ const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions();
+ const double top_k_threshold = options.GetTopKThreshold();
+ const double change_threshold = options.GetTopKChangeThreshold();
+ std::set<std::string> top_k, old_top_k;
+ profile->GetTopKSamples(top_k, top_k_threshold);
+ old_profile->GetTopKSamples(old_top_k, top_k_threshold);
+ std::set<std::string> diff;
+ std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(),
+ old_top_k.end(), std::inserter(diff, diff.end()));
+
+ // TODO: consider using the usedPercentage instead of the plain diff count.
+ double change_percent = 100.0 * static_cast<double>(diff.size())
+ / static_cast<double>(top_k.size());
+ std::set<std::string>::iterator end = diff.end();
+ for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
+ VLOG(oat) << "Profile new in topK: " << *it;
+ }
+
+ if (change_percent > change_threshold) {
+ VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_
+ << "has changed significantly: (top "
+ << top_k_threshold << "% samples changed in proportion of "
+ << change_percent << "%)";
+ return true;
+ }
+ return false;
+}
+
+// TODO: The CopyProfileFile implementation was copied from likely bit-rotted
+// code.
+void OatFileAssistant::CopyProfileFile() {
+ if (!ProfileExists()) {
+ return;
+ }
+
+ std::string profile_name = ProfileFileName();
+ std::string old_profile_name = OldProfileFileName();
+
+ ScopedFd src(open(old_profile_name.c_str(), O_RDONLY));
+ if (src.get() == -1) {
+ PLOG(WARNING) << "Failed to open profile file " << old_profile_name
+ << ". My uid:gid is " << getuid() << ":" << getgid();
+ return;
+ }
+
+ struct stat stat_src;
+ if (fstat(src.get(), &stat_src) == -1) {
+ PLOG(WARNING) << "Failed to get stats for profile file " << old_profile_name
+ << ". My uid:gid is " << getuid() << ":" << getgid();
+ return;
+ }
+
+ // Create the copy with rw------- (only accessible by system)
+ ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600));
+ if (dst.get() == -1) {
+ PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name
+ << ". My uid:gid is " << getuid() << ":" << getgid();
+ return;
+ }
+
+#ifdef __linux__
+ if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
+#else
+ off_t len;
+ if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
+#endif
+ PLOG(WARNING) << "Failed to copy profile file " << old_profile_name
+ << " to " << profile_name << ". My uid:gid is " << getuid()
+ << ":" << getgid();
+ }
+}
+
+bool OatFileAssistant::RelocateOatFile(std::string* error_msg) {
+ CHECK(error_msg != nullptr);
+
+ if (OdexFileName() == nullptr) {
+ *error_msg = "Patching of oat file for dex location "
+ + std::string(dex_location_)
+ + " not attempted because the odex file name could not be determined.";
+ return false;
+ }
+ const std::string& odex_file_name = *OdexFileName();
+
+ if (OatFileName() == nullptr) {
+ *error_msg = "Patching of oat file for dex location "
+ + std::string(dex_location_)
+ + " not attempted because the oat file name could not be determined.";
+ return false;
+ }
+ const std::string& oat_file_name = *OatFileName();
+
+ const ImageInfo* image_info = GetImageInfo();
+ Runtime* runtime = Runtime::Current();
+ if (image_info == nullptr) {
+ *error_msg = "Patching of oat file " + oat_file_name
+ + " not attempted because no image location was found.";
+ return false;
+ }
+
+ if (!runtime->IsDex2OatEnabled()) {
+ *error_msg = "Patching of oat file " + oat_file_name
+ + " not attempted because dex2oat is disabled";
+ return false;
+ }
+
+ std::vector<std::string> argv;
+ argv.push_back(runtime->GetPatchoatExecutable());
+ argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(isa_)));
+ argv.push_back("--input-oat-file=" + odex_file_name);
+ argv.push_back("--output-oat-file=" + oat_file_name);
+ argv.push_back("--patched-image-location=" + image_info->location);
+
+ std::string command_line(Join(argv, ' '));
+ if (!Exec(argv, error_msg)) {
+ // Manually delete the file. This ensures there is no garbage left over if
+ // the process unexpectedly died.
+ TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
+ return false;
+ }
+
+ // Mark that the oat file has changed and we should try to reload.
+ ClearOatFileCache();
+ return true;
+}
+
+bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
+ CHECK(error_msg != nullptr);
+
+ if (OatFileName() == nullptr) {
+ *error_msg = "Generation of oat file for dex location "
+ + std::string(dex_location_)
+ + " not attempted because the oat file name could not be determined.";
+ return false;
+ }
+ const std::string& oat_file_name = *OatFileName();
+
+ Runtime* runtime = Runtime::Current();
+ if (!runtime->IsDex2OatEnabled()) {
+ *error_msg = "Generation of oat file " + oat_file_name
+ + " not attempted because dex2oat is disabled";
+ return false;
+ }
+
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + std::string(dex_location_));
+ args.push_back("--oat-file=" + oat_file_name);
+
+ // dex2oat ignores missing dex files and doesn't report an error.
+ // Check explicitly here so we can detect the error properly.
+ // TODO: Why does dex2oat behave that way?
+ if (!OS::FileExists(dex_location_)) {
+ *error_msg = "Dex location " + std::string(dex_location_) + " does not exists.";
+ return false;
+ }
+
+ if (!Dex2Oat(args, error_msg)) {
+ // Manually delete the file. This ensures there is no garbage left over if
+ // the process unexpectedly died.
+ TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
+ return false;
+ }
+
+ // Mark that the oat file has changed and we should try to reload.
+ ClearOatFileCache();
+ return true;
+}
+
+bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,
+ std::string* error_msg) {
+ Runtime* runtime = Runtime::Current();
+ std::string image_location = ImageLocation();
+ if (image_location.empty()) {
+ *error_msg = "No image location found for Dex2Oat.";
+ return false;
+ }
+
+ std::vector<std::string> argv;
+ argv.push_back(runtime->GetCompilerExecutable());
+ argv.push_back("--runtime-arg");
+ argv.push_back("-classpath");
+ argv.push_back("--runtime-arg");
+ argv.push_back(runtime->GetClassPathString());
+ runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+ if (!runtime->IsVerificationEnabled()) {
+ argv.push_back("--compiler-filter=verify-none");
+ }
+
+ if (runtime->MustRelocateIfPossible()) {
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xrelocate");
+ } else {
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xnorelocate");
+ }
+
+ if (!kIsTargetBuild) {
+ argv.push_back("--host");
+ }
+
+ argv.push_back("--boot-image=" + image_location);
+
+ std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+ argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+ argv.insert(argv.end(), args.begin(), args.end());
+
+ std::string command_line(Join(argv, ' '));
+ return Exec(argv, error_msg);
+}
+
+bool OatFileAssistant::DexFilenameToOdexFilename(const std::string& location,
+ InstructionSet isa, std::string* odex_filename, std::string* error_msg) {
+ CHECK(odex_filename != nullptr);
+ CHECK(error_msg != nullptr);
+
+ // The odex file name is formed by replacing the dex_location extension with
+ // .odex and inserting an isa directory. For example:
+ // location = /foo/bar/baz.jar
+ // odex_location = /foo/bar/<isa>/baz.odex
+
+ // Find the directory portion of the dex location and add the isa directory.
+ size_t pos = location.rfind('/');
+ if (pos == std::string::npos) {
+ *error_msg = "Dex location " + location + " has no directory.";
+ return false;
+ }
+ std::string dir = location.substr(0, pos+1);
+ dir += std::string(GetInstructionSetString(isa));
+
+ // Find the file portion of the dex location.
+ std::string file;
+ if (pos == std::string::npos) {
+ file = location;
+ } else {
+ file = location.substr(pos+1);
+ }
+
+ // Get the base part of the file without the extension.
+ pos = file.rfind('.');
+ if (pos == std::string::npos) {
+ *error_msg = "Dex location " + location + " has no extension.";
+ return false;
+ }
+ std::string base = file.substr(0, pos);
+
+ *odex_filename = dir + "/" + base + ".odex";
+ return true;
+}
+
+std::string OatFileAssistant::DalvikCacheDirectory() {
+ // Note: We don't cache this, because it will only be called once by
+ // OatFileName, and we don't care about the performance of the profiling
+ // code, which isn't used in practice.
+
+ // TODO: The work done in GetDalvikCache is overkill for what we need.
+ // Ideally a new API for getting the DalvikCacheDirectory the way we want
+ // (without existence testing, creation, or death) is provided with the rest
+ // of the GetDalvikCache family of functions. Until such an API is in place,
+ // we use GetDalvikCache to avoid duplicating the logic for determining the
+ // dalvik cache directory.
+ std::string result;
+ bool have_android_data;
+ bool dalvik_cache_exists;
+ bool is_global_cache;
+ GetDalvikCache("", false, &result, &have_android_data, &dalvik_cache_exists, &is_global_cache);
+ return result;
+}
+
+std::string OatFileAssistant::ProfileFileName() {
+ if (package_name_ != nullptr) {
+ return DalvikCacheDirectory() + std::string("profiles/") + package_name_;
+ }
+ return "";
+}
+
+std::string OatFileAssistant::OldProfileFileName() {
+ std::string profile_name = ProfileFileName();
+ if (profile_name.empty()) {
+ return "";
+ }
+ return profile_name + "@old";
+}
+
+std::string OatFileAssistant::ImageLocation() {
+ Runtime* runtime = Runtime::Current();
+ const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (image_space == nullptr) {
+ return "";
+ }
+ return image_space->GetImageLocation();
+}
+
+const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
+ if (!required_dex_checksum_attempted) {
+ required_dex_checksum_attempted = true;
+ required_dex_checksum_found = false;
+ std::string error_msg;
+ CHECK(dex_location_ != nullptr) << "OatFileAssistant provided no dex location";
+ if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum, &error_msg)) {
+ required_dex_checksum_found = true;
+ } else {
+ // This can happen if the original dex file has been stripped from the
+ // apk.
+ VLOG(oat) << "OatFileAssistant: " << error_msg;
+
+ // Get the checksum from the odex if we can.
+ const OatFile* odex_file = GetOdexFile();
+ if (odex_file != nullptr) {
+ const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
+ dex_location_, nullptr, false);
+ if (odex_dex_file != nullptr) {
+ cached_required_dex_checksum = odex_dex_file->GetDexFileLocationChecksum();
+ required_dex_checksum_found = true;
+ }
+ }
+ }
+ }
+ return required_dex_checksum_found ? &cached_required_dex_checksum : nullptr;
+}
+
+const OatFile* OatFileAssistant::GetOdexFile() {
+ CHECK(!oat_file_released_) << "OdexFile called after oat file released.";
+ if (!odex_file_load_attempted_) {
+ odex_file_load_attempted_ = true;
+ if (OdexFileName() != nullptr) {
+ const std::string& odex_file_name = *OdexFileName();
+ std::string error_msg;
+ cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
+ odex_file_name.c_str(), nullptr, nullptr, load_executable_,
+ &error_msg));
+ if (cached_odex_file_.get() == nullptr) {
+ VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
+ << odex_file_name << ": " << error_msg;
+ }
+ }
+ }
+ return cached_odex_file_.get();
+}
+
+void OatFileAssistant::ClearOdexFileCache() {
+ odex_file_load_attempted_ = false;
+ cached_odex_file_.reset();
+ odex_file_is_out_of_date_attempted_ = false;
+ odex_file_is_up_to_date_attempted_ = false;
+}
+
+const OatFile* OatFileAssistant::GetOatFile() {
+ CHECK(!oat_file_released_) << "OatFile called after oat file released.";
+ if (!oat_file_load_attempted_) {
+ oat_file_load_attempted_ = true;
+ if (OatFileName() != nullptr) {
+ const std::string& oat_file_name = *OatFileName();
+ std::string error_msg;
+ cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
+ oat_file_name.c_str(), nullptr, nullptr, load_executable_, &error_msg));
+ if (cached_oat_file_.get() == nullptr) {
+ VLOG(oat) << "OatFileAssistant test for existing oat file "
+ << oat_file_name << ": " << error_msg;
+ }
+ }
+ }
+ return cached_oat_file_.get();
+}
+
+void OatFileAssistant::ClearOatFileCache() {
+ oat_file_load_attempted_ = false;
+ cached_oat_file_.reset();
+ oat_file_is_out_of_date_attempted_ = false;
+ oat_file_is_up_to_date_attempted_ = false;
+}
+
+const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
+ if (!image_info_load_attempted_) {
+ image_info_load_attempted_ = true;
+
+ Runtime* runtime = Runtime::Current();
+ const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (image_space != nullptr) {
+ cached_image_info_.location = image_space->GetImageLocation();
+
+ if (isa_ == kRuntimeISA) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ cached_image_info_.oat_checksum = image_header.GetOatChecksum();
+ cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
+ cached_image_info_.patch_delta = image_header.GetPatchDelta();
+ } else {
+ std::unique_ptr<ImageHeader> image_header(
+ gc::space::ImageSpace::ReadImageHeaderOrDie(
+ cached_image_info_.location.c_str(), isa_));
+ cached_image_info_.oat_checksum = image_header->GetOatChecksum();
+ cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
+ cached_image_info_.patch_delta = image_header->GetPatchDelta();
+ }
+ }
+ image_info_load_succeeded_ = (image_space != nullptr);
+ }
+ return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
+}
+
+ProfileFile* OatFileAssistant::GetProfile() {
+ if (!profile_load_attempted_) {
+ CHECK(package_name_ != nullptr)
+ << "pakage_name_ is nullptr: "
+ << "profile_load_attempted_ should have been true";
+ profile_load_attempted_ = true;
+ std::string profile_name = ProfileFileName();
+ if (!profile_name.empty()) {
+ profile_load_succeeded_ = cached_profile_.LoadFile(profile_name);
+ }
+ }
+ return profile_load_succeeded_ ? &cached_profile_ : nullptr;
+}
+
+ProfileFile* OatFileAssistant::GetOldProfile() {
+ if (!old_profile_load_attempted_) {
+ CHECK(package_name_ != nullptr)
+ << "pakage_name_ is nullptr: "
+ << "old_profile_load_attempted_ should have been true";
+ old_profile_load_attempted_ = true;
+ std::string old_profile_name = OldProfileFileName();
+ if (!old_profile_name.empty()) {
+ old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name);
+ }
+ }
+ return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr;
+}
+
+} // namespace art
+
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
new file mode 100644
index 0000000000..958b44048d
--- /dev/null
+++ b/runtime/oat_file_assistant.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OAT_FILE_ASSISTANT_H_
+#define ART_RUNTIME_OAT_FILE_ASSISTANT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "arch/instruction_set.h"
+#include "base/scoped_flock.h"
+#include "base/unix_file/fd_file.h"
+#include "oat_file.h"
+#include "os.h"
+#include "profiler.h"
+
+namespace art {
+
+// Class for assisting with oat file management.
+//
+// This class collects common utilities for determining the status of an oat
+// file on the device, updating the oat file, and loading the oat file.
+//
+// The oat file assistant is intended to be used with dex locations not on the
+// boot class path. See the IsInBootClassPath method for a way to check if the
+// dex location is in the boot class path.
+//
+// TODO: All the profiling related code is old and untested. It should either
+// be restored and tested, or removed.
+class OatFileAssistant {
+ public:
+ enum Status {
+ // kOutOfDate - An oat file is said to be out of date if the file does not
+ // exist, or is out of date with respect to the dex file or boot image.
+ kOutOfDate,
+
+ // kNeedsRelocation - An oat file is said to need relocation if the code
+ // is up to date, but not yet properly relocated for address space layout
+ // randomization (ASLR). In this case, the oat file is neither "out of
+ // date" nor "up to date".
+ kNeedsRelocation,
+
+ // kUpToDate - An oat file is said to be up to date if it is not out of
+ // date and has been properly relocated for the purposes of ASLR.
+ kUpToDate,
+ };
+
+ // Constructs an OatFileAssistant object to assist the oat file
+ // corresponding to the given dex location with the target instruction set.
+ //
+ // The dex_location must not be NULL and should remain available and
+ // unchanged for the duration of the lifetime of the OatFileAssistant object.
+ // Typically the dex_location is the absolute path to the original,
+ // un-optimized dex file.
+ //
+ //
+ // Note: Currently the dex_location must have an extension.
+ // TODO: Relax this restriction?
+ //
+ // The isa should be either the 32 bit or 64 bit variant for the current
+ // device. For example, on an arm device, use arm or arm64. An oat file can
+ // be loaded executable only if the ISA matches the current runtime.
+ OatFileAssistant(const char* dex_location, const InstructionSet isa,
+ bool load_executable);
+
+ // Constructs an OatFileAssistant, providing an explicit target oat_location
+ // to use instead of the standard oat location.
+ OatFileAssistant(const char* dex_location, const char* oat_location,
+ const InstructionSet isa, bool load_executable);
+
+ // Constructs an OatFileAssistant, providing an additional package_name used
+ // solely for the purpose of locating profile files.
+ //
+ // TODO: Why is the name of the profile file based on the package name and
+ // not the dex location? If there is no technical reason the dex_location
+ // can't be used, we should prefer that instead.
+ OatFileAssistant(const char* dex_location, const InstructionSet isa,
+ bool load_executable, const char* package_name);
+
+ // Constructs an OatFileAssistant with user specified oat location and a
+ // package name.
+ OatFileAssistant(const char* dex_location, const char* oat_location,
+ const InstructionSet isa, bool load_executable,
+ const char* package_name);
+
+ ~OatFileAssistant();
+
+ // Returns true if the dex location refers to an element of the boot class
+ // path.
+ bool IsInBootClassPath();
+
+ // Obtains a lock on the target oat file.
+ // Only one OatFileAssistant object can hold the lock for a target oat file
+ // at a time. The Lock is released automatically when the OatFileAssistant
+ // object goes out of scope. The Lock() method must not be called if the
+ // lock has already been acquired.
+ //
+ // Returns true on success.
+ // Returns false on error, in which case error_msg will contain more
+ // information on the error.
+ //
+ // The 'error_msg' argument must not be null.
+ //
+ // This is intended to be used to avoid race conditions when multiple
+ // processes generate oat files, such as when a foreground Activity and
+ // a background Service both use DexClassLoaders pointing to the same dex
+ // file.
+ bool Lock(std::string* error_msg);
+
+ // Returns the overall compilation status for the given dex location.
+ Status GetStatus();
+
+ // Attempts to generate or relocate the oat file as needed to make it up to
+ // date.
+ // Returns true on success.
+ //
+ // If there is a failure, the value of error_msg will be set to a string
+ // describing why there was failure. error_msg must not be nullptr.
+ bool MakeUpToDate(std::string* error_msg);
+
+ // Returns an oat file that can be used for loading dex files.
+ // Returns nullptr if no suitable oat file was found.
+ //
+ // After this call, no other methods of the OatFileAssistant should be
+ // called, because access to the loaded oat file has been taken away from
+ // the OatFileAssistant object.
+ std::unique_ptr<OatFile> GetBestOatFile();
+
+ // Loads the dex files in the given oat file for the given dex location.
+ // The oat file should be up to date for the given dex location.
+ // This loads multiple dex files in the case of multidex.
+ // Returns an empty vector if no dex files for that location could be loaded
+ // from the oat file.
+ //
+ // The caller is responsible for freeing the dex_files returned, if any. The
+ // dex_files will only remain valid as long as the oat_file is valid.
+ static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
+ const OatFile& oat_file, const char* dex_location);
+
+ // If the dex file has been pre-compiled on the host, the compiled oat file
+ // will have the extension .odex, and is referred to as the odex file.
+ // It is called odex for legacy reasons; the file is really an oat file. The
+ // odex file will typically have a patch delta of 0 and need to be relocated
+ // before use for the purposes of ASLR.
+ // These methods return the location and status of the odex file for the dex
+ // location.
+ // Notes:
+ // * OdexFileName may return null if the odex file name could not be
+ // determined.
+ const std::string* OdexFileName();
+ bool OdexFileExists();
+ Status OdexFileStatus();
+ bool OdexFileIsOutOfDate();
+ bool OdexFileNeedsRelocation();
+ bool OdexFileIsUpToDate();
+
+ // When the dex files is compiled on the target device, the oat file is the
+ // result. The oat file will have been relocated to some
+ // (possibly-out-of-date) offset for ASLR.
+ // These methods return the location and status of the target oat file for
+ // the dex location.
+ //
+ // Notes:
+ // * To get the overall status of the compiled code for this dex_location,
+ // use the GetStatus() method, not the OatFileStatus() method.
+ // * OatFileName may return null if the oat file name could not be
+ // determined.
+ const std::string* OatFileName();
+ bool OatFileExists();
+ Status OatFileStatus();
+ bool OatFileIsOutOfDate();
+ bool OatFileNeedsRelocation();
+ bool OatFileIsUpToDate();
+
+ // These methods return the status for a given opened oat file with respect
+ // to the dex location.
+ Status GivenOatFileStatus(const OatFile& file);
+ bool GivenOatFileIsOutOfDate(const OatFile& file);
+ bool GivenOatFileNeedsRelocation(const OatFile& file);
+ bool GivenOatFileIsUpToDate(const OatFile& file);
+
+ // Returns true if there is an accessible profile associated with the dex
+ // location.
+ // This returns false if profiling is disabled.
+ bool ProfileExists();
+
+ // The old profile is a file containing a previous snapshot of profiling
+ // information associated with the dex file code. This is used to track how
+ // the profiling information has changed over time.
+ //
+ // Returns true if there is an accessible old profile associated with the
+ // dex location.
+ // This returns false if profiling is disabled.
+ bool OldProfileExists();
+
+ // Returns true if there has been a significant change between the old
+ // profile and the current profile.
+ // This returns false if profiling is disabled.
+ bool IsProfileChangeSignificant();
+
+ // Copy the current profile to the old profile location.
+ void CopyProfileFile();
+
+ // Generates the oat file by relocation from the odex file.
+ // This does not check the current status before attempting to relocate the
+ // oat file.
+ // Returns true on success.
+ // This will fail if dex2oat is not enabled in the current runtime.
+ //
+ // If there is a failure, the value of error_msg will be set to a string
+ // describing why there was failure. error_msg must not be nullptr.
+ bool RelocateOatFile(std::string* error_msg);
+
+ // Generate the oat file from the dex file.
+ // This does not check the current status before attempting to generate the
+ // oat file.
+ // Returns true on success.
+ // This will fail if dex2oat is not enabled in the current runtime.
+ //
+ // If there is a failure, the value of error_msg will be set to a string
+ // describing why there was failure. error_msg must not be nullptr.
+ bool GenerateOatFile(std::string* error_msg);
+
+ // Executes dex2oat using the current runtime configuration overridden with
+ // the given arguments. This does not check to see if dex2oat is enabled in
+ // the runtime configuration.
+ // Returns true on success.
+ //
+ // If there is a failure, the value of error_msg will be set to a string
+ // describing why there was failure. error_msg must not be nullptr.
+ //
+ // TODO: The OatFileAssistant probably isn't the right place to have this
+ // function.
+ static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
+
+ // Constructs the odex file name for the given dex location.
+ // Returns true on success, in which case odex_filename is set to the odex
+ // file name.
+ // Returns false on error, in which case error_msg describes the error.
+ // Neither odex_filename nor error_msg may be null.
+ static bool DexFilenameToOdexFilename(const std::string& location,
+ InstructionSet isa, std::string* odex_filename, std::string* error_msg);
+
+ private:
+ struct ImageInfo {
+ uint32_t oat_checksum = 0;
+ uintptr_t oat_data_begin = 0;
+ int32_t patch_delta = 0;
+ std::string location;
+ };
+
+ // Returns the path to the dalvik cache directory.
+ // Does not check existence of the cache or try to create it.
+ // Includes the trailing slash.
+ // Returns an empty string if we can't get the dalvik cache directory path.
+ std::string DalvikCacheDirectory();
+
+ // Constructs the filename for the profile file.
+ // Returns an empty string if we do not have the necessary information to
+ // construct the filename.
+ std::string ProfileFileName();
+
+ // Constructs the filename for the old profile file.
+ // Returns an empty string if we do not have the necessary information to
+ // construct the filename.
+ std::string OldProfileFileName();
+
+ // Returns the current image location.
+ // Returns an empty string if the image location could not be retrieved.
+ //
+ // TODO: This method should belong with an image file manager, not
+ // the oat file assistant.
+ static std::string ImageLocation();
+
+ // Gets the dex checksum required for an up-to-date oat file.
+ // Returns dex_checksum if a required checksum was located. Returns
+ // nullptr if the required checksum was not found.
+ // The caller shouldn't clean up or free the returned pointer.
+ const uint32_t* GetRequiredDexChecksum();
+
+ // Returns the loaded odex file.
+ // Loads the file if needed. Returns nullptr if the file failed to load.
+ // The caller shouldn't clean up or free the returned pointer.
+ const OatFile* GetOdexFile();
+
+ // Clear any cached information about the odex file that depends on the
+ // contents of the file.
+ void ClearOdexFileCache();
+
+ // Returns the loaded oat file.
+ // Loads the file if needed. Returns nullptr if the file failed to load.
+ // The caller shouldn't clean up or free the returned pointer.
+ const OatFile* GetOatFile();
+
+ // Clear any cached information about the oat file that depends on the
+ // contents of the file.
+ void ClearOatFileCache();
+
+ // Returns the loaded image info.
+ // Loads the image info if needed. Returns nullptr if the image info failed
+ // to load.
+ // The caller shouldn't clean up or free the returned pointer.
+ const ImageInfo* GetImageInfo();
+
+ // Returns the loaded profile.
+ // Loads the profile if needed. Returns nullptr if the profile failed
+ // to load.
+ // The caller shouldn't clean up or free the returned pointer.
+ ProfileFile* GetProfile();
+
+ // Returns the loaded old profile.
+ // Loads the old profile if needed. Returns nullptr if the old profile
+ // failed to load.
+ // The caller shouldn't clean up or free the returned pointer.
+ ProfileFile* GetOldProfile();
+
+ // To implement Lock(), we lock a dummy file where the oat file would go
+ // (adding ".flock" to the target file name) and retain the lock for the
+ // remaining lifetime of the OatFileAssistant object.
+ std::unique_ptr<File> lock_file_;
+ ScopedFlock flock_;
+
+ // In a properly constructed OatFileAssistant object, dex_location_ should
+ // never be nullptr.
+ const char* dex_location_ = nullptr;
+
+ // In a properly constructed OatFileAssistant object, isa_ should be either
+ // the 32 or 64 bit variant for the current device.
+ const InstructionSet isa_ = kNone;
+
+ // The package name, used solely to find the profile file.
+ // This may be nullptr in a properly constructed object. In this case,
+ // profile_load_attempted_ and old_profile_load_attempted_ will be true, and
+ // profile_load_succeeded_ and old_profile_load_succeeded_ will be false.
+ const char* package_name_ = nullptr;
+
+ // Whether we will attempt to load oat files executable.
+ bool load_executable_ = false;
+
+ // Cached value of the required dex checksum.
+ // This should be accessed only by the GetRequiredDexChecksum() method.
+ uint32_t cached_required_dex_checksum;
+ bool required_dex_checksum_attempted = false;
+ bool required_dex_checksum_found;
+
+ // Cached value of the odex file name.
+ // This should be accessed only by the OdexFileName() method.
+ bool cached_odex_file_name_attempted_ = false;
+ bool cached_odex_file_name_found_;
+ std::string cached_odex_file_name_;
+
+ // Cached value of the loaded odex file.
+ // Use the GetOdexFile method rather than accessing this directly, unless you
+ // know the odex file isn't out of date.
+ bool odex_file_load_attempted_ = false;
+ std::unique_ptr<OatFile> cached_odex_file_;
+
+ // Cached results for OdexFileIsOutOfDate
+ bool odex_file_is_out_of_date_attempted_ = false;
+ bool cached_odex_file_is_out_of_date_;
+
+ // Cached results for OdexFileIsUpToDate
+ bool odex_file_is_up_to_date_attempted_ = false;
+ bool cached_odex_file_is_up_to_date_;
+
+ // Cached value of the oat file name.
+ // This should be accessed only by the OatFileName() method.
+ bool cached_oat_file_name_attempted_ = false;
+ bool cached_oat_file_name_found_;
+ std::string cached_oat_file_name_;
+
+ // Cached value of the loaded odex file.
+ // Use the GetOatFile method rather than accessing this directly, unless you
+ // know the odex file isn't out of date.
+ bool oat_file_load_attempted_ = false;
+ std::unique_ptr<OatFile> cached_oat_file_;
+
+ // Cached results for OatFileIsOutOfDate
+ bool oat_file_is_out_of_date_attempted_ = false;
+ bool cached_oat_file_is_out_of_date_;
+
+ // Cached results for OatFileIsUpToDate
+ bool oat_file_is_up_to_date_attempted_ = false;
+ bool cached_oat_file_is_up_to_date_;
+
+ // Cached value of the image info.
+ // Use the GetImageInfo method rather than accessing these directly.
+ // TODO: The image info should probably be moved out of the oat file
+ // assistant to an image file manager.
+ bool image_info_load_attempted_ = false;
+ bool image_info_load_succeeded_ = false;
+ ImageInfo cached_image_info_;
+
+ // Cached value of the profile file.
+ // Use the GetProfile method rather than accessing these directly.
+ bool profile_load_attempted_ = false;
+ bool profile_load_succeeded_ = false;
+ ProfileFile cached_profile_;
+
+ // Cached value of the profile file.
+ // Use the GetOldProfile method rather than accessing these directly.
+ bool old_profile_load_attempted_ = false;
+ bool old_profile_load_succeeded_ = false;
+ ProfileFile cached_old_profile_;
+
+ // For debugging only.
+ // If this flag is set, the oat or odex file has been released to the user
+ // of the OatFileAssistant object and the OatFileAssistant object is in a
+ // bad state and should no longer be used.
+ bool oat_file_released_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(OatFileAssistant);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_FILE_ASSISTANT_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
new file mode 100644
index 0000000000..d683cdc31c
--- /dev/null
+++ b/runtime/oat_file_assistant_test.cc
@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "oat_file_assistant.h"
+
+#include <algorithm>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <sys/param.h>
+
+#include <backtrace/BacktraceMap.h>
+#include <gtest/gtest.h>
+
+#include "class_linker.h"
+#include "common_runtime_test.h"
+#include "mem_map.h"
+#include "os.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+
+class OatFileAssistantTest : public CommonRuntimeTest {
+ public:
+ virtual void SetUp() {
+ ReserveImageSpace();
+ CommonRuntimeTest::SetUp();
+
+ // Create a scratch directory to work from.
+ scratch_dir_ = android_data_ + "/OatFileAssistantTest";
+ ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
+
+ // Create a subdirectory in scratch for the current isa.
+ // This is the location that will be used for odex files in the tests.
+ isa_dir_ = scratch_dir_ + "/" + GetInstructionSetString(kRuntimeISA);
+ ASSERT_EQ(0, mkdir(isa_dir_.c_str(), 0700));
+
+ // Verify the environment is as we expect
+ uint32_t checksum;
+ std::string error_msg;
+ ASSERT_TRUE(OS::FileExists(GetImageFile().c_str()))
+ << "Expected pre-compiled boot image to be at: " << GetImageFile();
+ ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
+ << "Expected dex file to be at: " << GetDexSrc1();
+ ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
+ << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
+ ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+ << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
+ ASSERT_TRUE(OS::FileExists(GetMultiDexSrc1().c_str()))
+ << "Expected multidex file to be at: " << GetMultiDexSrc1();
+ ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
+ << "Expected dex file to be at: " << GetDexSrc2();
+ }
+
+ virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
+ // options->push_back(std::make_pair("-verbose:oat", nullptr));
+
+ // Set up the image location.
+ options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
+ nullptr));
+ // Make sure compilercallbacks are not set so that relocation will be
+ // enabled.
+ for (std::pair<std::string, const void*>& pair : *options) {
+ if (pair.first == "compilercallbacks") {
+ pair.second = nullptr;
+ }
+ }
+ }
+
+ virtual void PreRuntimeCreate() {
+ UnreserveImageSpace();
+ }
+
+ virtual void PostRuntimeCreate() {
+ ReserveImageSpace();
+ }
+
+ virtual void TearDown() {
+ ClearDirectory(isa_dir_.c_str());
+ ASSERT_EQ(0, rmdir(isa_dir_.c_str()));
+
+ ClearDirectory(scratch_dir_.c_str());
+ ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
+
+ CommonRuntimeTest::TearDown();
+ }
+
+ void Copy(std::string src, std::string dst) {
+ std::ifstream src_stream(src, std::ios::binary);
+ std::ofstream dst_stream(dst, std::ios::binary);
+
+ dst_stream << src_stream.rdbuf();
+ }
+
+ // Returns the directory where the pre-compiled core.art can be found.
+ // TODO: We should factor out this into common tests somewhere rather than
+ // re-hardcoding it here (This was copied originally from the elf writer
+ // test).
+ std::string GetImageDirectory() {
+ if (IsHost()) {
+ const char* host_dir = getenv("ANDROID_HOST_OUT");
+ CHECK(host_dir != NULL);
+ return std::string(host_dir) + "/framework";
+ } else {
+ return std::string("/data/art-test");
+ }
+ }
+
+ std::string GetImageLocation() {
+ return GetImageDirectory() + "/core.art";
+ }
+
+ std::string GetImageFile() {
+ return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
+ + "/core.art";
+ }
+
+ std::string GetDexSrc1() {
+ return GetTestDexFileName("Main");
+ }
+
+ // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
+ // file stripped.
+ std::string GetStrippedDexSrc1() {
+ return GetTestDexFileName("MainStripped");
+ }
+
+ std::string GetMultiDexSrc1() {
+ return GetTestDexFileName("MultiDex");
+ }
+
+ std::string GetDexSrc2() {
+ return GetTestDexFileName("Nested");
+ }
+
+ // Scratch directory, for dex and odex files (oat files will go in the
+ // dalvik cache).
+ std::string GetScratchDir() {
+ return scratch_dir_;
+ }
+
+ // ISA directory is the subdirectory in the scratch directory where odex
+ // files should be located.
+ std::string GetISADir() {
+ return isa_dir_;
+ }
+
+ // Generate an odex file for the purposes of test.
+ // If pic is true, generates a PIC odex.
+ void GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ bool pic = false) {
+ // For this operation, we temporarily redirect the dalvik cache so dex2oat
+ // doesn't find the relocated image file.
+ std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
+ setenv("ANDROID_DATA", android_data_tmp.c_str(), 1);
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ if (pic) {
+ args.push_back("--compile-pic");
+ } else {
+ args.push_back("--include-patch-information");
+ }
+ args.push_back("--runtime-arg");
+ args.push_back("-Xnorelocate");
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+ setenv("ANDROID_DATA", android_data_.c_str(), 1);
+ }
+
+ void GeneratePicOdexForTest(const std::string& dex_location,
+ const std::string& odex_location) {
+ GenerateOdexForTest(dex_location, odex_location, true);
+ }
+
+ private:
+ // Reserve memory around where the image will be loaded so other memory
+ // won't conflict when it comes time to load the image.
+ // This can be called with an already loaded image to reserve the space
+ // around it.
+ void ReserveImageSpace() {
+ MemMap::Init();
+
+ // Ensure a chunk of memory is reserved for the image space.
+ uintptr_t reservation_start = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MIN_DELTA;
+ uintptr_t reservation_end = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MAX_DELTA
+ + 100 * 1024 * 1024;
+
+ std::string error_msg;
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
+ ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
+ for (BacktraceMap::const_iterator it = map->begin();
+ reservation_start < reservation_end && it != map->end(); ++it) {
+ if (it->end <= reservation_start) {
+ continue;
+ }
+
+ if (it->start < reservation_start) {
+ reservation_start = std::min(reservation_end, it->end);
+ }
+
+ image_reservation_.push_back(std::unique_ptr<MemMap>(
+ MemMap::MapAnonymous("image reservation",
+ reinterpret_cast<uint8_t*>(reservation_start),
+ std::min(it->start, reservation_end) - reservation_start,
+ PROT_NONE, false, false, &error_msg)));
+ ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
+ LOG(INFO) << "Reserved space for image " <<
+ reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
+ reinterpret_cast<void*>(image_reservation_.back()->End());
+ reservation_start = it->end;
+ }
+ }
+
+
+ // Unreserve any memory reserved by ReserveImageSpace. This should be called
+ // before the image is loaded.
+ void UnreserveImageSpace() {
+ image_reservation_.clear();
+ }
+
+ std::string scratch_dir_;
+ std::string isa_dir_;
+ std::vector<std::unique_ptr<MemMap>> image_reservation_;
+};
+
+class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest {
+ public:
+ virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
+ OatFileAssistantTest::SetUpRuntimeOptions(options);
+ options->push_back(std::make_pair("-Xnodex2oat", nullptr));
+ }
+};
+
+// Generate an oat file for the purposes of test, as opposed to testing
+// generation of oat files.
+static void GenerateOatForTest(const char* dex_location) {
+ OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false);
+
+ std::string error_msg;
+ ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg;
+}
+
+// Case: We have a DEX file, but no OAT file for it.
+// Expect: The oat file status is kOutOfDate.
+TEST_F(OatFileAssistantTest, DexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.OatFileStatus());
+}
+
+// Case: We have no DEX file and no OAT file.
+// Expect: Status is out of date. Loading should fail, but not crash.
+TEST_F(OatFileAssistantTest, NoDexNoOat) {
+ std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_EQ(nullptr, oat_file.get());
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, OatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.OatFileStatus());
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ // Verify we can load both dex files.
+ OatFileAssistant executable_oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ std::unique_ptr<OatFile> oat_file = executable_oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = executable_oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(2u, dex_files.size());
+}
+
+// Case: We have a DEX file and out of date OAT file.
+// Expect: The oat file status is kOutOfDate.
+TEST_F(OatFileAssistantTest, OatOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
+
+ // We create a dex, generate an oat for it, then overwrite the dex with a
+ // different dex to make the oat out of date.
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str());
+ Copy(GetDexSrc2(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: We have a DEX file and an ODEX file, but no OAT file.
+// Expect: The oat file status is kNeedsRelocation.
+TEST_F(OatFileAssistantTest, DexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
+ std::string odex_location = GetISADir() + "/DexOdexNoOat.odex";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location);
+
+ // Verify the status.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: We have a stripped DEX file and an ODEX file, but no OAT file.
+// Expect: The oat file status is kNeedsRelocation.
+TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
+ std::string odex_location = GetISADir() + "/StrippedDexOdexNoOat.odex";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location);
+
+ // Strip the dex file
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ // Verify the status.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+ // Make the oat file up to date.
+ std::string error_msg;
+ ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+ EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+
+ // Verify we can load the dex files from it.
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a stripped DEX file, an ODEX file, and an out of date OAT file.
+// Expect: The oat file status is kNeedsRelocation.
+TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
+ std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
+ std::string odex_location = GetISADir() + "/StrippedDexOdexOat.odex";
+
+ // Create the oat file from a different dex file so it looks out of date.
+ Copy(GetDexSrc2(), dex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ // Create the odex file
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location);
+
+ // Strip the dex file.
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ // Verify the status.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+ // Make the oat file up to date.
+ std::string error_msg;
+ ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+ EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+
+ // Verify we can load the dex files from it.
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
+// OAT files both have patch delta of 0.
+// Expect: It shouldn't crash.
+TEST_F(OatFileAssistantTest, OdexOatOverlap) {
+ std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
+ std::string odex_location = GetISADir() + "/OdexOatOverlap.odex";
+ std::string oat_location = GetISADir() + "/OdexOatOverlap.oat";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location);
+
+ // Create the oat file by copying the odex so they are located in the same
+ // place in memory.
+ Copy(odex_location, oat_location);
+
+ // Verify things don't go bad.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ oat_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+ // Things aren't relocated, so it should fall back to interpreted.
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_FALSE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file and a PIC ODEX file, but no OAT file.
+// Expect: The oat file status is kUpToDate, because PIC needs no relocation.
+TEST_F(OatFileAssistantTest, DexPicOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar";
+ std::string odex_location = GetISADir() + "/DexPicOdexNoOat.odex";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location);
+
+ // Verify the status.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+// Expect: We should load an executable dex file.
+TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/LoadOatUpToDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ // Load the oat using an oat file assistant.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+// Expect: Loading non-executable should load the oat non-executable.
+TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/LoadNoExecOatUpToDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ // Load the oat using an oat file assistant.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_FALSE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file.
+// Expect: We should load an executable dex file from an alternative oat
+// location.
+TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) {
+ std::string dex_location = GetScratchDir() + "/LoadDexNoAlternateOat.jar";
+ std::string oat_location = GetScratchDir() + "/LoadDexNoAlternateOat.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(
+ dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ std::string error_msg;
+ ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+
+ EXPECT_TRUE(OS::FileExists(oat_location.c_str()));
+
+ // Verify it didn't create an oat in the default location.
+ OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_FALSE(ofm.OatFileExists());
+}
+
+// Case: Non-existent Dex location.
+// Expect: The dex code is out of date, and trying to update it fails.
+TEST_F(OatFileAssistantTest, NonExsistentDexLocation) {
+ std::string dex_location = GetScratchDir() + "/BadDexLocation.jar";
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+ std::string error_msg;
+ EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
+ EXPECT_FALSE(error_msg.empty());
+}
+
+// Turn an absolute path into a path relative to the current working
+// directory.
+static std::string MakePathRelative(std::string target) {
+ char buf[MAXPATHLEN];
+ std::string cwd = getcwd(buf, MAXPATHLEN);
+
+ // Split the target and cwd paths into components.
+ std::vector<std::string> target_path;
+ std::vector<std::string> cwd_path;
+ Split(target, '/', &target_path);
+ Split(cwd, '/', &cwd_path);
+
+ // Reverse the path components, so we can use pop_back().
+ std::reverse(target_path.begin(), target_path.end());
+ std::reverse(cwd_path.begin(), cwd_path.end());
+
+ // Drop the common prefix of the paths. Because we reversed the path
+ // components, this becomes the common suffix of target_path and cwd_path.
+ while (!target_path.empty() && !cwd_path.empty()
+ && target_path.back() == cwd_path.back()) {
+ target_path.pop_back();
+ cwd_path.pop_back();
+ }
+
+ // For each element of the remaining cwd_path, add '..' to the beginning
+ // of the target path. Because we reversed the path components, we add to
+ // the end of target_path.
+ for (unsigned int i = 0; i < cwd_path.size(); i++) {
+ target_path.push_back("..");
+ }
+
+ // Reverse again to get the right path order, and join to get the result.
+ std::reverse(target_path.begin(), target_path.end());
+ return Join(target_path, '/');
+}
+
+// Case: Non-absolute path to Dex location.
+// Expect: Not sure, but it shouldn't crash.
+TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) {
+ std::string abs_dex_location = GetScratchDir() + "/NonAbsoluteDexLocation.jar";
+ Copy(GetDexSrc1(), abs_dex_location);
+
+ std::string dex_location = MakePathRelative(abs_dex_location);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: Very short, non-existent Dex location.
+// Expect: Dex code is out of date, and trying to update it fails.
+TEST_F(OatFileAssistantTest, ShortDexLocation) {
+ std::string dex_location = "/xx";
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+ std::string error_msg;
+ EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
+ EXPECT_FALSE(error_msg.empty());
+}
+
+// Case: Non-standard extension for dex file.
+// Expect: The oat file status is kOutOfDate.
+TEST_F(OatFileAssistantTest, LongDexExtension) {
+ std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
+ Copy(GetDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// A task to generate a dex location. Used by the RaceToGenerate test.
+class RaceGenerateTask : public Task {
+ public:
+ explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
+ : dex_location_(dex_location), oat_location_(oat_location),
+ loaded_oat_file_(nullptr)
+ {}
+
+ void Run(Thread* self) {
+ UNUSED(self);
+
+ // Load the dex files, and save a pointer to the loaded oat file, so that
+ // we can verify only one oat file was loaded for the dex location.
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::vector<std::string> error_msgs;
+ dex_files = linker->OpenDexFilesFromOat(dex_location_.c_str(), oat_location_.c_str(), &error_msgs);
+ CHECK(!dex_files.empty()) << Join(error_msgs, '\n');
+ loaded_oat_file_ = dex_files[0]->GetOatFile();
+ }
+
+ const OatFile* GetLoadedOatFile() const {
+ return loaded_oat_file_;
+ }
+
+ private:
+ std::string dex_location_;
+ std::string oat_location_;
+ const OatFile* loaded_oat_file_;
+};
+
+// Test the case where multiple processes race to generate an oat file.
+// This simulates multiple processes using multiple threads.
+//
+// We want only one Oat file to be loaded when there is a race to load, to
+// avoid using up the virtual memory address space.
+TEST_F(OatFileAssistantTest, RaceToGenerate) {
+ std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
+ std::string oat_location = GetISADir() + "/RaceToGenerate.oat";
+
+ // We use the lib core dex file, because it's large, and hopefully should
+ // take a while to generate.
+ Copy(GetLibCoreDexFileName(), dex_location);
+
+ const int kNumThreads = 32;
+ Thread* self = Thread::Current();
+ ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads);
+ std::vector<std::unique_ptr<RaceGenerateTask>> tasks;
+ for (int i = 0; i < kNumThreads; i++) {
+ std::unique_ptr<RaceGenerateTask> task(new RaceGenerateTask(dex_location, oat_location));
+ thread_pool.AddTask(self, task.get());
+ tasks.push_back(std::move(task));
+ }
+ thread_pool.StartWorkers(self);
+ thread_pool.Wait(self, true, false);
+
+ // Verify every task got the same pointer.
+ const OatFile* expected = tasks[0]->GetLoadedOatFile();
+ for (auto& task : tasks) {
+ EXPECT_EQ(expected, task->GetLoadedOatFile());
+ }
+}
+
+// Case: We have a DEX file and an ODEX file, no OAT file, and dex2oat is
+// disabled.
+// Expect: We should load the odex file non-executable.
+TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
+ std::string odex_location = GetISADir() + "/LoadDexOdexNoOat.odex";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location);
+
+ // Load the oat using an executable oat file assistant.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_FALSE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a MultiDEX file and an ODEX file, no OAT file, and dex2oat is
+// disabled.
+// Expect: We should load the odex file non-executable.
+TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
+ std::string odex_location = GetISADir() + "/LoadMultiDexOdexNoOat.odex";
+
+ // Create the dex and odex files
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location);
+
+ // Load the oat using an executable oat file assistant.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_FALSE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(2u, dex_files.size());
+}
+
+TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) {
+ std::string error_msg;
+ std::string odex_file;
+
+ EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename(
+ "/foo/bar/baz.jar", kArm, &odex_file, &error_msg)) << error_msg;
+ EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file);
+
+ EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename(
+ "/foo/bar/baz.funnyext", kArm, &odex_file, &error_msg)) << error_msg;
+ EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file);
+
+ EXPECT_FALSE(OatFileAssistant::DexFilenameToOdexFilename(
+ "nopath.jar", kArm, &odex_file, &error_msg));
+ EXPECT_FALSE(OatFileAssistant::DexFilenameToOdexFilename(
+ "/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
+}
+
+
+// TODO: More Tests:
+// * Test class linker falls back to unquickened dex for DexNoOat
+// * Test class linker falls back to unquickened dex for MultiDexNoOat
+// * Test multidex files:
+// - Multidex with only classes2.dex out of date should have status
+// kOutOfDate
+// * Test using secondary isa
+// * Test with profiling info?
+// * Test for status of oat while oat is being generated (how?)
+// * Test case where 32 and 64 bit boot class paths differ,
+// and we ask IsInBootClassPath for a class in exactly one of the 32 or
+// 64 bit boot class paths.
+// * Test unexpected scenarios (?):
+// - Dex is stripped, don't have odex.
+// - Oat file corrupted after status check, before reload unexecutable
+// because it's unrelocated and no dex2oat
+
+} // namespace art
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 851ecebb05..8a23ff7233 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1508,23 +1508,6 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is
return filename;
}
-std::string DexFilenameToOdexFilename(const std::string& location, const InstructionSet isa) {
- // location = /foo/bar/baz.jar
- // odex_location = /foo/bar/<isa>/baz.odex
- std::string odex_location(location);
- InsertIsaDirectory(isa, &odex_location);
- size_t dot_index = odex_location.rfind('.');
-
- // The location must have an extension, otherwise it's not clear what we
- // should return.
- CHECK_NE(dot_index, std::string::npos) << odex_location;
- CHECK_EQ(std::string::npos, odex_location.find('/', dot_index)) << odex_location;
-
- odex_location.resize(dot_index + 1);
- odex_location += "odex";
- return odex_location;
-}
-
bool IsZipMagic(uint32_t magic) {
return (('P' == ((magic >> 0) & 0xff)) &&
('K' == ((magic >> 8) & 0xff)));
diff --git a/runtime/utils.h b/runtime/utils.h
index 9d04d35e26..d294f4b1a1 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -516,12 +516,6 @@ std::string GetDalvikCacheFilenameOrDie(const char* file_location,
// Returns the system location for an image
std::string GetSystemImageFilename(const char* location, InstructionSet isa);
-// Returns an .odex file name adjacent to the dex location.
-// For example, for "/foo/bar/baz.jar", return "/foo/bar/<isa>/baz.odex".
-// The dex location must include a directory component and have an extension.
-// Note: does not support multidex location strings.
-std::string DexFilenameToOdexFilename(const std::string& location, InstructionSet isa);
-
// Check whether the given magic matches a known file type.
bool IsZipMagic(uint32_t magic);
bool IsDexMagic(uint32_t magic);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 5465762fd9..6b36c192e8 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -371,13 +371,6 @@ TEST_F(UtilsTest, GetSystemImageFilename) {
GetSystemImageFilename("/system/framework/boot.art", kArm).c_str());
}
-TEST_F(UtilsTest, DexFilenameToOdexFilename) {
- EXPECT_STREQ("/foo/bar/arm/baz.odex",
- DexFilenameToOdexFilename("/foo/bar/baz.jar", kArm).c_str());
- EXPECT_STREQ("/foo/bar/arm/baz.odex",
- DexFilenameToOdexFilename("/foo/bar/baz.funnyext", kArm).c_str());
-}
-
TEST_F(UtilsTest, ExecSuccess) {
std::vector<std::string> command;
if (kIsTargetBuild) {
diff --git a/test/MultiDex/Main.java b/test/MultiDex/Main.java
new file mode 100644
index 0000000000..659dba9af6
--- /dev/null
+++ b/test/MultiDex/Main.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+ public static void main(String args[]) {
+ Second second = new Second();
+ System.out.println(second.getSecond());
+ }
+}
diff --git a/test/MultiDex/Second.java b/test/MultiDex/Second.java
new file mode 100644
index 0000000000..540aedbb1a
--- /dev/null
+++ b/test/MultiDex/Second.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Second {
+ public String getSecond() {
+ return "I Second That.";
+ }
+}
diff --git a/test/MultiDex/main.list b/test/MultiDex/main.list
new file mode 100644
index 0000000000..44ba78ead5
--- /dev/null
+++ b/test/MultiDex/main.list
@@ -0,0 +1 @@
+Main.class