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
Change-Id: Ibe92d8b55a24bbf718b0416a21b76e5df7a2de26
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index ac1a310..0af2c22 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -275,8 +275,96 @@
}
}
-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 <const bool kVerboseLogging, const bool kReasonLogging>
+static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
+ InstructionSet target_instruction_set) {
+ std::string error_msg;
+ std::unique_ptr<const OatFile> 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 @@
ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
const char* message = (filename == nullptr) ? "<empty file name>" : 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 @@
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<const OatFile> 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 @@
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 @@
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 @@
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_isDexOptNeeded new in topK: " << *it;
+ LOG(INFO) << "DexFile_isDexOptNeededInternal new in topK: " << *it;
}
}
}
@@ -413,67 +468,84 @@
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;
+ LOG(INFO) << "DexFile_isDexOptNeededInternal previous profile doesn't exist: " << prev_profile_file;
}
- if (!defer) {
- CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
- }
+ 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<kVerboseLogging, kReasonLogging>(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<kVerboseLogging, kReasonLogging>(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_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 @@
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"),
};