From 7bcfcb80a31f57a84d754e00bca8698829365208 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 23 Mar 2016 15:31:51 +0000 Subject: [PATCH] Revert "Revert "Use compiler filter to determine oat file status."" This reverts commit 845e5064580bd37ad5014f7aa0d078be7265464d. Add an option to change what OatFileManager considers up-to-date. In our tests we're allowed to write to the dalvik-cache, so it cannot be kSpeed. (cherry picked from commit 29d38e77c553c6cf71fc4dafe2d22b4e3f814872) Bug: 27689078 Change-Id: I6274188610f31dcd9d086fc080b2be93afae5a6b --- build/Android.gtest.mk | 1 + compiler/dex/verification_results.cc | 2 +- compiler/driver/compiler_options.cc | 22 +- compiler/driver/compiler_options.h | 42 +-- compiler/optimizing/builder.cc | 4 +- compiler/optimizing/optimizing_compiler.cc | 2 +- dex2oat/dex2oat.cc | 42 ++- runtime/Android.mk | 1 + runtime/common_runtime_test.cc | 2 +- runtime/compiler_filter.cc | 145 +++++++++ runtime/compiler_filter.h | 88 ++++++ runtime/compiler_filter_test.cc | 52 ++++ runtime/native/dalvik_system_DexFile.cc | 31 +- runtime/oat.cc | 19 +- runtime/oat.h | 10 +- runtime/oat_file.cc | 8 +- runtime/oat_file.h | 4 +- runtime/oat_file_assistant.cc | 229 ++++++++------ runtime/oat_file_assistant.h | 58 ++-- runtime/oat_file_assistant_test.cc | 477 +++++++++++++++-------------- runtime/oat_file_manager.cc | 19 +- runtime/oat_file_manager.h | 9 + runtime/parsed_options.cc | 3 + runtime/runtime.cc | 11 + runtime/runtime_options.def | 1 + test/117-nopatchoat/nopatchoat.cc | 3 +- test/run-test | 8 +- 27 files changed, 804 insertions(+), 489 deletions(-) create mode 100644 runtime/compiler_filter.cc create mode 100644 runtime/compiler_filter.h create mode 100644 runtime/compiler_filter_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 426c3cac6..f67da3ff4 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -186,6 +186,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/base/variant_map_test.cc \ runtime/base/unix_file/fd_file_test.cc \ runtime/class_linker_test.cc \ + runtime/compiler_filter_test.cc \ runtime/dex_file_test.cc \ runtime/dex_file_verifier_test.cc \ runtime/dex_instruction_test.cc \ diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index dd24220e0..7c9ce1ee6 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -109,7 +109,7 @@ bool VerificationResults::IsCandidateForCompilation(MethodReference&, return false; } // Don't compile class initializers unless kEverything. - if ((compiler_options_->GetCompilerFilter() != CompilerOptions::kEverything) && + if ((compiler_options_->GetCompilerFilter() != CompilerFilter::kEverything) && ((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { return false; } diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index f5969aa82..1bd4c3ad8 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -52,7 +52,7 @@ CompilerOptions::~CompilerOptions() { // because we don't want to include the PassManagerOptions definition from the header file. } -CompilerOptions::CompilerOptions(CompilerFilter compiler_filter, +CompilerOptions::CompilerOptions(CompilerFilter::Filter compiler_filter, size_t huge_method_threshold, size_t large_method_threshold, size_t small_method_threshold, @@ -147,25 +147,7 @@ void CompilerOptions::ParseDumpInitFailures(const StringPiece& option, bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usage) { if (option.starts_with("--compiler-filter=")) { const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data(); - if (strcmp(compiler_filter_string, "verify-none") == 0) { - compiler_filter_ = CompilerOptions::kVerifyNone; - } else if (strcmp(compiler_filter_string, "interpret-only") == 0) { - compiler_filter_ = CompilerOptions::kInterpretOnly; - } else if (strcmp(compiler_filter_string, "verify-at-runtime") == 0) { - compiler_filter_ = CompilerOptions::kVerifyAtRuntime; - } else if (strcmp(compiler_filter_string, "space") == 0) { - compiler_filter_ = CompilerOptions::kSpace; - } else if (strcmp(compiler_filter_string, "balanced") == 0) { - compiler_filter_ = CompilerOptions::kBalanced; - } else if (strcmp(compiler_filter_string, "speed") == 0) { - compiler_filter_ = CompilerOptions::kSpeed; - } else if (strcmp(compiler_filter_string, "everything") == 0) { - compiler_filter_ = CompilerOptions::kEverything; - } else if (strcmp(compiler_filter_string, "time") == 0) { - compiler_filter_ = CompilerOptions::kTime; - } else if (strcmp(compiler_filter_string, "verify-profile") == 0) { - compiler_filter_ = CompilerOptions::kVerifyProfile; - } else { + if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, &compiler_filter_)) { Usage("Unknown --compiler-filter value %s", compiler_filter_string); } } else if (option == "--compile-pic") { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 11a4e0631..c67ab6ef1 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -22,6 +22,7 @@ #include #include "base/macros.h" +#include "compiler_filter.h" #include "globals.h" #include "utils.h" @@ -29,20 +30,8 @@ namespace art { class CompilerOptions FINAL { public: - enum CompilerFilter { - kVerifyNone, // Skip verification and compile nothing except JNI stubs. - kInterpretOnly, // Verify, and compile only JNI stubs. - kVerifyAtRuntime, // Only compile JNI stubs and verify at runtime. - kSpace, // Maximize space savings. - kBalanced, // Try to get the best performance return on compilation investment. - kSpeed, // Maximize runtime performance. - kEverything, // Force compilation of everything capable of being compiled. - kTime, // Compile methods, but minimize compilation time. - kVerifyProfile, // Verify only the classes in the profile. - }; - // Guide heuristics to determine whether to compile method if profile data not available. - static const CompilerFilter kDefaultCompilerFilter = kSpeed; + static const CompilerFilter::Filter kDefaultCompilerFilter = CompilerFilter::kSpeed; static const size_t kDefaultHugeMethodThreshold = 10000; static const size_t kDefaultLargeMethodThreshold = 600; static const size_t kDefaultSmallMethodThreshold = 60; @@ -64,7 +53,7 @@ class CompilerOptions FINAL { CompilerOptions(); ~CompilerOptions(); - CompilerOptions(CompilerFilter compiler_filter, + CompilerOptions(CompilerFilter::Filter compiler_filter, size_t huge_method_threshold, size_t large_method_threshold, size_t small_method_threshold, @@ -88,40 +77,32 @@ class CompilerOptions FINAL { bool dump_cfg_append, bool force_determinism); - CompilerFilter GetCompilerFilter() const { + CompilerFilter::Filter GetCompilerFilter() const { return compiler_filter_; } - void SetCompilerFilter(CompilerFilter compiler_filter) { + void SetCompilerFilter(CompilerFilter::Filter compiler_filter) { compiler_filter_ = compiler_filter; } bool VerifyAtRuntime() const { - return compiler_filter_ == CompilerOptions::kVerifyAtRuntime; + return compiler_filter_ == CompilerFilter::kVerifyAtRuntime; } bool IsCompilationEnabled() const { - return compiler_filter_ != CompilerOptions::kVerifyNone && - compiler_filter_ != CompilerOptions::kInterpretOnly && - compiler_filter_ != CompilerOptions::kVerifyAtRuntime && - compiler_filter_ != CompilerOptions::kVerifyProfile; + return CompilerFilter::IsCompilationEnabled(compiler_filter_); } bool IsVerificationEnabled() const { - return compiler_filter_ != CompilerOptions::kVerifyNone && - compiler_filter_ != CompilerOptions::kVerifyAtRuntime; + return CompilerFilter::IsVerificationEnabled(compiler_filter_); } bool NeverVerify() const { - return compiler_filter_ == CompilerOptions::kVerifyNone; - } - - bool IsExtractOnly() const { - return compiler_filter_ == CompilerOptions::kVerifyAtRuntime; + return compiler_filter_ == CompilerFilter::kVerifyNone; } bool VerifyOnlyProfile() const { - return compiler_filter_ == CompilerOptions::kVerifyProfile; + return compiler_filter_ == CompilerFilter::kVerifyProfile; } size_t GetHugeMethodThreshold() const { @@ -271,7 +252,7 @@ class CompilerOptions FINAL { void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage); void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage); - CompilerFilter compiler_filter_; + CompilerFilter::Filter compiler_filter_; size_t huge_method_threshold_; size_t large_method_threshold_; size_t small_method_threshold_; @@ -317,7 +298,6 @@ class CompilerOptions FINAL { DISALLOW_COPY_AND_ASSIGN(CompilerOptions); }; -std::ostream& operator<<(std::ostream& os, const CompilerOptions::CompilerFilter& rhs); } // namespace art diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 124afbc73..082d15961 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -145,8 +145,8 @@ void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) { bool HGraphBuilder::SkipCompilation(const DexFile::CodeItem& code_item, size_t number_of_branches) { const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions(); - CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter(); - if (compiler_filter == CompilerOptions::kEverything) { + CompilerFilter::Filter compiler_filter = compiler_options.GetCompilerFilter(); + if (compiler_filter == CompilerFilter::kEverything) { return false; } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f1c5581c5..125c00d27 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -655,7 +655,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, // code units is bigger than 128. static constexpr size_t kSpaceFilterOptimizingThreshold = 128; const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); - if ((compiler_options.GetCompilerFilter() == CompilerOptions::kSpace) + if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace) && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) { MaybeRecordStat(MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index ede0bdad6..ec6f96f59 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -257,11 +257,14 @@ NO_RETURN static void Usage(const char* fmt, ...) { "|verify-at-runtime" "|verify-profile" "|interpret-only" + "|time" + "|space-profile" "|space" "|balanced" + "|speed-profile" "|speed" - "|everything" - "|time):"); + "|everything-profile" + "|everything):"); UsageError(" select compiler filter."); UsageError(" verify-profile requires a --profile(-fd) to also be passed in."); UsageError(" Example: --compiler-filter=everything"); @@ -798,10 +801,6 @@ class Dex2Oat FINAL { Usage("Profile file should not be specified with both --profile-file-fd and --profile-file"); } - if (compiler_options_->VerifyOnlyProfile() && !have_profile_file && !have_profile_fd) { - Usage("verify-profile compiler filter must be used with a profile file or fd"); - } - if (!parser_options->oat_symbols.empty()) { oat_unstripped_ = std::move(parser_options->oat_symbols); } @@ -834,14 +833,14 @@ class Dex2Oat FINAL { // time here, which is orthogonal to space. if (compiler_options_->inline_depth_limit_ == CompilerOptions::kUnsetInlineDepthLimit) { compiler_options_->inline_depth_limit_ = - (compiler_options_->compiler_filter_ == CompilerOptions::kSpace) + (compiler_options_->compiler_filter_ == CompilerFilter::kSpace) // Implementation of the space filter: limit inlining depth. ? CompilerOptions::kSpaceFilterInlineDepthLimit : CompilerOptions::kDefaultInlineDepthLimit; } if (compiler_options_->inline_max_code_units_ == CompilerOptions::kUnsetInlineMaxCodeUnits) { compiler_options_->inline_max_code_units_ = - (compiler_options_->compiler_filter_ == CompilerOptions::kSpace) + (compiler_options_->compiler_filter_ == CompilerFilter::kSpace) // Implementation of the space filter: limit inlining max code units. ? CompilerOptions::kSpaceFilterInlineMaxCodeUnits : CompilerOptions::kDefaultInlineMaxCodeUnits; @@ -1029,11 +1028,8 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kNativeDebuggableKey, compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue); - if (compiler_options_->IsExtractOnly()) { - key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue); - } else if (UseProfileGuidedCompilation()) { - key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue); - } + key_value_store_->Put(OatHeader::kCompilerFilter, + CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter())); } // Parse the arguments from the command line. In case of an unrecognized option or impossible @@ -1322,13 +1318,7 @@ class Dex2Oat FINAL { return false; } - if (compiler_options_->IsExtractOnly()) { - // ExtractOnly oat files only contain non-quickened DEX code and are - // therefore independent of the image file. - image_file_location_oat_checksum_ = 0u; - image_file_location_oat_data_begin_ = 0u; - image_patch_delta_ = 0; - } else { + if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) { TimingLogger::ScopedTiming t3("Loading image checksum", timings_); std::vector image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); @@ -1345,6 +1335,10 @@ class Dex2Oat FINAL { if (!image_file_location.empty()) { key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); } + } else { + image_file_location_oat_checksum_ = 0u; + image_file_location_oat_data_begin_ = 0u; + image_patch_delta_ = 0; } // Open dex files for class path. @@ -1456,7 +1450,7 @@ class Dex2Oat FINAL { num_methods += dex_file->NumMethodIds(); } if (num_methods <= compiler_options_->GetNumDexMethodsThreshold()) { - compiler_options_->SetCompilerFilter(CompilerOptions::kSpeed); + compiler_options_->SetCompilerFilter(CompilerFilter::kSpeed); VLOG(compiler) << "Below method threshold, compiling anyways"; } } @@ -1857,7 +1851,7 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd); + return CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter()); } bool LoadProfile() { @@ -1865,7 +1859,7 @@ class Dex2Oat FINAL { profile_compilation_info_.reset(new ProfileCompilationInfo()); ScopedFlock flock; - bool success = false; + bool success = true; std::string error; if (profile_file_fd_ != -1) { // The file doesn't need to be flushed so don't check the usage. @@ -1874,7 +1868,7 @@ class Dex2Oat FINAL { if (flock.Init(&file, &error)) { success = profile_compilation_info_->Load(profile_file_fd_); } - } else { + } else if (profile_file_ != "") { if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) { success = profile_compilation_info_->Load(flock.GetFile()->Fd()); } diff --git a/runtime/Android.mk b/runtime/Android.mk index fc96acf4b..0c6541ebe 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -44,6 +44,7 @@ LIBART_COMMON_SRC_FILES := \ class_table.cc \ code_simulator_container.cc \ common_throws.cc \ + compiler_filter.cc \ debugger.cc \ dex_file.cc \ dex_file_verifier.cc \ diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 729957f31..f58af5a8d 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -72,7 +72,7 @@ ScratchFile::ScratchFile() { filename_ = getenv("ANDROID_DATA"); filename_ += "/TmpFile-XXXXXX"; int fd = mkstemp(&filename_[0]); - CHECK_NE(-1, fd); + CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_; file_.reset(new File(fd, GetFilename(), true)); } diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc new file mode 100644 index 000000000..31a1bc10f --- /dev/null +++ b/runtime/compiler_filter.cc @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2016 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 "compiler_filter.h" + +#include "utils.h" + +namespace art { + +bool CompilerFilter::IsCompilationEnabled(Filter filter) { + switch (filter) { + case CompilerFilter::kVerifyNone: + case CompilerFilter::kVerifyAtRuntime: + case CompilerFilter::kVerifyProfile: + case CompilerFilter::kInterpretOnly: return false; + + case CompilerFilter::kSpaceProfile: + case CompilerFilter::kSpace: + case CompilerFilter::kBalanced: + case CompilerFilter::kTime: + case CompilerFilter::kSpeedProfile: + case CompilerFilter::kSpeed: + case CompilerFilter::kEverythingProfile: + case CompilerFilter::kEverything: return true; + } + UNREACHABLE(); +} + +bool CompilerFilter::IsVerificationEnabled(Filter filter) { + switch (filter) { + case CompilerFilter::kVerifyNone: + case CompilerFilter::kVerifyAtRuntime: return false; + + case CompilerFilter::kVerifyProfile: + case CompilerFilter::kInterpretOnly: + case CompilerFilter::kSpaceProfile: + case CompilerFilter::kSpace: + case CompilerFilter::kBalanced: + case CompilerFilter::kTime: + case CompilerFilter::kSpeedProfile: + case CompilerFilter::kSpeed: + case CompilerFilter::kEverythingProfile: + case CompilerFilter::kEverything: return true; + } + UNREACHABLE(); +} + +bool CompilerFilter::DependsOnImageChecksum(Filter filter) { + // We run dex2dex with verification, so the oat file will depend on the + // image checksum if verification is enabled. + return IsVerificationEnabled(filter); +} + +bool CompilerFilter::DependsOnProfile(Filter filter) { + switch (filter) { + case CompilerFilter::kVerifyNone: + case CompilerFilter::kVerifyAtRuntime: + case CompilerFilter::kInterpretOnly: + case CompilerFilter::kSpace: + case CompilerFilter::kBalanced: + case CompilerFilter::kTime: + case CompilerFilter::kSpeed: + case CompilerFilter::kEverything: return false; + + case CompilerFilter::kVerifyProfile: + case CompilerFilter::kSpaceProfile: + case CompilerFilter::kSpeedProfile: + case CompilerFilter::kEverythingProfile: return true; + } + UNREACHABLE(); +} + +bool CompilerFilter::IsAsGoodAs(Filter current, Filter target) { + return current >= target; +} + +std::string CompilerFilter::NameOfFilter(Filter filter) { + switch (filter) { + case CompilerFilter::kVerifyNone: return "verify-none"; + case CompilerFilter::kVerifyAtRuntime: return "verify-at-runtime"; + case CompilerFilter::kVerifyProfile: return "verify-profile"; + case CompilerFilter::kInterpretOnly: return "interpret-only"; + case CompilerFilter::kSpaceProfile: return "space-profile"; + case CompilerFilter::kSpace: return "space"; + case CompilerFilter::kBalanced: return "balanced"; + case CompilerFilter::kTime: return "time"; + case CompilerFilter::kSpeedProfile: return "speed-profile"; + case CompilerFilter::kSpeed: return "speed"; + case CompilerFilter::kEverythingProfile: return "everything-profile"; + case CompilerFilter::kEverything: return "everything"; + } + UNREACHABLE(); +} + +bool CompilerFilter::ParseCompilerFilter(const char* option, Filter* filter) { + CHECK(filter != nullptr); + + if (strcmp(option, "verify-none") == 0) { + *filter = kVerifyNone; + } else if (strcmp(option, "interpret-only") == 0) { + *filter = kInterpretOnly; + } else if (strcmp(option, "verify-profile") == 0) { + *filter = kVerifyProfile; + } else if (strcmp(option, "verify-at-runtime") == 0) { + *filter = kVerifyAtRuntime; + } else if (strcmp(option, "space") == 0) { + *filter = kSpace; + } else if (strcmp(option, "space-profile") == 0) { + *filter = kSpaceProfile; + } else if (strcmp(option, "balanced") == 0) { + *filter = kBalanced; + } else if (strcmp(option, "speed") == 0) { + *filter = kSpeed; + } else if (strcmp(option, "speed-profile") == 0) { + *filter = kSpeedProfile; + } else if (strcmp(option, "everything") == 0) { + *filter = kEverything; + } else if (strcmp(option, "everything-profile") == 0) { + *filter = kEverythingProfile; + } else if (strcmp(option, "time") == 0) { + *filter = kTime; + } else { + return false; + } + return true; +} + +std::ostream& operator<<(std::ostream& os, const CompilerFilter::Filter& rhs) { + return os << CompilerFilter::NameOfFilter(rhs); +} + +} // namespace art diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h new file mode 100644 index 000000000..1bea8b4f5 --- /dev/null +++ b/runtime/compiler_filter.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 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_COMPILER_FILTER_H_ +#define ART_RUNTIME_COMPILER_FILTER_H_ + +#include +#include +#include + +#include "base/macros.h" + +namespace art { + +class CompilerFilter FINAL { + public: + // Note: Order here matters. Later filter choices are considered "as good + // as" earlier filter choices. + enum Filter { + kVerifyNone, // Skip verification and compile nothing except JNI stubs. + kVerifyAtRuntime, // Only compile JNI stubs and verify at runtime. + kVerifyProfile, // Verify only the classes in the profile. + kInterpretOnly, // Verify, and compile only JNI stubs. + kTime, // Compile methods, but minimize compilation time. + kSpaceProfile, // Maximize space savings based on profile. + kSpace, // Maximize space savings. + kBalanced, // Good performance return on compilation investment. + kSpeedProfile, // Maximize runtime performance based on profile. + kSpeed, // Maximize runtime performance. + kEverythingProfile, // Compile everything capable of being compiled based on profile. + kEverything, // Compile everything capable of being compiled. + }; + + // Returns true if an oat file with this compiler filter contains + // compiled executable code. + static bool IsCompilationEnabled(Filter filter); + + // Returns true if this compiler filter requires running verification. + static bool IsVerificationEnabled(Filter filter); + + // Returns true if an oat file with this compiler filter depends on the + // boot image checksum. + static bool DependsOnImageChecksum(Filter filter); + + // Returns true if an oat file with this compiler filter depends on a + // profile. + static bool DependsOnProfile(Filter filter); + + // Returns true if the 'current' compiler filter is considered at least as + // good as the 'target' compilation type. + // For example: kSpeed is as good as kInterpretOnly, but kInterpretOnly is + // not as good as kSpeed. + static bool IsAsGoodAs(Filter current, Filter target); + + // Return the flag name of the given filter. + // For example: given kVerifyAtRuntime, returns "verify-at-runtime". + // The name returned corresponds to the name accepted by + // ParseCompilerFilter. + static std::string NameOfFilter(Filter filter); + + // Parse the compiler filter from the given name. + // Returns true and sets filter to the parsed value if name refers to a + // valid filter. Returns false if no filter matches that name. + // 'filter' must be non-null. + static bool ParseCompilerFilter(const char* name, /*out*/Filter* filter); + + private: + DISALLOW_COPY_AND_ASSIGN(CompilerFilter); +}; + +std::ostream& operator<<(std::ostream& os, const CompilerFilter::Filter& rhs); + +} // namespace art + +#endif // ART_RUNTIME_COMPILER_FILTER_H_ diff --git a/runtime/compiler_filter_test.cc b/runtime/compiler_filter_test.cc new file mode 100644 index 000000000..c603be6e5 --- /dev/null +++ b/runtime/compiler_filter_test.cc @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 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 "compiler_filter.h" + +#include + +namespace art { + +static void TestCompilerFilterName(CompilerFilter::Filter filter, std::string name) { + CompilerFilter::Filter parsed; + EXPECT_TRUE(CompilerFilter::ParseCompilerFilter(name.c_str(), &parsed)); + EXPECT_EQ(filter, parsed); + + EXPECT_EQ(name, CompilerFilter::NameOfFilter(filter)); +} + +// Verify the dexopt status values from dalvik.system.DexFile +// match the OatFileAssistant::DexOptStatus values. +TEST(CompilerFilterTest, ParseCompilerFilter) { + CompilerFilter::Filter filter; + + TestCompilerFilterName(CompilerFilter::kVerifyNone, "verify-none"); + TestCompilerFilterName(CompilerFilter::kVerifyAtRuntime, "verify-at-runtime"); + TestCompilerFilterName(CompilerFilter::kVerifyProfile, "verify-profile"); + TestCompilerFilterName(CompilerFilter::kInterpretOnly, "interpret-only"); + TestCompilerFilterName(CompilerFilter::kTime, "time"); + TestCompilerFilterName(CompilerFilter::kSpaceProfile, "space-profile"); + TestCompilerFilterName(CompilerFilter::kSpace, "space"); + TestCompilerFilterName(CompilerFilter::kBalanced, "balanced"); + TestCompilerFilterName(CompilerFilter::kSpeedProfile, "speed-profile"); + TestCompilerFilterName(CompilerFilter::kSpeed, "speed"); + TestCompilerFilterName(CompilerFilter::kEverythingProfile, "everything-profile"); + TestCompilerFilterName(CompilerFilter::kEverything, "everything"); + + EXPECT_FALSE(CompilerFilter::ParseCompilerFilter("super-awesome-filter", &filter)); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index f1e0fa7b5..3397989fd 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -348,7 +348,8 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie static jint GetDexOptNeeded(JNIEnv* env, const char* filename, const char* instruction_set, - const int target_compilation_type_mask) { + const char* compiler_filter_name, + bool profile_changed) { if ((filename == nullptr) || !OS::FileExists(filename)) { LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist"; ScopedLocalRef fnfe(env, env->FindClass("java/io/FileNotFoundException")); @@ -365,17 +366,24 @@ static jint GetDexOptNeeded(JNIEnv* env, return -1; } + CompilerFilter::Filter filter; + if (!CompilerFilter::ParseCompilerFilter(compiler_filter_name, &filter)) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Compiler filter %s is invalid.", compiler_filter_name)); + env->ThrowNew(iae.get(), message.c_str()); + return -1; + } + // TODO: Verify the dex location is well formed, and throw an IOException if // not? - OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask, - target_instruction_set, false); + + OatFileAssistant oat_file_assistant(filename, target_instruction_set, profile_changed, false); // Always treat elements of the bootclasspath as up-to-date. if (oat_file_assistant.IsInBootClassPath()) { return OatFileAssistant::kNoDexOptNeeded; } - - return oat_file_assistant.GetDexOptNeeded(); + return oat_file_assistant.GetDexOptNeeded(filter); } static jint DexFile_getDexOptNeeded(JNIEnv* env, @@ -393,10 +401,18 @@ static jint DexFile_getDexOptNeeded(JNIEnv* env, return -1; } + // TODO: Take profile changed and compiler filter as arguments. + // For now, we use "speed" by default, unless EXTRACT_ONLY = 0x4 was + // included in the mask. + const char* compiler_filter = "speed"; + if (javaTargetCompilationTypeMask & 0x4) { + compiler_filter = "verify-at-runtime"; + } return GetDexOptNeeded(env, filename.c_str(), instruction_set.c_str(), - javaTargetCompilationTypeMask); + compiler_filter, + /*profile_changed*/false); } // public API @@ -407,7 +423,8 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename env, filename.c_str(), instruction_set, - OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation); + "speed-profile", + /*profile_changed*/false); return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE; } diff --git a/runtime/oat.cc b/runtime/oat.cc index ed99cbabb..d13999a83 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -29,8 +29,6 @@ constexpr uint8_t OatHeader::kOatMagic[4]; constexpr uint8_t OatHeader::kOatVersion[4]; constexpr const char OatHeader::kTrueValue[]; constexpr const char OatHeader::kFalseValue[]; -constexpr const char OatHeader::kExtractOnlyValue[]; -constexpr const char OatHeader::kProfileGuideCompiledValue[]; static size_t ComputeOatHeaderSize(const SafeMap* variable_data) { size_t estimate = 0U; @@ -472,16 +470,13 @@ bool OatHeader::IsNativeDebuggable() const { return IsKeyEnabled(OatHeader::kNativeDebuggableKey); } -bool OatHeader::IsExtractOnly() const { - return KeyHasValue(kCompilationType, - kExtractOnlyValue, - sizeof(kExtractOnlyValue)); -} - -bool OatHeader::IsProfileGuideCompiled() const { - return KeyHasValue(kCompilationType, - kProfileGuideCompiledValue, - sizeof(kProfileGuideCompiledValue)); +CompilerFilter::Filter OatHeader::GetCompilerFilter() const { + CompilerFilter::Filter filter; + const char* key_value = GetStoreValueByKey(kCompilerFilter); + CHECK(key_value != nullptr) << "compiler-filter not found in oat header"; + CHECK(CompilerFilter::ParseCompilerFilter(key_value, &filter)) + << "Invalid compiler-filter in oat header: " << key_value; + return filter; } bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const { diff --git a/runtime/oat.h b/runtime/oat.h index 1d6c076c1..0dcc52e51 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -21,6 +21,7 @@ #include "arch/instruction_set.h" #include "base/macros.h" +#include "compiler_filter.h" #include "dex_file.h" #include "safe_map.h" @@ -31,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '7', '5', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '7', '6', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; @@ -39,14 +40,12 @@ class PACKED(4) OatHeader { static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; static constexpr const char* kNativeDebuggableKey = "native-debuggable"; - static constexpr const char* kCompilationType = "compilation-type"; + static constexpr const char* kCompilerFilter = "compiler-filter"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; - static constexpr const char kExtractOnlyValue[] = "extract-only"; - static constexpr const char kProfileGuideCompiledValue[] = "profile-guide"; static OatHeader* Create(InstructionSet instruction_set, @@ -112,8 +111,7 @@ class PACKED(4) OatHeader { bool IsPic() const; bool IsDebuggable() const; bool IsNativeDebuggable() const; - bool IsExtractOnly() const; - bool IsProfileGuideCompiled() const; + CompilerFilter::Filter GetCompilerFilter() const; private: bool KeyHasValue(const char* key, const char* value, size_t value_size) const; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 033ea563b..9ae033f22 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1257,12 +1257,8 @@ bool OatFile::IsDebuggable() const { return GetOatHeader().IsDebuggable(); } -bool OatFile::IsExtractOnly() const { - return GetOatHeader().IsExtractOnly(); -} - -bool OatFile::IsProfileGuideCompiled() const { - return GetOatHeader().IsProfileGuideCompiled(); +CompilerFilter::Filter OatFile::GetCompilerFilter() const { + return GetOatHeader().GetCompilerFilter(); } static constexpr char kDexClassPathEncodingSeparator = '*'; diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 7af77aee8..21aeab4c7 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -92,9 +92,7 @@ class OatFile { // Indicates whether the oat file was compiled with full debugging capability. bool IsDebuggable() const; - bool IsExtractOnly() const; - - bool IsProfileGuideCompiled() const; + CompilerFilter::Filter GetCompilerFilter() const; const std::string& GetLocation() const { return location_; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index cbc0ec6d2..096296b4b 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 @@ bool OatFileAssistant::Lock(std::string* error_msg) { 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; +} + +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() { - if (OatFileIsUpToDate() || OdexFileIsUpToDate()) { - return kNoDexOptNeeded; +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 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& } 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 @@ bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) { } } - 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 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,44 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { return false; } - if (file.IsPic() || file.IsExtractOnly()) { - // 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; - } + CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter(); - const ImageInfo* image_info = GetImageInfo(); - if (image_info == nullptr) { - VLOG(oat) << "No image to check oat relocation against."; - return false; - } + if (CompilerFilter::IsCompilationEnabled(current_compiler_filter)) { + 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_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; + // 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. + VLOG(oat) << "Oat relocation test skipped for PIC oat file"; + } + } else { + VLOG(oat) << "Oat relocation test skipped for compiler filter " << current_compiler_filter; } return true; } @@ -589,18 +625,9 @@ bool OatFileAssistant::RelocateOatFile(const std::string* input_file, 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 +669,7 @@ bool OatFileAssistant::GenerateOatFile(std::string* error_msg) { 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 +779,7 @@ bool OatFileAssistant::DexFilenameToOdexFilename(const std::string& location, 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 diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 893aea2ab..452cd8472 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -24,6 +24,7 @@ #include "arch/instruction_set.h" #include "base/scoped_flock.h" #include "base/unix_file/fd_file.h" +#include "compiler_filter.h" #include "oat_file.h" #include "os.h" #include "profiler.h" @@ -85,20 +86,6 @@ class OatFileAssistant { kOatUpToDate, }; - // Represents the different compilation types of oat files that OatFileAssitant - // and external GetDexOptNeeded callers care about. - // Note: these should be able to be used as part of a mask. - enum CompilationType { - // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1 - kFullCompilation = 1, - - // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2 - kProfileGuideCompilation = 2, - - // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4 - kExtractOnly = 4, - }; - // Constructs an OatFileAssistant object to assist the oat file // corresponding to the given dex location with the target instruction set. // @@ -110,27 +97,26 @@ class OatFileAssistant { // Note: Currently the dex_location must have an extension. // TODO: Relax this restriction? // - // The target compilation type specifies a set of CompilationTypes that - // should be considered up to date. An oat file compiled in a way not - // included in the set is considered out of date. For example, to consider - // otherwise up-to-date fully compiled and profile-guide compiled oat - // files as up to date, but to consider extract-only files as out of date, - // specify: (kFullCompilation | kProfileGuideCompilation). - // // 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. + // + // profile_changed should be true if the profile has recently changed + // for this dex location. + // + // load_executable should be true if the caller intends to try and load + // executable code for this dex location. OatFileAssistant(const char* dex_location, - int target_compilation_type_mask, const InstructionSet isa, + bool profile_changed, 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, - int target_compilation_type_mask, const InstructionSet isa, + bool profile_changed, bool load_executable); ~OatFileAssistant(); @@ -158,16 +144,18 @@ class OatFileAssistant { bool Lock(std::string* error_msg); // Return what action needs to be taken to produce up-to-date code for this - // dex location. - DexOptNeeded GetDexOptNeeded(); + // dex location that is at least as good as an oat file generated with the + // given compiler filter. + DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter); // Attempts to generate or relocate the oat file as needed to make it up to - // date. + // date with in a way that is at least as good as an oat file generated with + // the given compiler filter. // 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 null. - bool MakeUpToDate(std::string* error_msg); + bool MakeUpToDate(CompilerFilter::Filter target_compiler_filter, std::string* error_msg); // Returns an oat file that can be used for loading dex files. // Returns null if no suitable oat file was found. @@ -251,7 +239,7 @@ class OatFileAssistant { // describing why there was failure. error_msg must not be null. bool RelocateOatFile(const std::string* input_file, std::string* error_msg); - // Generate the oat file from the dex file. + // Generate the oat file from the dex file using the given compiler filter. // This does not check the current status before attempting to generate the // oat file. // Returns true on success. @@ -259,7 +247,7 @@ class OatFileAssistant { // // 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 null. - bool GenerateOatFile(std::string* error_msg); + bool GenerateOatFile(CompilerFilter::Filter filter, 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 @@ -315,6 +303,10 @@ class OatFileAssistant { // The caller shouldn't clean up or free the returned pointer. const OatFile* GetOdexFile(); + // Returns true if the compiler filter used to generate the odex file is at + // least as good as the given target filter. + bool OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target); + // Returns true if the odex file is opened executable. bool OdexFileIsExecutable(); @@ -327,6 +319,10 @@ class OatFileAssistant { // The caller shouldn't clean up or free the returned pointer. const OatFile* GetOatFile(); + // Returns true if the compiler filter used to generate the oat file is at + // least as good as the given target filter. + bool OatFileCompilerFilterIsOkay(CompilerFilter::Filter target); + // Returns true if the oat file is opened executable. bool OatFileIsExecutable(); @@ -346,12 +342,14 @@ class OatFileAssistant { ScopedFlock flock_; std::string dex_location_; - const int target_compilation_type_mask_; // In a properly constructed OatFileAssistant object, isa_ should be either // the 32 or 64 bit variant for the current device. const InstructionSet isa_ = kNone; + // Whether the profile has recently changed. + bool profile_changed_ = false; + // Whether we will attempt to load oat files executable. bool load_executable_ = false; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 046d8ae77..634e04828 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -188,7 +188,8 @@ class OatFileAssistantTest : public CommonRuntimeTest { // Generate a non-PIC odex file for the purposes of test. // The generated odex file will be un-relocated. void GenerateOdexForTest(const std::string& dex_location, - const std::string& odex_location) { + const std::string& odex_location, + CompilerFilter::Filter filter) { // To generate an un-relocated odex file, we first compile a relocated // version of the file, then manually call patchoat to make it look as if // it is unrelocated. @@ -196,11 +197,12 @@ class OatFileAssistantTest : public CommonRuntimeTest { std::vector args; args.push_back("--dex-file=" + dex_location); args.push_back("--oat-file=" + relocated_odex_location); - args.push_back("--include-patch-information"); + args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); // We need to use the quick compiler to generate non-PIC code, because // the optimizing compiler always generates PIC. args.push_back("--compiler-backend=Quick"); + args.push_back("--include-patch-information"); std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; @@ -227,21 +229,25 @@ class OatFileAssistantTest : public CommonRuntimeTest { dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + EXPECT_FALSE(odex_file->IsPic()); + EXPECT_EQ(filter, odex_file->GetCompilerFilter()); - const std::vector image_spaces = + if (CompilerFilter::IsCompilationEnabled(filter)) { + const std::vector image_spaces = runtime->GetHeap()->GetBootImageSpaces(); - ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); - const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); - const OatHeader& oat_header = odex_file->GetOatHeader(); - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum()); - EXPECT_NE(reinterpret_cast(image_header.GetOatDataBegin()), - oat_header.GetImageFileLocationOatDataBegin()); - EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta()); + ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); + const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); + const OatHeader& oat_header = odex_file->GetOatHeader(); + EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum()); + EXPECT_NE(reinterpret_cast(image_header.GetOatDataBegin()), + oat_header.GetImageFileLocationOatDataBegin()); + EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta()); + } } void GeneratePicOdexForTest(const std::string& dex_location, - const std::string& odex_location) { + const std::string& odex_location, + CompilerFilter::Filter filter) { // Temporarily redirect the dalvik cache so dex2oat doesn't find the // relocated image file. std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; @@ -249,6 +255,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { std::vector args; args.push_back("--dex-file=" + dex_location); args.push_back("--oat-file=" + odex_location); + args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); args.push_back("--compile-pic"); args.push_back("--runtime-arg"); args.push_back("-Xnorelocate"); @@ -267,55 +274,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; EXPECT_TRUE(odex_file->IsPic()); - } - - void GenerateExtractOnlyOdexForTest(const std::string& dex_location, - const std::string& odex_location) { - std::vector args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=verify-at-runtime"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - // Verify the odex file was generated as expected. - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_TRUE(odex_file->IsExtractOnly()); - EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u); - EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u); - EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0); - } - - void GenerateProfileGuideOdexForTest(const std::string& dex_location, - const std::string& odex_location) { - std::vector args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - ScratchFile profile_file; - args.push_back("--profile-file=" + profile_file.GetFilename()); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - // Verify the odex file was generated as expected. - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - printf("error %s", error_msg.c_str()); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_TRUE(odex_file->IsProfileGuideCompiled()); + EXPECT_EQ(filter, odex_file->GetCompilerFilter()); } private: @@ -382,12 +341,32 @@ class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest { // 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, - OatFileAssistant::kFullCompilation, kRuntimeISA, false); +static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) { + // Use an oat file assistant to find the proper oat location. + OatFileAssistant ofa(dex_location, kRuntimeISA, false, false); + const std::string* oat_location = ofa.OatFileName(); + ASSERT_TRUE(oat_location != nullptr); + std::vector args; + args.push_back("--dex-file=" + std::string(dex_location)); + args.push_back("--oat-file=" + *oat_location); + args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + args.push_back("--runtime-arg"); + args.push_back("-Xnorelocate"); std::string error_msg; - ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + // Verify the oat file was generated as expected. + std::unique_ptr oat_file(OatFile::Open(oat_location->c_str(), + oat_location->c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex_location, + &error_msg)); + ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; + EXPECT_EQ(filter, oat_file->GetCompilerFilter()); } // Case: We have a DEX file, but no OAT file for it. @@ -396,10 +375,16 @@ TEST_F(OatFileAssistantTest, DexNoOat) { std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -420,15 +405,15 @@ TEST_F(OatFileAssistantTest, DexNoOat) { TEST_F(OatFileAssistantTest, NoDexNoOat) { std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Trying to make the oat file up to date should not fail or crash. std::string error_msg; - EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)); + EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)); // Trying to get the best oat file should fail, but not crash. std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); @@ -440,12 +425,19 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) { TEST_F(OatFileAssistantTest, OatUpToDate) { std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; Copy(GetDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); + + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); @@ -458,16 +450,68 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and speed-profile OAT file for it. +// Expect: The status is kNoDexOptNeeded if the profile hasn't changed. +TEST_F(OatFileAssistantTest, ProfileOatUpToDate) { + std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar"; + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); + + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + + 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::kOatUpToDate, oat_file_assistant.OatFileStatus()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + +// Case: We have a DEX file and speed-profile OAT file for it. +// Expect: The status is kNoDex2OatNeeded if the profile has changed. +TEST_F(OatFileAssistantTest, ProfileOatOutOfDate) { + std::string dex_location = GetScratchDir() + "/ProfileOatOutOfDate.jar"; + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true, false); + + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + + 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.OatFileNeedsRelocation()); + EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + // Case: We have a MultiDEX file and up-to-date OAT file for it. // Expect: The status is kNoDexOptNeeded and we load all dex files. TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar"; Copy(GetMultiDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); // Verify we can load both dex files. @@ -486,15 +530,15 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { // Compile code for GetMultiDexSrc1. Copy(GetMultiDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum // is out of date. Copy(GetMultiDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -513,6 +557,7 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { args.push_back("--dex-file=" + dex_location); args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar")); args.push_back("--oat-file=" + oat_location); + args.push_back("--compiler-filter=speed"); std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; @@ -520,8 +565,7 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { // Verify we can load both dex files. OatFileAssistant oat_file_assistant(dex_location.c_str(), oat_location.c_str(), - OatFileAssistant::kFullCompilation, - kRuntimeISA, true); + kRuntimeISA, false, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); EXPECT_TRUE(oat_file->IsExecutable()); @@ -538,12 +582,14 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { // 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()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); Copy(GetDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -563,13 +609,15 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -594,16 +642,16 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Strip the dex file Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -616,9 +664,10 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { // Make the oat file up to date. std::string error_msg; - ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; + ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg; - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -646,20 +695,24 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { // Create the oat file from a different dex file so it looks out of date. Copy(GetDexSrc2(), dex_location); - GenerateOatForTest(dex_location.c_str()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); // Create the odex file Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Strip the dex file. Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, // Can't run dex2oat because dex file is stripped. + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -673,9 +726,12 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { // Make the oat file up to date. std::string error_msg; - ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; + ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg; - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, // Can't run dex2oat because dex file is stripped. + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -705,10 +761,14 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -722,9 +782,10 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { // Make the oat file up to date. This should have no effect. std::string error_msg; - EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; + EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg; - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -746,12 +807,17 @@ TEST_F(OatFileAssistantTest, SelfRelocation) { // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, oat_location); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); OatFileAssistant oat_file_assistant(dex_location.c_str(), - oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true); + oat_location.c_str(), kRuntimeISA, false, true); - EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -766,9 +832,10 @@ TEST_F(OatFileAssistantTest, SelfRelocation) { // Make the oat file up to date. std::string error_msg; - ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; + ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg; - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -799,7 +866,7 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Create the oat file by copying the odex so they are located in the same // place in memory. @@ -807,9 +874,10 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // Verify things don't go bad. OatFileAssistant oat_file_assistant(dex_location.c_str(), - oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true); + oat_location.c_str(), kRuntimeISA, false, true); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -838,13 +906,15 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GeneratePicOdexForTest(dex_location, odex_location); + GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -856,22 +926,23 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } -// Case: We have a DEX file and a ExtractOnly ODEX file, but no OAT file. -// Expect: The status is kNoDexOptNeeded, because ExtractOnly contains no code. -TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) { - std::string dex_location = GetScratchDir() + "/DexExtractOnlyOdexNoOat.jar"; - std::string odex_location = GetOdexDir() + "/DexExtractOnlyOdexNoOat.odex"; +// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file. +// Expect: The status is kNoDexOptNeeded, because VerifyAtRuntime contains no code. +TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex"; // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GenerateExtractOnlyOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerifyAtRuntime); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly, - kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_TRUE(oat_file_assistant.OdexFileExists()); @@ -889,11 +960,29 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) { std::string dex_location = GetScratchDir() + "/LoadOatUpToDate.jar"; Copy(GetDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); + + std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); + ASSERT_TRUE(oat_file.get() != nullptr); + EXPECT_TRUE(oat_file->IsExecutable()); + std::vector> 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 interpret-only OAT file for it. +// Expect: We should still load the oat file as executable. +TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) { + std::string dex_location = GetScratchDir() + "/LoadExecInterpretOnlyOatUpToDate.jar"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kInterpretOnly); + + // Load the oat using an oat file assistant. + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -909,11 +998,10 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { std::string dex_location = GetScratchDir() + "/LoadNoExecOatUpToDate.jar"; Copy(GetDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str()); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -933,10 +1021,9 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { Copy(GetDexSrc1(), dex_location); OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true); std::string error_msg; - ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; + ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg; std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -948,8 +1035,7 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { 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(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false, false); EXPECT_FALSE(ofm.OatFileExists()); } @@ -965,10 +1051,9 @@ TEST_F(OatFileAssistantTest, LoadDexUnwriteableAlternateOat) { Copy(GetDexSrc1(), dex_location); OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true); std::string error_msg; - ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg)); + ASSERT_FALSE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() == nullptr); @@ -981,10 +1066,9 @@ TEST_F(OatFileAssistantTest, GenNoDex) { std::string oat_location = GetScratchDir() + "/GenNoDex.oat"; OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true); std::string error_msg; - ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg)); + ASSERT_FALSE(oat_file_assistant.GenerateOatFile(CompilerFilter::kSpeed, &error_msg)); } // Turn an absolute path into a path relative to the current working @@ -1030,11 +1114,11 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { Copy(GetDexSrc1(), abs_dex_location); std::string dex_location = MakePathRelative(abs_dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); EXPECT_FALSE(oat_file_assistant.OatFileExists()); EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); @@ -1048,11 +1132,11 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { TEST_F(OatFileAssistantTest, ShortDexLocation) { std::string dex_location = "/xx"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); EXPECT_FALSE(oat_file_assistant.OatFileExists()); EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); @@ -1063,7 +1147,7 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) { // Trying to make it up to date should have no effect. std::string error_msg; - EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)); + EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)); EXPECT_TRUE(error_msg.empty()); } @@ -1073,10 +1157,10 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_FALSE(oat_file_assistant.OdexFileExists()); @@ -1168,11 +1252,10 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) { // Create the dex and odex files Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1191,11 +1274,10 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) { // Create the dex and odex files Copy(GetMultiDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1223,45 +1305,6 @@ TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) { "/foo/bar/baz_noext", kArm, &odex_file, &error_msg)); } -// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT. -// Expect: The status depends on the target compilation type mask. -TEST_F(OatFileAssistantTest, TargetCompilationType) { - std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar"; - std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex"; - Copy(GetDexSrc1(), dex_location); - GenerateExtractOnlyOdexForTest(dex_location, odex_location); - GenerateOatForTest(dex_location.c_str()); - - OatFileAssistant ofa_full(dex_location.c_str(), - OatFileAssistant::kFullCompilation, kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded()); - EXPECT_FALSE(ofa_full.IsInBootClassPath()); - EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate()); - EXPECT_TRUE(ofa_full.OatFileIsUpToDate()); - - OatFileAssistant ofa_extract(dex_location.c_str(), - OatFileAssistant::kExtractOnly, kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded()); - EXPECT_FALSE(ofa_extract.IsInBootClassPath()); - EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate()); - EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate()); - - OatFileAssistant ofa_profile(dex_location.c_str(), - OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded()); - EXPECT_FALSE(ofa_profile.IsInBootClassPath()); - EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate()); - EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate()); - - OatFileAssistant ofa_extract_full(dex_location.c_str(), - OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly, - kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded()); - EXPECT_FALSE(ofa_extract_full.IsInBootClassPath()); - EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate()); - EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate()); -} - // Verify the dexopt status values from dalvik.system.DexFile // match the OatFileAssistant::DexOptStatus values. TEST_F(OatFileAssistantTest, DexOptStatusValues) { @@ -1296,28 +1339,12 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { ASSERT_FALSE(self_patchoat_needed == nullptr); EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get())); - - ArtField* compilation_type_full = mirror::Class::FindStaticField( - soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I"); - ASSERT_FALSE(compilation_type_full == nullptr); - EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get())); - - ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField( - soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I"); - ASSERT_FALSE(compilation_type_profile_guide == nullptr); - EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation, - compilation_type_profile_guide->GetInt(dexfile.Get())); - - ArtField* compilation_type_extract_only = mirror::Class::FindStaticField( - soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I"); - ASSERT_FALSE(compilation_type_extract_only == nullptr); - EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get())); } // TODO: More Tests: +// * Image checksum change is out of date for kIntepretOnly, but not +// kVerifyAtRuntime. But target of kVerifyAtRuntime still says current +// kInterpretOnly is out of date. // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat // * Test using secondary isa diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 3ec382637..2f13f5574 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -44,6 +44,8 @@ static constexpr bool kDuplicateClassesCheck = kIsDebugBuild; // If true, then we attempt to load the application image if it exists. static constexpr bool kEnableAppImage = true; +CompilerFilter::Filter OatFileManager::filter_ = CompilerFilter::Filter::kSpeed; + const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr oat_file) { WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); DCHECK(oat_file != nullptr); @@ -308,13 +310,10 @@ std::vector> OatFileManager::OpenDexFilesFromOat( Locks::mutator_lock_->AssertNotHeld(self); Runtime* const runtime = Runtime::Current(); - int target_compilation_type_mask = OatFileAssistant::kFullCompilation - | OatFileAssistant::kProfileGuideCompilation - | OatFileAssistant::kExtractOnly; OatFileAssistant oat_file_assistant(dex_location, oat_location, - target_compilation_type_mask, kRuntimeISA, + /*profile_changed*/false, !runtime->IsAotCompiler()); // Lock the target oat location to avoid races generating and loading the @@ -330,7 +329,7 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // 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(/*out*/&error_msg)) { + if (!oat_file_assistant.MakeUpToDate(filter_, /*out*/ &error_msg)) { LOG(INFO) << error_msg; } @@ -484,15 +483,7 @@ void OatFileManager::DumpForSigQuit(std::ostream& os) { if (ContainsElement(boot_oat_files, oat_file.get())) { continue; } - // Use "platform-default" if it's neither extract nor profile guided. - // Saying 'full' could be misleading if for example the platform uses - // compiler filters. - const char* status = oat_file->IsExtractOnly() - ? OatHeader::kExtractOnlyValue - : oat_file->IsProfileGuideCompiled() - ? OatHeader::kProfileGuideCompiledValue - : "platform-default"; - os << oat_file->GetLocation() << ": " << status << "\n"; + os << oat_file->GetLocation() << ": " << oat_file->GetCompilerFilter() << "\n"; } } diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index a541d1022..574d0e258 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "base/mutex.h" +#include "compiler_filter.h" #include "jni.h" namespace art { @@ -110,6 +111,10 @@ class OatFileManager { void DumpForSigQuit(std::ostream& os); + static void SetCompilerFilter(CompilerFilter::Filter filter) { + filter_ = filter; + } + private: // Check for duplicate class definitions of the given oat file against all open oat files. // Return true if there are any class definition collisions in the oat_file. @@ -122,6 +127,10 @@ class OatFileManager { std::set> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); std::unordered_map oat_file_count_ GUARDED_BY(Locks::oat_file_count_lock_); bool have_non_pic_oat_file_; + + // The compiler filter used for oat files loaded by the oat file manager. + static CompilerFilter::Filter filter_; + DISALLOW_COPY_AND_ASSIGN(OatFileManager); }; diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 60403f975..48c91f605 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -286,6 +286,9 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::Experimental) .Define("-Xforce-nb-testing") .IntoKey(M::ForceNativeBridge) + .Define("-XOatFileManagerCompilerFilter:_") + .WithType() + .IntoKey(M::OatFileManagerCompilerFilter) .Ignore({ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa", "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 941217f24..79af14fc1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -60,6 +60,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "compiler_callbacks.h" +#include "compiler_filter.h" #include "debugger.h" #include "elf_file.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -959,6 +960,16 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental); is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode); + { + CompilerFilter::Filter filter; + std::string filter_str = runtime_options.GetOrDefault(Opt::OatFileManagerCompilerFilter); + if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &filter)) { + LOG(ERROR) << "Cannot parse compiler filter " << filter_str; + return false; + } + OatFileManager::SetCompilerFilter(filter); + } + XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption); heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize), runtime_options.GetOrDefault(Opt::HeapGrowthLimit), diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 3fd9905a6..6a50ffaf8 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -132,5 +132,6 @@ RUNTIME_OPTIONS_KEY (void (*)(int32_t status), \ // We don't call abort(3) by default; see // Runtime::Abort. RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr) +RUNTIME_OPTIONS_KEY (std::string, OatFileManagerCompilerFilter, "speed") #undef RUNTIME_OPTIONS_KEY diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index 82e1fc8ae..0dab4007a 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -54,7 +54,8 @@ class NoPatchoatTest { } const OatFile* oat_file = oat_dex_file->GetOatFile(); - return !oat_file->IsPic() && !oat_file->IsExtractOnly(); + return !oat_file->IsPic() + && CompilerFilter::IsCompilationEnabled(oat_file->GetCompilerFilter()); } }; diff --git a/test/run-test b/test/run-test index 55989c6ed..01464cd6b 100755 --- a/test/run-test +++ b/test/run-test @@ -241,11 +241,11 @@ while true; do run_args="${run_args} --zygote" shift elif [ "x$1" = "x--interpreter" ]; then - run_args="${run_args} --interpreter" + run_args="${run_args} --interpreter --runtime-option -XOatFileManagerCompilerFilter:verify-at-runtime" image_suffix="-interpreter" shift elif [ "x$1" = "x--jit" ]; then - run_args="${run_args} --jit" + run_args="${run_args} --jit --runtime-option -XOatFileManagerCompilerFilter:verify-at-runtime" image_suffix="-jit" shift elif [ "x$1" = "x--optimizing" ]; then @@ -253,10 +253,10 @@ while true; do image_suffix="-optimizing" shift elif [ "x$1" = "x--no-verify" ]; then - run_args="${run_args} --no-verify" + run_args="${run_args} --no-verify --runtime-option -XOatFileManagerCompilerFilter:verify-none" shift elif [ "x$1" = "x--verify-soft-fail" ]; then - run_args="${run_args} --verify-soft-fail" + run_args="${run_args} --verify-soft-fail --runtime-option -XOatFileManagerCompilerFilter:verify-at-runtime" image_suffix="-interp-ac" shift elif [ "x$1" = "x--no-optimize" ]; then -- 2.11.0