From: Alex Light Date: Fri, 18 Jul 2014 21:57:04 +0000 (-0700) Subject: Make system use patchoat to relocate during runtime. X-Git-Tag: android-x86-6.0-r1~2716 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=345c4b19758703793ed31024cfb79940e2c63b75;p=android-x86%2Fart.git Make system use patchoat to relocate during runtime. Change dalvik_system_DexFile.cc so that isDexOptNeededInternal will be able to indicate that a patchoat is required. Change default of relocate option to be on. Bug: 15358152 (cherry picked from commit 6e183f2e973a20f2eaca135c240908e1bf98c5d0) Change-Id: Ib21f4f41b6cbf18094e3ca1a30d65a3b197b71b0 --- diff --git a/Android.mk b/Android.mk index e536a714d..b006bfe12 100644 --- a/Android.mk +++ b/Android.mk @@ -316,7 +316,8 @@ $$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OA --dex-location=/$(1) --oat-file=$$@ \ --instruction-set=$(DEX2OAT_TARGET_ARCH) \ --instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \ - --android-root=$(PRODUCT_OUT)/system --include-patch-information + --android-root=$(PRODUCT_OUT)/system --include-patch-information \ + --runtime-arg -Xnorelocate endif diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f85bc65e9..ed126adfe 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -933,13 +933,13 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i } *out_is_finalizable = resolved_class->IsFinalizable(); const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot(); + const bool support_boot_image_fixup = GetSupportBootImageFixup(); if (compiling_boot) { // boot -> boot class pointers. // True if the class is in the image at boot compiling time. const bool is_image_class = IsImage() && IsImageClass( dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_)); // True if pc relative load works. - const bool support_boot_image_fixup = GetSupportBootImageFixup(); if (is_image_class && support_boot_image_fixup) { *is_type_initialized = resolved_class->IsInitialized(); *use_direct_type_ptr = false; @@ -952,7 +952,7 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i // True if the class is in the image at app compiling time. const bool class_in_image = Runtime::Current()->GetHeap()->FindSpaceFromObject(resolved_class, false)->IsImageSpace(); - if (class_in_image) { + if (class_in_image && support_boot_image_fixup) { // boot -> app class pointers. *is_type_initialized = resolved_class->IsInitialized(); // TODO This is somewhat hacky. We should refactor all of this invoke codepath. diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc index 6112fbb5c..137110f5a 100644 --- a/compiler/elf_patcher.cc +++ b/compiler/elf_patcher.cc @@ -120,6 +120,7 @@ void ElfPatcher::AddPatch(uintptr_t p) { uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) { CHECK_GE(patch_ptr, reinterpret_cast(oat_file_->Begin())); + CHECK_LE(patch_ptr, reinterpret_cast(oat_file_->End())); uintptr_t off = patch_ptr - reinterpret_cast(oat_file_->Begin()); uintptr_t ret = reinterpret_cast(oat_header_) + off; @@ -144,20 +145,20 @@ void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx()); uint32_t expected = reinterpret_cast(&id) & 0xFFFFFFFF; uint32_t actual = *patch_location; - CHECK(actual == expected || actual == value) << std::hex - << "actual=" << actual - << "expected=" << expected - << "value=" << value; + CHECK(actual == expected || actual == value) << "Patching call failed: " << std::hex + << " actual=" << actual + << " expected=" << expected + << " value=" << value; } if (patch->IsType()) { const CompilerDriver::TypePatchInformation* tpatch = patch->AsType(); const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx()); uint32_t expected = reinterpret_cast(&id) & 0xFFFFFFFF; uint32_t actual = *patch_location; - CHECK(actual == expected || actual == value) << std::hex - << "actual=" << actual - << "expected=" << expected - << "value=" << value; + CHECK(actual == expected || actual == value) << "Patching type failed: " << std::hex + << " actual=" << actual + << " expected=" << expected + << " value=" << value; } } *patch_location = value; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 3005e56a9..6b23345eb 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -141,6 +141,8 @@ TEST_F(ImageTest, WriteRead) { std::string image("-Ximage:"); image.append(image_location.GetFilename()); options.push_back(std::make_pair(image.c_str(), reinterpret_cast(NULL))); + // By default the compiler this creates will not include patch information. + options.push_back(std::make_pair("-Xnorelocate", nullptr)); if (!Runtime::Create(options, false)) { LOG(FATAL) << "Failed to create runtime"; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 99907e316..c1e3b257f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -615,6 +615,14 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename, 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"); } @@ -986,11 +994,25 @@ const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location return oat_file.release(); } -bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, - const char* dex_location, - uint32_t dex_location_checksum, - const InstructionSet instruction_set, - std::string* error_msg) { +bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file, + const InstructionSet instruction_set) { + Runtime* runtime = Runtime::Current(); + const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); + 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 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(); @@ -1013,9 +1035,28 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, image_patch_delta = image_header->GetPatchDelta(); } const OatHeader& oat_header = oat_file->GetOatHeader(); - bool image_check = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum) - && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin) - && (oat_header.GetImagePatchDelta() == image_patch_delta)); + bool ret = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum) + && (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); @@ -1031,27 +1072,15 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, } return false; } - bool dex_check = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum(); - if (image_check && dex_check) { - return true; - } - - if (!image_check) { - ScopedObjectAccess soa(Thread::Current()); - *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d) with (0x%x, %" PRIdPTR ")", - oat_file->GetLocation().c_str(), - oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), - oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), - image_oat_checksum, image_oat_data_begin); - } - if (!dex_check) { + if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) { *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)", oat_file->GetLocation().c_str(), oat_dex_file->GetDexFileLocationChecksum(), dex_location, dex_location_checksum); + return false; } - return false; + return true; } bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file, @@ -1074,8 +1103,8 @@ bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file, } dex_file.reset(oat_dex_file->OpenDexFile(error_msg)); } else { - bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum, - kRuntimeISA, error_msg); + bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, dex_location_checksum, + kRuntimeISA, error_msg); if (!verified) { return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 1bb1635b2..8c0904203 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -274,12 +274,18 @@ class ClassLinker { std::vector* dex_files) 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 VerifyOatFileChecksums(const OatFile* oat_file, - const char* dex_location, - uint32_t dex_location_checksum, - InstructionSet instruction_set, - std::string* error_msg); + static bool VerifyOatAndDexFileChecksums(const OatFile* oat_file, + const char* dex_location, + uint32_t dex_location_checksum, + InstructionSet instruction_set, + std::string* error_msg); // TODO: replace this with multiple methods that allocate the correct managed type. template diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index fc6d2ef6a..1d10af2e6 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -166,7 +166,8 @@ static bool ReadSpecificImageHeader(const char* filename, ImageHeader* image_hea return true; } -bool ImageSpace::RelocateImage(const char* image_location, const char* dest_filename, +// Relocate the image at image_location to dest_filename and relocate it by a random amount. +static bool RelocateImage(const char* image_location, const char* dest_filename, InstructionSet isa, std::string* error_msg) { std::string patchoat(Runtime::Current()->GetPatchoatExecutable()); diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index debca52e5..6be3b8f3d 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -124,9 +124,6 @@ class ImageSpace : public MemMapSpace { bool validate_oat_file, std::string* error_msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static bool RelocateImage(const char* image_location, const char* dest_filename, - InstructionSet isa, std::string* error_msg); - OatFile* OpenOatFile(const char* image, std::string* error_msg) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/globals.h b/runtime/globals.h index 1d9f22c35..107e0646a 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -118,6 +118,8 @@ static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceDu static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceWall; #endif +static constexpr bool kDefaultMustRelocate = true; + } // namespace art #endif // ART_RUNTIME_GLOBALS_H_ diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index ac1a310c2..0af2c22cb 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -275,8 +275,96 @@ static void CopyProfileFile(const char* oldfile, const char* newfile) { } } -static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, +// Java: dalvik.system.DexFile.UP_TO_DATE +static const jbyte kUpToDate = 0; +// Java: dalvik.system.DexFile.DEXOPT_NEEDED +static const jbyte kPatchoatNeeded = 1; +// Java: dalvik.system.DexFile.PATCHOAT_NEEDED +static const jbyte kDexoptNeeded = 2; + +template +static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename, + InstructionSet target_instruction_set) { + std::string error_msg; + std::unique_ptr oat_file(OatFile::Open(oat_filename, oat_filename, nullptr, + false, &error_msg)); + if (oat_file.get() == nullptr) { + if (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename + << "' for file location '" << filename << "': " << error_msg; + } + error_msg.clear(); + return kDexoptNeeded; + } + 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 (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename + << " needs to be relocated for " << filename; + } + return kPatchoatNeeded; + } else { + if (kVerboseLogging) { + 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 (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename + << " needs to be relocated for " << filename; + } + return kPatchoatNeeded; + } else { + if (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename + << " is out of date for " << filename; + } + return kDexoptNeeded; + } + } + } else { + if (kVerboseLogging) { + 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) { + // TODO disable this logging. const bool kVerboseLogging = false; // Spammy logging. const bool kReasonLogging = true; // Logging of reason for returning JNI_TRUE. @@ -285,7 +373,7 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, ScopedLocalRef fnfe(env, env->FindClass("java/io/FileNotFoundException")); const char* message = (filename == nullptr) ? "" : filename; env->ThrowNew(fnfe.get(), message); - return JNI_FALSE; + return kUpToDate; } // Always treat elements of the bootclasspath as up-to-date. The @@ -301,78 +389,45 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename; } - return JNI_FALSE; + return kUpToDate; } } - const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set); - - // Check if we have an odex file next to the dex file. - std::string odex_filename(DexFilenameToOdexFilename(filename, kRuntimeISA)); - std::string error_msg; - std::unique_ptr oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false, - &error_msg)); - if (oat_file.get() == nullptr) { - if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename - << "': " << error_msg; - } - error_msg.clear(); - } else { - const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, NULL, - kReasonLogging); - if (oat_dex_file != nullptr) { - uint32_t location_checksum; - // 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 ignoring precompiled stripped file: " - << filename << ": " << error_msg; - } - return JNI_FALSE; - } - if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum, - target_instruction_set, - &error_msg)) { - if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename - << " has an up-to-date checksum compared to " << filename; - } - return JNI_FALSE; - } else { - if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename - << " with an out-of-date checksum compared to " << filename - << ": " << error_msg; - } - error_msg.clear(); - } - } - } + 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)) { - const std::string profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */) + profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */) + std::string("/") + pkgname; - const std::string prev_profile_file = profile_file + std::string("@old"); + 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) { - // No profile file, need to run dex2oat - if (kReasonLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded profile file " << profile_file << " doesn't exist"; + 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; } - return JNI_TRUE; - } - - if (e2 == 0) { + } 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. @@ -384,7 +439,7 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, bool old_ok = old_profile.LoadFile(prev_profile_file); if (!new_ok || !old_ok) { if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded Ignoring invalid profiles: " + LOG(INFO) << "DexFile_isDexOptNeededInternal Ignoring invalid profiles: " << (new_ok ? "" : profile_file) << " " << (old_ok ? "" : prev_profile_file); } } else { @@ -393,7 +448,7 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, old_profile.GetTopKSamples(old_top_k, top_k_threshold); if (new_top_k.empty()) { if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file; + 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 { @@ -405,7 +460,7 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, if (kVerboseLogging) { std::set::iterator end = diff.end(); for (std::set::iterator it = diff.begin(); it != end; it++) { - LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it; + LOG(INFO) << "DexFile_isDexOptNeededInternal new in topK: " << *it; } } } @@ -413,67 +468,84 @@ static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename, if (change_percent > change_threshold) { if (kReasonLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file << + 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 << "%)"; } - if (!defer) { - CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str()); - } - return JNI_TRUE; + should_copy_profile = !defer; + // Force us to only accept system files. + force_system_only = true; } - } else { + } else if (e2_errno == ENOENT) { // Previous profile does not exist. Make a copy of the current one. if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded previous profile doesn't exist: " << prev_profile_file; - } - if (!defer) { - CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str()); + 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; } } - // Check if we have an oat file in the cache - const std::string cache_dir(GetDalvikCacheOrDie(instruction_set)); - const std::string cache_location( - GetDalvikCacheFilenameOrDie(filename, cache_dir.c_str())); - oat_file.reset(OatFile::Open(cache_location, filename, NULL, false, &error_msg)); - if (oat_file.get() == nullptr) { - if (kReasonLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " does not exist for " << filename << ": " << error_msg; + const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set); + + // 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; + GetDalvikCache(instruction_set, false, &cache_dir, &have_android_data, &dalvik_cache_exists); + 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; } - return JNI_TRUE; } - uint32_t location_checksum; - if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) { - if (kReasonLogging) { - LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename - << " (error " << error_msg << ")"; + bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate(); + + InstructionSet isa = Runtime::Current()->GetInstructionSet(); + 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) { + // We can use the dalvik-cache if we find a good file. + dalvik_cache_decision = + IsDexOptNeededForFile(cache_filename, filename, isa); + // 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; } - return JNI_TRUE; + // We couldn't find one thats easy. We should now try the system. } - if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum, - target_instruction_set, &error_msg)) { - if (kReasonLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " has out-of-date checksum compared to " << filename - << " (error " << error_msg << ")"; - } - return JNI_TRUE; + jbyte system_decision = + IsDexOptNeededForFile(odex_filename, filename, isa); + CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible)) + << "May not return PatchoatNeeded when patching is disabled."; + + 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; } - if (kVerboseLogging) { - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " is up-to-date for " << filename; + if (should_copy_profile && system_decision == kDexoptNeeded) { + CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str()); } - CHECK(error_msg.empty()) << error_msg; - return JNI_FALSE; + + return system_decision; } -static jboolean DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename, +static jbyte DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename, jstring javaPkgname, jstring javaInstructionSet, jboolean defer) { ScopedUtfChars filename(env, javaFilename); NullableScopedUtfChars pkgname(env, javaPkgname); @@ -487,8 +559,8 @@ static jboolean DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring java static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { const char* instruction_set = GetInstructionSetString(kRuntimeISA); ScopedUtfChars filename(env, javaFilename); - return IsDexOptNeededInternal(env, filename.c_str(), nullptr /* pkgname */, - instruction_set, false /* defer */); + return kUpToDate != IsDexOptNeededInternal(env, filename.c_str(), nullptr /* pkgname */, + instruction_set, false /* defer */); } @@ -497,7 +569,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"), NATIVE_METHOD(DexFile, getClassNameList, "(J)[Ljava/lang/String;"), NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), - NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Z"), + NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)B"), NATIVE_METHOD(DexFile, openDexFile, "(Ljava/lang/String;Ljava/lang/String;I)J"), }; diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 668ed9ed2..3dbe26f30 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -48,8 +48,6 @@ class ParsedOptions { std::string native_bridge_library_string_; CompilerCallbacks* compiler_callbacks_; bool is_zygote_; - // TODO Change this to true when we want it on by default. - static constexpr bool kDefaultMustRelocate = false; bool must_relocate_; std::string patchoat_executable_; bool interpreter_only_; diff --git a/runtime/utils.cc b/runtime/utils.cc index 52cdcc1d6..4d4980933 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1236,7 +1236,7 @@ bool GetDalvikCacheFilename(const char* location, const char* cache_location, return false; } std::string cache_file(&location[1]); // skip leading slash - if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) { + if (!EndsWith(location, ".dex") && !EndsWith(location, ".art") && !EndsWith(location, ".oat")) { cache_file += "/"; cache_file += DexFile::kClassesDex; } diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 7cd5980c4..d6c90e1d4 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -350,6 +350,8 @@ TEST_F(UtilsTest, GetDalvikCacheFilenameOrDie) { GetDalvikCacheFilenameOrDie("/system/framework/core.jar", "/foo").c_str()); EXPECT_STREQ("/foo/system@framework@boot.art", GetDalvikCacheFilenameOrDie("/system/framework/boot.art", "/foo").c_str()); + EXPECT_STREQ("/foo/system@framework@boot.oat", + GetDalvikCacheFilenameOrDie("/system/framework/boot.oat", "/foo").c_str()); } TEST_F(UtilsTest, GetSystemImageFilename) {