From 67f02829d08c2045090a9061c08c15f162fb72f0 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 24 Jun 2016 21:05:23 -0700 Subject: [PATCH] ART: Add very-large threshold to dex2oat Add a variable threshold to dex2oat. If the total dex file size for an app reaches this threshold, dex2oat will punt all compilation and compile the app with verify-at-runtime. This ensures smaller compile time and memory thrashing, while still extracting the dex files and thus helping with dirty memory later. Added tests. Bug: 29557002 Bug: 29790079 Test: m test-art-host-gtest-dex2oat_test Change-Id: I78870e4a80ccaafcbbe56839e61ced0acd2ca05e (cherry picked from commit 338a1d206c16427cf61bd42171fa0c8b9cea8165) --- dex2oat/dex2oat.cc | 32 ++++++++++++ dex2oat/dex2oat_test.cc | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 437aba7ad..c13398023 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -364,6 +365,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Example: --swap-dex-count-threshold=10"); UsageError(" Default: %zu", kDefaultMinDexFilesForSwap); UsageError(""); + UsageError(" --very-large-app-threshold=: specifies the minimum total dex file size in"); + UsageError(" bytes to consider the input \"very large\" and punt on the compilation."); + UsageError(" Example: --very-large-app-threshold=100000000"); + UsageError(""); UsageError(" --app-image-fd=: specify output file descriptor for app image."); UsageError(" Example: --app-image-fd=10"); UsageError(""); @@ -1136,6 +1141,11 @@ class Dex2Oat FINAL { "--swap-dex-count-threshold", &min_dex_files_for_swap_, Usage); + } else if (option.starts_with("--very-large-app-threshold=")) { + ParseUintOption(option, + "--very-large-app-threshold", + &very_large_threshold_, + Usage); } else if (option.starts_with("--app-image-file=")) { app_image_file_name_ = option.substr(strlen("--app-image-file=")).data(); } else if (option.starts_with("--app-image-fd=")) { @@ -1418,6 +1428,19 @@ class Dex2Oat FINAL { } // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that. + // If we need to downgrade the compiler-filter for size reasons, do that check now. + if (!IsBootImage() && IsVeryLarge(dex_files_)) { + if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime, + compiler_options_->GetCompilerFilter())) { + LOG(INFO) << "Very large app, downgrading to verify-at-runtime."; + // Note: this change won't be reflected in the key-value store, as that had to be + // finalized before loading the dex files. This setup is currently required + // to get the size from the DexFile objects. + // TODO: refactor. b/29790079 + compiler_options_->SetCompilerFilter(CompilerFilter::kVerifyAtRuntime); + } + } + if (IsBootImage()) { // For boot image, pass opened dex files to the Runtime::Create(). // Note: Runtime acquires ownership of these dex files. @@ -1913,6 +1936,14 @@ class Dex2Oat FINAL { return dex_files_size >= min_dex_file_cumulative_size_for_swap_; } + bool IsVeryLarge(std::vector& dex_files) { + size_t dex_files_size = 0; + for (const auto* dex_file : dex_files) { + dex_files_size += dex_file->GetHeader().file_size_; + } + return dex_files_size >= very_large_threshold_; + } + template static std::vector MakeNonOwningPointerVector(const std::vector>& src) { std::vector result; @@ -2504,6 +2535,7 @@ class Dex2Oat FINAL { int swap_fd_; size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap; size_t min_dex_file_cumulative_size_for_swap_ = kDefaultMinDexFileCumulativeSizeForSwap; + size_t very_large_threshold_ = std::numeric_limits::max(); std::string app_image_file_name_; int app_image_fd_; std::string profile_file_; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index de3aed9d0..618888335 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -24,6 +24,8 @@ #include "base/macros.h" #include "base/stringprintf.h" #include "dex2oat_environment_test.h" +#include "oat.h" +#include "oat_file.h" #include "utils.h" #include @@ -284,4 +286,134 @@ TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) { { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); } +class Dex2oatVeryLargeTest : public Dex2oatTest { + protected: + void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED, + CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE { + // Ignore, we'll do our own checks. + } + + void RunTest(CompilerFilter::Filter filter, + bool expect_large, + const std::vector& extra_args = {}) { + std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; + + Copy(GetDexSrc1(), dex_location); + + std::vector copy(extra_args); + + GenerateOdexForTest(dex_location, odex_location, filter, copy); + + CheckValidity(); + ASSERT_TRUE(success_); + CheckResult(dex_location, odex_location, filter, expect_large); + } + + void CheckResult(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter, + bool expect_large) { + // Host/target independent checks. + std::string error_msg; + 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; + if (expect_large) { + // Note: we cannot check the following: + // EXPECT_TRUE(CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime, + // odex_file->GetCompilerFilter())); + // The reason is that the filter override currently happens when the dex files are + // loaded in dex2oat, which is after the oat file has been started. Thus, the header + // store cannot be changed, and the original filter is set in stone. + + for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) { + std::unique_ptr dex_file = oat_dex_file->OpenDexFile(&error_msg); + ASSERT_TRUE(dex_file != nullptr); + uint32_t class_def_count = dex_file->NumClassDefs(); + ASSERT_LT(class_def_count, std::numeric_limits::max()); + for (uint16_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { + OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); + EXPECT_EQ(oat_class.GetType(), OatClassType::kOatClassNoneCompiled); + } + } + + // If the input filter was "below," it should have been used. + if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime, filter)) { + EXPECT_EQ(odex_file->GetCompilerFilter(), filter); + } + } else { + EXPECT_EQ(odex_file->GetCompilerFilter(), filter); + } + + // Host/target dependent checks. + if (kIsTargetBuild) { + CheckTargetResult(expect_large); + } else { + CheckHostResult(expect_large); + } + } + + void CheckTargetResult(bool expect_large ATTRIBUTE_UNUSED) { + // TODO: Ignore for now. May do something for fd things. + } + + void CheckHostResult(bool expect_large) { + if (!kIsTargetBuild) { + if (expect_large) { + EXPECT_NE(output_.find("Very large app, downgrading to verify-at-runtime."), + std::string::npos) + << output_; + } else { + EXPECT_EQ(output_.find("Very large app, downgrading to verify-at-runtime."), + std::string::npos) + << output_; + } + } + } + + // Check whether the dex2oat run was really successful. + void CheckValidity() { + if (kIsTargetBuild) { + CheckTargetValidity(); + } else { + CheckHostValidity(); + } + } + + void CheckTargetValidity() { + // TODO: Ignore for now. + } + + // On the host, we can get the dex2oat output. Here, look for "dex2oat took." + void CheckHostValidity() { + EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_; + } +}; + +TEST_F(Dex2oatVeryLargeTest, DontUseVeryLarge) { + RunTest(CompilerFilter::kVerifyNone, false); + RunTest(CompilerFilter::kVerifyAtRuntime, false); + RunTest(CompilerFilter::kInterpretOnly, false); + RunTest(CompilerFilter::kSpeed, false); + + RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=1000000" }); + RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=1000000" }); + RunTest(CompilerFilter::kInterpretOnly, false, { "--very-large-app-threshold=1000000" }); + RunTest(CompilerFilter::kSpeed, false, { "--very-large-app-threshold=1000000" }); +} + +TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) { + RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=100" }); + RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=100" }); + RunTest(CompilerFilter::kInterpretOnly, true, { "--very-large-app-threshold=100" }); + RunTest(CompilerFilter::kSpeed, true, { "--very-large-app-threshold=100" }); +} + } // namespace art -- 2.11.0