Use compiler filter to determine oat file status.
Record the compiler filter in the oat header. Use that to determine
when the oat file is up-to-date with respect to a target compiler
filter level.
New xxx-profile filter levels are added to specify if a profile should
be used instead of testing for the presence of a profile file.
This change should allow for different compiler-filters to be set for
different package manager use cases.
Bug: 27689078
Change-Id: Id6706d0ed91b45f307142692ea4316aa9713b023
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index cbc0ec6..67c8e65 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -44,19 +44,18 @@
namespace art {
OatFileAssistant::OatFileAssistant(const char* dex_location,
- const int target_compilation_type_mask,
const InstructionSet isa,
+ bool profile_changed,
bool load_executable)
- : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable)
+ : OatFileAssistant(dex_location, nullptr, isa, profile_changed, load_executable)
{ }
OatFileAssistant::OatFileAssistant(const char* dex_location,
const char* oat_location,
- const int target_compilation_type_mask,
const InstructionSet isa,
+ bool profile_changed,
bool load_executable)
- : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa),
- load_executable_(load_executable) {
+ : isa_(isa), profile_changed_(profile_changed), load_executable_(load_executable) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
dex_location_.assign(dex_location);
@@ -116,42 +115,78 @@
return true;
}
-// Returns the compilation mode of the given oat file.
-static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) {
- if (oat_file.IsExtractOnly()) {
- return OatFileAssistant::kExtractOnly;
- }
- if (oat_file.IsProfileGuideCompiled()) {
- return OatFileAssistant::kProfileGuideCompilation;
- }
- // Assume that if the oat files is not extract-only or profile-guide compiled
- // then it must be fully compiled.
- // NB: this does not necessary mean that the oat file is actually fully compiled. It
- // might have been compiled in a different way (e.g. interpret-only) which does
- // not record a type in the header.
- return OatFileAssistant::kFullCompilation;
+bool OatFileAssistant::OatFileCompilerFilterIsOkay(CompilerFilter::Filter target) {
+ const OatFile* oat_file = GetOatFile();
+ if (oat_file != nullptr) {
+ CompilerFilter::Filter current = oat_file->GetCompilerFilter();
+ return CompilerFilter::IsAsGoodAs(current, target);
+ }
+ return false;
}
-OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
- if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
- return kNoDexOptNeeded;
+bool OatFileAssistant::OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target) {
+ const OatFile* odex_file = GetOdexFile();
+ if (odex_file != nullptr) {
+ CompilerFilter::Filter current = odex_file->GetCompilerFilter();
+ return CompilerFilter::IsAsGoodAs(current, target);
+ }
+ return false;
+}
+
+OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target) {
+ bool compilation_desired = CompilerFilter::IsCompilationEnabled(target);
+
+ // See if the oat file is in good shape as is.
+ bool oat_okay = OatFileCompilerFilterIsOkay(target);
+ if (oat_okay) {
+ if (compilation_desired) {
+ if (OatFileIsUpToDate()) {
+ return kNoDexOptNeeded;
+ }
+ } else {
+ if (!OatFileIsOutOfDate()) {
+ return kNoDexOptNeeded;
+ }
+ }
}
- if (OdexFileNeedsRelocation()) {
- return kPatchOatNeeded;
+ // See if the odex file is in good shape as is.
+ bool odex_okay = OdexFileCompilerFilterIsOkay(target);
+ if (odex_okay) {
+ if (compilation_desired) {
+ if (OdexFileIsUpToDate()) {
+ return kNoDexOptNeeded;
+ }
+ } else {
+ if (!OdexFileIsOutOfDate()) {
+ return kNoDexOptNeeded;
+ }
+ }
}
- if (OatFileNeedsRelocation()) {
- return kSelfPatchOatNeeded;
+ // See if we can get an up-to-date file by running patchoat.
+ if (compilation_desired) {
+ if (odex_okay && OdexFileNeedsRelocation()) {
+ // TODO: don't return kPatchOatNeeded if the odex file contains no
+ // patch information.
+ return kPatchOatNeeded;
+ }
+
+ if (oat_okay && OatFileNeedsRelocation()) {
+ // TODO: don't return kSelfPatchOatNeeded if the oat file contains no
+ // patch information.
+ return kSelfPatchOatNeeded;
+ }
}
+ // We can only run dex2oat if there are original dex files.
return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
}
-bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
- switch (GetDexOptNeeded()) {
+bool OatFileAssistant::MakeUpToDate(CompilerFilter::Filter target, std::string* error_msg) {
+ switch (GetDexOptNeeded(target)) {
case kNoDexOptNeeded: return true;
- case kDex2OatNeeded: return GenerateOatFile(error_msg);
+ case kDex2OatNeeded: return GenerateOatFile(target, error_msg);
case kPatchOatNeeded: return RelocateOatFile(OdexFileName(), error_msg);
case kSelfPatchOatNeeded: return RelocateOatFile(OatFileName(), error_msg);
}
@@ -410,11 +445,6 @@
}
bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
- // Verify the file satisfies the desired compilation type.
- if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) {
- return true;
- }
-
// 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.
@@ -457,36 +487,38 @@
}
}
- if (file.IsExtractOnly()) {
- VLOG(oat) << "Oat file is extract-only. Image checksum test skipped.";
- if (kIsDebugBuild) {
- // Sanity check that no classes have compiled code. Does not test that
- // the DEX code has not been quickened.
- std::string error_msg;
- for (const OatFile::OatDexFile* current : file.GetOatDexFiles()) {
- std::unique_ptr<const DexFile> dex_file = current->OpenDexFile(&error_msg);
- DCHECK(dex_file != nullptr);
- for (size_t i = 0, e = dex_file->NumClassDefs(); i < e; ++i) {
- DCHECK_EQ(current->GetOatClass(i).GetType(), kOatClassNoneCompiled);
- }
- }
- }
- return false;
- }
+ CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter();
+ VLOG(oat) << "Compiler filter for " << file.GetLocation() << " is " << current_compiler_filter;
// 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 (CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) {
+ 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;
+ }
+ } else {
+ VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
}
- if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) {
- VLOG(oat) << "Oat image checksum does not match image checksum.";
- return true;
+ // Verify the profile hasn't changed recently.
+ // TODO: Move this check to OatFileCompilerFilterIsOkay? Nothing bad should
+ // happen if we use an oat file compiled with an out-of-date profile.
+ if (CompilerFilter::DependsOnProfile(current_compiler_filter)) {
+ if (profile_changed_) {
+ VLOG(oat) << "The profile has changed recently.";
+ return true;
+ }
+ } else {
+ VLOG(oat) << "Profile check skipped for compiler filter " << current_compiler_filter;
}
- // The checksums are all good; the dex file is not out of date.
+ // Everything looks good; the dex file is not out of date.
return false;
}
@@ -499,40 +531,49 @@
return false;
}
- if (file.IsPic() || file.IsExtractOnly()) {
+ CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter();
+
+ // Don't consider an oat file as up-to-date with compiled code unless
+ // compilation is enabled. This ensures we fall back to a non-executable oat
+ // file if there is no compiled code.
+ if (!CompilerFilter::IsCompilationEnabled(current_compiler_filter)) {
+ VLOG(oat) << "Compilation not enabled for " << current_compiler_filter;
+ return false;
+ }
+
+ if (!file.IsPic()) {
+ const ImageInfo* image_info = GetImageInfo();
+ if (image_info == nullptr) {
+ VLOG(oat) << "No image 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;
+ }
+ } else {
// Oat files compiled in PIC mode do not require relocation and extract-only
// oat files do not contain any compiled code. Skip the relocation test.
- VLOG(oat) << "Oat relocation test skipped.";
- return true;
- }
-
- const ImageInfo* image_info = GetImageInfo();
- if (image_info == nullptr) {
- VLOG(oat) << "No image 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;
+ VLOG(oat) << "Oat relocation test skipped for PIC oat file";
}
return true;
}
@@ -589,18 +630,9 @@
return true;
}
-bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
+bool OatFileAssistant::GenerateOatFile(CompilerFilter::Filter target, std::string* error_msg) {
CHECK(error_msg != nullptr);
- // TODO: Currently we only know how to make a fully-compiled oat file.
- // Perhaps we should support generating other kinds of oat files?
- if ((target_compilation_type_mask_ & kFullCompilation) == 0) {
- *error_msg = "Generation of oat file for dex location " + dex_location_
- + " not attempted because full compilation was not specified"
- + " as an acceptable target compilation type.";
- return false;
- }
-
Runtime* runtime = Runtime::Current();
if (!runtime->IsDex2OatEnabled()) {
*error_msg = "Generation of oat file for dex location " + dex_location_
@@ -642,6 +674,7 @@
args.push_back("--dex-file=" + dex_location_);
args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
args.push_back("--oat-location=" + oat_file_name);
+ args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(target));
if (!Dex2Oat(args, error_msg)) {
// Manually delete the file. This ensures there is no garbage left over if
@@ -751,8 +784,7 @@
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.
+ // OatFileName.
// TODO: The work done in GetDalvikCache is overkill for what we need.
// Ideally a new API for getting the DalvikCacheDirectory the way we want