diff options
author | 2022-05-26 17:24:17 +0100 | |
---|---|---|
committer | 2022-05-30 19:46:41 +0100 | |
commit | 78d86b5810d1e39b078a1b18d42c3ed556328a64 (patch) | |
tree | 659468b259f0c6f132b7095ad23557b1f8f35e7a | |
parent | a79a6eba31fdcecccf939e412f64507102bb4f22 (diff) |
Refactor OatFileAssistant to allow it to function without a runtime.
After this change, OatFileAssistant accepts an optional input
OatFileAssistant::RuntimeOptions, and when the input is provided,
OatFileAssistant can function without a runtime.
Also:
- Update tests to verify that OatFileAssistant works properly with and
without a runtime.
Bug: 233383589
Test: atest art_standalone_runtime_tests:OatFileAssistantTest
Change-Id: Ie828a37f5f3c0843cf33cc40b07420eba0ee6dd0
-rw-r--r-- | dexoptanalyzer/dexoptanalyzer.cc | 2 | ||||
-rw-r--r-- | runtime/Android.bp | 2 | ||||
-rw-r--r-- | runtime/art_standalone_runtime_tests.xml | 2 | ||||
-rw-r--r-- | runtime/dexopt_test.cc | 1 | ||||
-rw-r--r-- | runtime/gc/space/image_space.cc | 86 | ||||
-rw-r--r-- | runtime/gc/space/image_space.h | 32 | ||||
-rw-r--r-- | runtime/gc/space/image_space_test.cc | 1 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 167 | ||||
-rw-r--r-- | runtime/oat_file_assistant.h | 58 | ||||
-rw-r--r-- | runtime/oat_file_assistant_test.cc | 552 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 38 | ||||
-rw-r--r-- | runtime/runtime.cc | 19 | ||||
-rw-r--r-- | runtime/runtime.h | 14 | ||||
-rw-r--r-- | test/art-gtests-target-chroot.xml | 2 | ||||
-rw-r--r-- | test/art-gtests-target-install-apex.xml | 2 |
15 files changed, 636 insertions, 342 deletions
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 0cc2cdb03a..7d0aff0cbb 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -324,6 +324,7 @@ class DexoptAnalyzer final { class_loader_context.get(), /*load_executable=*/ false, /*only_load_trusted_executable=*/ false, + /*runtime_options=*/ nullptr, vdex_fd_, oat_fd_, zip_fd_); @@ -395,6 +396,7 @@ class DexoptAnalyzer final { ArrayRef<const std::string>(bcp), ArrayRef<const int>(bcp_fds), runtime->GetInstructionSet(), + runtime->GetApexVersions(), &error_msg)) { LOG(INFO) << "Failed to verify boot class path checksums: " << error_msg; return ReturnCode::kDex2OatFromScratch; diff --git a/runtime/Android.bp b/runtime/Android.bp index 98ddbb7210..d11f8fa009 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -876,6 +876,7 @@ art_cc_test { "art_standalone_gtest_defaults", "art_runtime_tests_defaults", ], + data: [":generate-boot-image"], target: { host: { required: ["dex2oat"], @@ -929,6 +930,7 @@ art_cc_test { "art_standalone_gtest_defaults", "art_runtime_compiler_tests_defaults", ], + data: [":generate-boot-image"], shared_libs: [ "libart-compiler", ], diff --git a/runtime/art_standalone_runtime_tests.xml b/runtime/art_standalone_runtime_tests.xml index 520052962e..cd6a33bd64 100644 --- a/runtime/art_standalone_runtime_tests.xml +++ b/runtime/art_standalone_runtime_tests.xml @@ -93,7 +93,7 @@ <option name="exclude-filter" value="HiddenApiTest.DexDomain_SystemSystemExtFrameworkDir" /> <option name="exclude-filter" value="HiddenApiTest.DexDomain_SystemSystemExtFrameworkDir_MultiDex" /> <option name="exclude-filter" value="JniInternalTest.CallVarArgMethodBadPrimitive" /> - <option name="exclude-filter" value="OatFileAssistantTest.SystemFrameworkDir" /> + <option name="exclude-filter" value="OatFileAssistantBaseTest.SystemFrameworkDir" /> <option name="exclude-filter" value="StubTest.Fields16" /> <option name="exclude-filter" value="StubTest.Fields32" /> <option name="exclude-filter" value="StubTest.Fields64" /> diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index eb5b149039..0c02eadd8e 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -178,6 +178,7 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, ArrayRef<const std::string>(Runtime::Current()->GetBootClassPath()), ArrayRef<const int>(Runtime::Current()->GetBootClassPathFds()), kRuntimeISA, + Runtime::Current()->GetApexVersions(), &error_msg); ASSERT_EQ(!with_alternate_image, match) << error_msg; } diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 6afd63e4a5..b1ab960173 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -23,7 +23,9 @@ #include <memory> #include <random> #include <string> +#include <vector> +#include "android-base/logging.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" #include "android-base/unique_fd.h" @@ -1424,14 +1426,16 @@ class ImageSpace::BootImageLayout { ArrayRef<const int> boot_class_path_fds, ArrayRef<const int> boot_class_path_image_fds, ArrayRef<const int> boot_class_path_vdex_fds, - ArrayRef<const int> boot_class_path_oat_fds) - : image_locations_(image_locations), - boot_class_path_(boot_class_path), - boot_class_path_locations_(boot_class_path_locations), - boot_class_path_fds_(boot_class_path_fds), - boot_class_path_image_fds_(boot_class_path_image_fds), - boot_class_path_vdex_fds_(boot_class_path_vdex_fds), - boot_class_path_oat_fds_(boot_class_path_oat_fds) {} + ArrayRef<const int> boot_class_path_oat_fds, + const std::string* apex_versions = nullptr) + : image_locations_(image_locations), + boot_class_path_(boot_class_path), + boot_class_path_locations_(boot_class_path_locations), + boot_class_path_fds_(boot_class_path_fds), + boot_class_path_image_fds_(boot_class_path_image_fds), + boot_class_path_vdex_fds_(boot_class_path_vdex_fds), + boot_class_path_oat_fds_(boot_class_path_oat_fds), + apex_versions_(GetApexVersions(apex_versions)) {} std::string GetPrimaryImageLocation(); @@ -1550,6 +1554,17 @@ class ImageSpace::BootImageLayout { /*inout*/std::string_view* oat_checksums, /*out*/std::string* error_msg); + // This function prefers taking APEX versions from the input instead of from the runtime if + // possible. If the input is present, `ValidateFromSystem` can work without an active runtime. + static const std::string& GetApexVersions(const std::string* apex_versions) { + if (apex_versions == nullptr) { + DCHECK(Runtime::Current() != nullptr); + return Runtime::Current()->GetApexVersions(); + } else { + return *apex_versions; + } + } + ArrayRef<const std::string> image_locations_; ArrayRef<const std::string> boot_class_path_; ArrayRef<const std::string> boot_class_path_locations_; @@ -1563,6 +1578,7 @@ class ImageSpace::BootImageLayout { size_t next_bcp_index_ = 0u; size_t total_component_count_ = 0u; size_t total_reservation_size_ = 0u; + const std::string& apex_versions_; }; std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() { @@ -1886,7 +1902,7 @@ bool ImageSpace::BootImageLayout::ValidateOatFile( error_msg->c_str()); return false; } - if (!ImageSpace::ValidateOatFile(*oat_file, error_msg, dex_filenames, dex_fds)) { + if (!ImageSpace::ValidateOatFile(*oat_file, error_msg, dex_filenames, dex_fds, apex_versions_)) { return false; } return true; @@ -3519,7 +3535,9 @@ void ImageSpace::Dump(std::ostream& os) const { << ",name=\"" << GetName() << "\"]"; } -bool ImageSpace::ValidateApexVersions(const OatFile& oat_file, std::string* error_msg) { +bool ImageSpace::ValidateApexVersions(const OatFile& oat_file, + const std::string& apex_versions, + std::string* error_msg) { // For a boot image, the key value store only exists in the first OAT file. Skip other OAT files. if (oat_file.GetOatHeader().GetKeyValueStoreSize() == 0) { return true; @@ -3542,27 +3560,33 @@ bool ImageSpace::ValidateApexVersions(const OatFile& oat_file, std::string* erro // For a boot image, it can be generated from a subset of the bootclasspath. // For an app image, some dex files get compiled with a subset of the bootclasspath. // For such cases, the OAT APEX versions will be a prefix of the runtime APEX versions. - if (!android::base::StartsWith(Runtime::Current()->GetApexVersions(), oat_apex_versions)) { + if (!android::base::StartsWith(apex_versions, oat_apex_versions)) { *error_msg = StringPrintf( "ValidateApexVersions found APEX versions mismatch between oat file '%s' and the runtime " "(Oat file: '%s', Runtime: '%s')", oat_file.GetLocation().c_str(), oat_apex_versions, - Runtime::Current()->GetApexVersions().c_str()); + apex_versions.c_str()); return false; } return true; } bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) { - return ValidateOatFile(oat_file, error_msg, ArrayRef<const std::string>(), ArrayRef<const int>()); + DCHECK(Runtime::Current() != nullptr); + return ValidateOatFile(oat_file, + error_msg, + ArrayRef<const std::string>(), + ArrayRef<const int>(), + Runtime::Current()->GetApexVersions()); } bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg, ArrayRef<const std::string> dex_filenames, - ArrayRef<const int> dex_fds) { - if (!ValidateApexVersions(oat_file, error_msg)) { + ArrayRef<const int> dex_fds, + const std::string& apex_versions) { + if (!ValidateApexVersions(oat_file, apex_versions, error_msg)) { return false; } @@ -3734,6 +3758,7 @@ bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums, ArrayRef<const std::string> boot_class_path, ArrayRef<const int> boot_class_path_fds, InstructionSet image_isa, + const std::string& apex_versions, /*out*/std::string* error_msg) { if (oat_checksums.empty() || oat_boot_class_path.empty()) { *error_msg = oat_checksums.empty() ? "Empty checksums." : "Empty boot class path."; @@ -3761,7 +3786,8 @@ bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums, bcp_fds, /*boot_class_path_image_fds=*/ ArrayRef<const int>(), /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(), - /*boot_class_path_oat_fds=*/ ArrayRef<const int>()); + /*boot_class_path_oat_fds=*/ ArrayRef<const int>(), + &apex_versions); std::string primary_image_location = layout.GetPrimaryImageLocation(); std::string system_filename; bool has_system = false; @@ -3831,6 +3857,34 @@ bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums, return true; } +bool ImageSpace::VerifyBootImages(ArrayRef<const std::string> image_locations, + ArrayRef<const std::string> boot_class_path_locations, + ArrayRef<const std::string> boot_class_path, + ArrayRef<const int> boot_class_path_fds, + InstructionSet image_isa, + const std::string& apex_versions, + /*out*/std::string* error_msg) { + // Remove profiles to prevent compilation. We only need to verify boot images. + std::vector<std::string> image_location_without_profiles; + for (std::string location : image_locations) { + size_t profile_separator_pos = location.find(kProfileSeparator); + if (profile_separator_pos != std::string::npos) { + location.resize(profile_separator_pos); + } + image_location_without_profiles.push_back(std::move(location)); + } + + BootImageLayout layout(ArrayRef<const std::string>(image_location_without_profiles), + boot_class_path, + boot_class_path_locations, + boot_class_path_fds, + /*boot_class_path_image_fds=*/ ArrayRef<const int>(), + /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(), + /*boot_class_path_oat_fds=*/ ArrayRef<const int>(), + &apex_versions); + return layout.LoadFromSystem(image_isa, error_msg); +} + bool ImageSpace::VerifyBootClassPathChecksums( std::string_view oat_checksums, std::string_view oat_boot_class_path, diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 8a93f2bad1..f1adbec60a 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -249,8 +249,18 @@ class ImageSpace : public MemMapSpace { ArrayRef<const std::string> boot_class_path, ArrayRef<const int> boot_class_path_fds, InstructionSet image_isa, + const std::string& apex_versions, /*out*/std::string* error_msg); + // Returns whether all the boot images listed in `image_locations` are valid. + static bool VerifyBootImages(ArrayRef<const std::string> image_locations, + ArrayRef<const std::string> boot_class_path_locations, + ArrayRef<const std::string> boot_class_path, + ArrayRef<const int> boot_class_path_fds, + InstructionSet image_isa, + const std::string& apex_versions, + /*out*/std::string* error_msg); + // Returns whether the oat checksums and boot class path description are valid // for the given boot image spaces and boot class path. Used for boot image extensions. static bool VerifyBootClassPathChecksums( @@ -267,8 +277,10 @@ class ImageSpace : public MemMapSpace { const std::string& image_location, bool boot_image_extension = false); - // Returns true if the APEX versions in the OAT file match the current APEX versions. - static bool ValidateApexVersions(const OatFile& oat_file, std::string* error_msg); + // Returns true if the APEX versions in the OAT file match the given APEX versions. + static bool ValidateApexVersions(const OatFile& oat_file, + const std::string& apex_versions, + std::string* error_msg); // Returns true if the dex checksums in the given oat file match the // checksums of the original dex files on disk. This is intended to be used @@ -279,17 +291,23 @@ class ImageSpace : public MemMapSpace { // oat and odex file. // // This function is exposed for testing purposes. + // + // Calling this function requires an active runtime. static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg); // Same as above, but allows to use `dex_filenames` and `dex_fds` to find the dex files instead of - // using the dex filenames in the header of the oat file. This overload is useful when the actual - // dex filenames are different from what's in the header (e.g., when we run dex2oat on host), or - // when the runtime can only access files through FDs (e.g., when we run dex2oat on target in a - // restricted SELinux domain). + // using the dex filenames in the header of the oat file, and also takes `apex_versions` from the + // input. This overload is useful when the actual dex filenames are different from what's in the + // header (e.g., when we run dex2oat on host), when the runtime can only access files through FDs + // (e.g., when we run dex2oat on target in a restricted SELinux domain), or when there is no + // active runtime. + // + // Calling this function does not require an active runtime. static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg, ArrayRef<const std::string> dex_filenames, - ArrayRef<const int> dex_fds); + ArrayRef<const int> dex_fds, + const std::string& apex_versions); // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields. uint8_t* GetImageEnd() const { diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index 3a6d0e12e2..fc85f15a11 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -343,6 +343,7 @@ TEST_F(DexoptTest, Checksums) { ArrayRef<const std::string>(bcp), /*boot_class_path_fds=*/ ArrayRef<const int>(), kRuntimeISA, + runtime->GetApexVersions(), &error_msg); }; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 914d2dd08b..81aaad580a 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -16,15 +16,16 @@ #include "oat_file_assistant.h" -#include <sstream> - #include <sys/stat.h> -#include "zlib.h" + +#include <memory> +#include <sstream> #include "android-base/file.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" - +#include "arch/instruction_set.h" +#include "base/array_ref.h" #include "base/compiler_filter.h" #include "base/file_utils.h" #include "base/logging.h" // For VLOG. @@ -47,6 +48,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" +#include "zlib.h" namespace art { @@ -82,22 +84,24 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, const InstructionSet isa, ClassLoaderContext* context, bool load_executable, - bool only_load_trusted_executable) + bool only_load_trusted_executable, + std::unique_ptr<RuntimeOptions> runtime_options) : OatFileAssistant(dex_location, isa, context, load_executable, only_load_trusted_executable, + std::move(runtime_options), /*vdex_fd=*/ -1, /*oat_fd=*/ -1, /*zip_fd=*/ -1) {} - OatFileAssistant::OatFileAssistant(const char* dex_location, const InstructionSet isa, ClassLoaderContext* context, bool load_executable, bool only_load_trusted_executable, + std::unique_ptr<RuntimeOptions> runtime_options, int vdex_fd, int oat_fd, int zip_fd) @@ -105,6 +109,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, isa_(isa), load_executable_(load_executable), only_load_trusted_executable_(only_load_trusted_executable), + runtime_options_(std::move(runtime_options)), odex_(this, /*is_oat_location=*/ false), oat_(this, /*is_oat_location=*/ true), vdex_for_odex_(this, /*is_oat_location=*/ false), @@ -127,12 +132,47 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, dex_location_.assign(dex_location); + Runtime* runtime = Runtime::Current(); + + if (load_executable_ && runtime == nullptr) { + LOG(WARNING) << "OatFileAssistant: Load executable specified, " + << "but no active runtime is found. Will not attempt to load executable."; + load_executable_ = false; + } + if (load_executable_ && isa != kRuntimeISA) { LOG(WARNING) << "OatFileAssistant: Load executable specified, " << "but isa is not kRuntimeISA. Will not attempt to load executable."; load_executable_ = false; } + if (runtime_options_ == nullptr) { + CHECK(runtime != nullptr) << "runtime_options is not provided, and no active runtime is found."; + + runtime_options_ = std::make_unique<RuntimeOptions>(RuntimeOptions{ + .image_locations = runtime->GetImageLocations(), + .boot_class_path = runtime->GetBootClassPath(), + .boot_class_path_locations = runtime->GetBootClassPathLocations(), + .boot_class_path_fds = &runtime->GetBootClassPathFds(), + .use_jit_zygote = runtime->HasImageWithProfile(), + .deny_art_apex_data_files = runtime->DenyArtApexDataFiles(), + .apex_versions = runtime->GetApexVersions(), + }); + + if (isa == kRuntimeISA) { + // For the fast path that checks the OAT files against the runtime boot classpath checksums + // and boot classpath locations. + cached_boot_class_path_ = android::base::Join(runtime->GetBootClassPathLocations(), ":"); + cached_boot_class_path_checksums_ = runtime->GetBootClassPathChecksums(); + } + } + + if (runtime == nullptr) { + // We need `MemMap` for mapping files. We don't have to initialize it when there is a runtime + // because the runtime initializes it. + MemMap::Init(); + } + // Get the odex filename. std::string error_msg; std::string odex_file_name; @@ -159,7 +199,11 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, if (!UseFdToReadFiles()) { // Get the oat filename. std::string oat_file_name; - if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) { + if (DexLocationToOatFilename(dex_location_, + isa_, + runtime_options_->deny_art_apex_data_files, + &oat_file_name, + &error_msg)) { oat_.Reset(oat_file_name, /*use_fd=*/ false); std::string vdex_file_name = GetVdexFilename(oat_file_name); vdex_for_oat_.Reset(vdex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd); @@ -199,11 +243,8 @@ bool OatFileAssistant::IsInBootClassPath() { // specified by the user. This is okay, because the boot class path should // be the same for all ISAs. // TODO: Can we verify the boot class path is the same for all ISAs? - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - const auto& boot_class_path = class_linker->GetBootClassPath(); - for (size_t i = 0; i < boot_class_path.size(); i++) { - if (boot_class_path[i]->GetLocation() == dex_location_) { + for (const std::string& boot_class_path_location : runtime_options_->boot_class_path_locations) { + if (boot_class_path_location == dex_location_) { VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path"; return true; } @@ -443,7 +484,8 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& VLOG(oat) << "Oat image checksum does not match image checksum."; return kOatBootImageOutOfDate; } - if (!gc::space::ImageSpace::ValidateApexVersions(file, &error_msg)) { + if (!gc::space::ImageSpace::ValidateApexVersions( + file, runtime_options_->apex_versions, &error_msg)) { VLOG(oat) << error_msg; return kOatBootImageOutOfDate; } @@ -454,9 +496,8 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums. DCHECK(required_dex_checksums_attempted_); if (only_load_trusted_executable_ && - !LocationIsTrusted(file.GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) && - file.ContainsDexCode() && - zip_file_only_contains_uncompressed_dex_) { + !LocationIsTrusted(file.GetLocation(), !runtime_options_->deny_art_apex_data_files) && + file.ContainsDexCode() && zip_file_only_contains_uncompressed_dex_) { LOG(ERROR) << "Not loading " << dex_location_ << ": oat file has dex code, but APK has uncompressed dex code"; @@ -474,6 +515,11 @@ bool OatFileAssistant::AnonymousDexVdexLocation(const std::vector<const DexFile: InstructionSet isa, /* out */ std::string* dex_location, /* out */ std::string* vdex_filename) { + // Normally, OatFileAssistant should not assume that there is an active runtime. However, we + // reference the runtime here. This is okay because we are in a static function that is unrelated + // to other parts of OatFileAssistant. + DCHECK(Runtime::Current() != nullptr); + uint32_t checksum = adler32(0L, Z_NULL, 0); for (const DexFile::Header* header : headers) { checksum = adler32_combine(checksum, @@ -571,13 +617,23 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, InstructionSet isa, std::string* oat_filename, std::string* error_msg) { + DCHECK(Runtime::Current() != nullptr); + return DexLocationToOatFilename( + location, isa, Runtime::Current()->DenyArtApexDataFiles(), oat_filename, error_msg); +} + +bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, + InstructionSet isa, + bool deny_art_apex_data_files, + std::string* oat_filename, + std::string* error_msg) { CHECK(oat_filename != nullptr); CHECK(error_msg != nullptr); // Check if `location` could have an oat file in the ART APEX data directory. If so, and the // file exists, use it. const std::string apex_data_file = GetApexDataOdexFilename(location, isa); - if (!apex_data_file.empty() && !Runtime::Current()->DenyArtApexDataFiles()) { + if (!apex_data_file.empty() && !deny_art_apex_data_files) { if (OS::FileExists(apex_data_file.c_str(), /*check_file_type=*/true)) { *oat_filename = apex_data_file; return true; @@ -656,26 +712,19 @@ bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) { return true; } - Runtime* runtime = Runtime::Current(); std::string error_msg; - bool result = false; - // Fast path when the runtime boot classpath cheksums and boot classpath - // locations directly match. - if (oat_boot_class_path_checksums_view == runtime->GetBootClassPathChecksums() && - isa_ == kRuntimeISA && - oat_boot_class_path_view == android::base::Join(runtime->GetBootClassPathLocations(), ":")) { - result = true; - } else { - result = gc::space::ImageSpace::VerifyBootClassPathChecksums( - oat_boot_class_path_checksums_view, - oat_boot_class_path_view, - ArrayRef<const std::string>(runtime->GetImageLocations()), - ArrayRef<const std::string>(runtime->GetBootClassPathLocations()), - ArrayRef<const std::string>(runtime->GetBootClassPath()), - ArrayRef<const int>(runtime->GetBootClassPathFds()), - isa_, - &error_msg); - } + bool result = gc::space::ImageSpace::VerifyBootClassPathChecksums( + oat_boot_class_path_checksums_view, + oat_boot_class_path_view, + ArrayRef<const std::string>(runtime_options_->image_locations), + ArrayRef<const std::string>(runtime_options_->boot_class_path_locations), + ArrayRef<const std::string>(runtime_options_->boot_class_path), + runtime_options_->boot_class_path_fds != nullptr ? + ArrayRef<const int>(*runtime_options_->boot_class_path_fds) : + ArrayRef<const int>(), + isa_, + runtime_options_->apex_versions, + &error_msg); if (!result) { VLOG(oat) << "Failed to verify checksums of oat file " << oat_file.GetLocation() << " error: " << error_msg; @@ -688,6 +737,31 @@ bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) { return true; } +bool OatFileAssistant::IsPrimaryBootImageUsable() { + if (cached_is_boot_image_usable_.has_value()) { + return cached_is_boot_image_usable_.value(); + } + + if (runtime_options_->image_locations.empty() || runtime_options_->use_jit_zygote) { + return false; + } + + std::string ignored_error_msg; + // Only verify the primary boot image. + cached_is_boot_image_usable_ = gc::space::ImageSpace::VerifyBootImages( + ArrayRef<const std::string>(runtime_options_->image_locations) + .SubArray(/*pos=*/0u, /*size=*/1u), + ArrayRef<const std::string>(runtime_options_->boot_class_path_locations), + ArrayRef<const std::string>(runtime_options_->boot_class_path), + runtime_options_->boot_class_path_fds != nullptr ? + ArrayRef<const int>(*runtime_options_->boot_class_path_fds) : + ArrayRef<const int>(), + isa_, + runtime_options_->apex_versions, + &ignored_error_msg); + return cached_is_boot_image_usable_.value(); +} + OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() { ScopedTrace trace("GetBestInfo"); // TODO(calin): Document the side effects of class loading when @@ -840,7 +914,8 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() { return nullptr; } - if (LocationIsOnArtApexData(filename_) && Runtime::Current()->DenyArtApexDataFiles()) { + if (LocationIsOnArtApexData(filename_) && + oat_file_assistant_->runtime_options_->deny_art_apex_data_files) { LOG(WARNING) << "OatFileAssistant rejected file " << filename_ << ": ART apexdata is untrusted."; return nullptr; @@ -961,7 +1036,7 @@ bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay( file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey); if (oat_boot_class_path_checksums != nullptr && !StartsWith(oat_boot_class_path_checksums, "i") && - !Runtime::Current()->HasImageWithProfile()) { + oat_file_assistant_->IsPrimaryBootImageUsable()) { DCHECK(!file->GetOatHeader().RequiresImage()); return false; } @@ -1044,17 +1119,19 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { // TODO(calin): we could provide a more refined status here // (e.g. run from uncompressed apk, run with vdex but not oat etc). It will allow us to // track more experiments but adds extra complexity. -void OatFileAssistant::GetOptimizationStatus( - const std::string& filename, - InstructionSet isa, - std::string* out_compilation_filter, - std::string* out_compilation_reason) { +void OatFileAssistant::GetOptimizationStatus(const std::string& filename, + InstructionSet isa, + std::string* out_compilation_filter, + std::string* out_compilation_reason, + std::unique_ptr<RuntimeOptions> runtime_options) { // It may not be possible to load an oat file executable (e.g., selinux restrictions). Load // non-executable and check the status manually. OatFileAssistant oat_file_assistant(filename.c_str(), isa, - /* context= */ nullptr, - /*load_executable=*/ false); + /*context=*/nullptr, + /*load_executable=*/false, + /*only_load_trusted_executable=*/false, + std::move(runtime_options)); std::string out_odex_location; // unused std::string out_odex_status; // unused oat_file_assistant.GetOptimizationStatus( diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index c243cc3a54..6a6b3c08f1 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -19,11 +19,12 @@ #include <cstdint> #include <memory> +#include <optional> #include <sstream> #include <string> -#include "base/compiler_filter.h" #include "arch/instruction_set.h" +#include "base/compiler_filter.h" #include "base/os.h" #include "base/scoped_flock.h" #include "base/unix_file/fd_file.h" @@ -89,6 +90,28 @@ class OatFileAssistant { kOatUpToDate, }; + // Options that a runtime would take if the OAT file were going to be loaded by the runtime. + // Note that the struct only keeps references, so the caller must keep the objects alive during + // the lifetime of OatFileAssistant. + struct RuntimeOptions { + // Required. See `-Ximage`. + const std::vector<std::string>& image_locations; + // Required. See `-Xbootclasspath`. + const std::vector<std::string>& boot_class_path; + // Required. See `-Xbootclasspath-locations`. + const std::vector<std::string>& boot_class_path_locations; + // Optional. See `-Xbootclasspathfds`. + const std::vector<int>* const boot_class_path_fds = nullptr; + // Optional. See `-Xforcejitzygote`. + const bool use_jit_zygote = false; + // Optional. See `-Xdeny-art-apex-data-files`. + const bool deny_art_apex_data_files = false; + // Required. A string that represents the apex versions of boot classpath jars. See + // `Runtime::apex_versions_` for the encoding format. Can be obtained from + // `Runtime::GetApexVersions(boot_class_path_locations)`. + const std::string& apex_versions; + }; + // Constructs an OatFileAssistant object to assist the oat file // corresponding to the given dex location with the target instruction set. // @@ -110,11 +133,15 @@ class OatFileAssistant { // only_load_trusted_executable should be true if the caller intends to have // only oat files from trusted locations loaded executable. See IsTrustedLocation() for // details on trusted locations. + // + // runtime_options should be provided with all the required fields filled if the caller intends to + // use OatFileAssistant without a runtime. OatFileAssistant(const char* dex_location, const InstructionSet isa, ClassLoaderContext* context, bool load_executable, - bool only_load_trusted_executable = false); + bool only_load_trusted_executable = false, + std::unique_ptr<RuntimeOptions> runtime_options = nullptr); // Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is // provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively. @@ -124,6 +151,7 @@ class OatFileAssistant { ClassLoaderContext* context, bool load_executable, bool only_load_trusted_executable, + std::unique_ptr<RuntimeOptions> runtime_options, int vdex_fd, int oat_fd, int zip_fd); @@ -189,7 +217,8 @@ class OatFileAssistant { static void GetOptimizationStatus(const std::string& filename, InstructionSet isa, std::string* out_compilation_filter, - std::string* out_compilation_reason); + std::string* out_compilation_reason, + std::unique_ptr<RuntimeOptions> runtime_options = nullptr); // Open and returns an image space associated with the oat file. static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file); @@ -253,8 +282,19 @@ class OatFileAssistant { // Returns false on error, in which case error_msg describes the error and // oat_filename is not changed. // Neither oat_filename nor error_msg may be null. + // + // Calling this function requires an active runtime. + static bool DexLocationToOatFilename(const std::string& location, + InstructionSet isa, + std::string* oat_filename, + std::string* error_msg); + + // Same as above, but also takes `deny_art_apex_data_files` from input. + // + // Calling this function does not require an active runtime. static bool DexLocationToOatFilename(const std::string& location, InstructionSet isa, + bool deny_art_apex_data_files, std::string* oat_filename, std::string* error_msg); @@ -262,6 +302,8 @@ class OatFileAssistant { // is known, creates an absolute path in that directory and tries to infer path // of a corresponding vdex file. Otherwise only creates a basename dex_location // from the combined checksums. Returns true if all out-arguments have been set. + // + // Calling this function requires an active runtime. static bool AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& dex_headers, InstructionSet isa, /* out */ std::string* dex_location, @@ -412,6 +454,9 @@ class OatFileAssistant { // Validates the boot class path checksum of an OatFile. bool ValidateBootClassPathChecksums(const OatFile& oat_file); + // Returns whether there is at least one boot image usable. + bool IsPrimaryBootImageUsable(); + std::string dex_location_; ClassLoaderContext* context_; @@ -432,6 +477,12 @@ class OatFileAssistant { // Will be set during GetRequiredDexChecksums. bool zip_file_only_contains_uncompressed_dex_ = true; + // The runtime options taken from the active runtime or the input. + // + // All member functions should get runtime options from this variable rather than referencing the + // active runtime. This is to allow OatFileAssistant to function without an active runtime. + std::unique_ptr<RuntimeOptions> runtime_options_; + // Cached value of the required dex checksums. // This should be accessed only by the GetRequiredDexChecksums() method. std::vector<uint32_t> cached_required_dex_checksums_; @@ -461,6 +512,7 @@ class OatFileAssistant { std::string cached_boot_class_path_; std::string cached_boot_class_path_checksums_; + std::optional<bool> cached_is_boot_image_usable_ = std::nullopt; friend class OatFileAssistantTest; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 07998dd59f..15182b4a36 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -16,16 +16,17 @@ #include "oat_file_assistant.h" +#include <fcntl.h> +#include <gtest/gtest.h> #include <sys/param.h> +#include <functional> +#include <memory> #include <string> #include <vector> -#include <fcntl.h> - -#include <gtest/gtest.h> +#include "android-base/scopeguard.h" #include "android-base/strings.h" - #include "art_field-inl.h" #include "base/os.h" #include "base/utils.h" @@ -42,8 +43,16 @@ namespace art { -class OatFileAssistantTest : public DexoptTest { +class OatFileAssistantBaseTest : public DexoptTest {}; + +class OatFileAssistantTest : public OatFileAssistantBaseTest, + public testing::WithParamInterface<bool> { public: + void SetUp() override { + DexoptTest::SetUp(); + with_runtime_ = GetParam(); + } + void VerifyOptimizationStatus(OatFileAssistant* assistant, const std::string& file, const std::string& expected_filter, @@ -54,7 +63,7 @@ class OatFileAssistantTest : public DexoptTest { std::string compilation_reason1; OatFileAssistant::GetOptimizationStatus( - file, kRuntimeISA, &compilation_filter1, &compilation_reason1); + file, kRuntimeISA, &compilation_filter1, &compilation_reason1, MaybeCreateRuntimeOptions()); ASSERT_EQ(expected_filter, compilation_filter1); ASSERT_EQ(expected_reason, compilation_reason1); @@ -115,7 +124,53 @@ class OatFileAssistantTest : public DexoptTest { return context; } + // Temporarily disables the pointer to the current runtime if `with_runtime_` is false. + // Essentially simulates an environment where there is no active runtime. + android::base::ScopeGuard<std::function<void()>> ScopedMaybeWithoutRuntime() { + if (!with_runtime_) { + Runtime::TestOnlySetCurrent(nullptr); + } + return android::base::make_scope_guard( + [this]() { Runtime::TestOnlySetCurrent(runtime_.get()); }); + } + + // Returns runtime options if `with_runtime_` is false. + std::unique_ptr<OatFileAssistant::RuntimeOptions> MaybeCreateRuntimeOptions() { + if (with_runtime_) { + return nullptr; + } else { + return std::make_unique<OatFileAssistant::RuntimeOptions>(OatFileAssistant::RuntimeOptions{ + .image_locations = runtime_->GetImageLocations(), + .boot_class_path = runtime_->GetBootClassPath(), + .boot_class_path_locations = runtime_->GetBootClassPathLocations(), + .boot_class_path_fds = &runtime_->GetBootClassPathFds(), + .use_jit_zygote = runtime_->HasImageWithProfile(), + .deny_art_apex_data_files = runtime_->DenyArtApexDataFiles(), + .apex_versions = runtime_->GetApexVersions(), + }); + } + } + + // A helper function to create OatFileAssistant with some default arguments. + OatFileAssistant CreateOatFileAssistant(const char* dex_location, + ClassLoaderContext* context = nullptr, + bool load_executable = false, + int vdex_fd = -1, + int oat_fd = -1, + int zip_fd = -1) { + return OatFileAssistant(dex_location, + kRuntimeISA, + context != nullptr ? context : default_context_.get(), + load_executable, + /*only_load_trusted_executable=*/false, + MaybeCreateRuntimeOptions(), + vdex_fd, + oat_fd, + zip_fd); + } + std::unique_ptr<ClassLoaderContext> default_context_ = InitializeDefaultContext(); + bool with_runtime_; }; class ScopedNonWritable { @@ -154,7 +209,7 @@ static bool IsExecutedAsRoot() { // Case: We have a MultiDEX file and up-to-date ODEX file for it with relative // encoded dex locations. // Expect: The oat file status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { +TEST_P(OatFileAssistantTest, RelativeEncodedDexLocation) { std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar"; std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex"; @@ -172,21 +227,24 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { std::string error_msg; ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Verify we can load both dex files. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(2u, dex_files.size()); } -TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { +TEST_P(OatFileAssistantTest, MakeUpToDateWithContext) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string odex_location = GetOdexDir() + "/TestDex.odex"; std::string context_location = GetScratchDir() + "/ContextDex.jar"; @@ -198,8 +256,6 @@ TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { ASSERT_TRUE(context != nullptr); ASSERT_TRUE(context->OpenDexFiles()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, context.get(), false); - std::string error_msg; std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); @@ -207,6 +263,10 @@ TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { args.push_back("--class-loader-context=" + context_str); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), context.get()); + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_NE(nullptr, oat_file.get()); ASSERT_NE(nullptr, oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); @@ -214,7 +274,7 @@ TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); } -TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string odex_location = GetOdexDir() + "/TestDex.odex"; std::string context_location = GetScratchDir() + "/ContextDex.jar"; @@ -228,11 +288,6 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) { std::vector<int> context_fds; ASSERT_TRUE(relative_context->OpenDexFiles(GetScratchDir(), context_fds)); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - relative_context.get(), - false); - std::string error_msg; std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); @@ -240,20 +295,24 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) { args.push_back("--class-loader-context=PCL[" + context_location + "]"); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = + CreateOatFileAssistant(dex_location.c_str(), relative_context.get()); + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kDefaultCompilerFilter)); } // Case: We have a DEX file, but no OAT file for it. // Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, DexNoOat) { +TEST_P(OatFileAssistantTest, DexNoOat) { std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); @@ -279,13 +338,12 @@ TEST_F(OatFileAssistantTest, DexNoOat) { // Case: We have no DEX file and no OAT file. // Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash. -TEST_F(OatFileAssistantTest, NoDexNoOat) { +TEST_P(OatFileAssistantTest, NoDexNoOat) { std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -298,18 +356,16 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) { // Case: We have a DEX file and an ODEX file, but no OAT file. // Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OdexUpToDate) { +TEST_P(OatFileAssistantTest, OdexUpToDate) { std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar"; std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex"; Copy(GetDexSrc1(), dex_location); GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install"); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Force the use of oat location by making the dex parent not writable. - OatFileAssistant oat_file_assistant( - dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - /*load_executable=*/ false); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -335,7 +391,7 @@ TEST_F(OatFileAssistantTest, OdexUpToDate) { // Case: We have an ODEX file compiled against partial boot image. // Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OdexUpToDatePartialBootImage) { +TEST_P(OatFileAssistantTest, OdexUpToDatePartialBootImage) { std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar"; std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex"; Copy(GetDexSrc1(), dex_location); @@ -344,12 +400,10 @@ TEST_F(OatFileAssistantTest, OdexUpToDatePartialBootImage) { // Insert an extra dex file to the boot class path. InsertNewBootClasspathEntry(); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Force the use of oat location by making the dex parent not writable. - OatFileAssistant oat_file_assistant( - dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - /*load_executable=*/ false); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -376,7 +430,7 @@ TEST_F(OatFileAssistantTest, OdexUpToDatePartialBootImage) { // Case: We have a DEX file and a PIC ODEX file, but no OAT file. We load the dex // file via a symlink. // Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OdexUpToDateSymLink) { +TEST_P(OatFileAssistantTest, OdexUpToDateSymLink) { std::string scratch_dir = GetScratchDir(); std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar"; std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex"; @@ -389,10 +443,9 @@ TEST_F(OatFileAssistantTest, OdexUpToDateSymLink) { ASSERT_EQ(0, symlink(scratch_dir.c_str(), link.c_str())); dex_location = link + "/OdexUpToDate.jar"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -411,7 +464,7 @@ TEST_F(OatFileAssistantTest, OdexUpToDateSymLink) { // Case: We have a DEX file and up-to-date OAT file for it. // Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OatUpToDate) { +TEST_P(OatFileAssistantTest, OatUpToDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -426,10 +479,9 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -455,7 +507,7 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { // Case: Passing valid file descriptors of updated odex/vdex files along with the dex file. // Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithFd) { std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex"; @@ -470,14 +522,14 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) { android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY | O_CLOEXEC)); android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC)); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false, - false, - vdex_fd.get(), - odex_fd.get(), - zip_fd.get()); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/false, + vdex_fd.get(), + odex_fd.get(), + zip_fd.get()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, @@ -495,7 +547,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) { // Case: Passing invalid odex fd and valid vdex and zip fds. // Expect: The status should be kDex2OatForBootImage. -TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) { std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex"; @@ -509,14 +561,14 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) { android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY | O_CLOEXEC)); android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC)); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false, - false, - vdex_fd.get(), - /* oat_fd= */ -1, - zip_fd.get()); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/false, + vdex_fd.get(), + /*oat_fd=*/-1, + zip_fd.get()); EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify)); EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, @@ -532,7 +584,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) { // Case: Passing invalid vdex fd and valid odex and zip fds. // Expect: The status should be kDex2OatFromScratch. -TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) { std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; @@ -545,14 +597,14 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) { android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY | O_CLOEXEC)); android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC)); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false, - false, - /* vdex_fd= */ -1, - odex_fd.get(), - zip_fd.get()); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/false, + /*vdex_fd=*/-1, + odex_fd.get(), + zip_fd.get()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -564,20 +616,21 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) { // Case: Passing invalid vdex and odex fd with valid zip fd. // Expect: The status is kDex2oatFromScratch. -TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) { std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; Copy(GetDexSrc1(), dex_location); android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC)); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false, - false, - /* vdex_fd= */ -1, - /* oat_fd= */ -1, - zip_fd); + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/false, + /*vdex_fd=*/-1, + /*oat_fd=*/-1, + zip_fd); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); @@ -586,7 +639,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) { // Case: We have a DEX file and up-to-date VDEX file for it, but no // ODEX file. -TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { +TEST_P(OatFileAssistantTest, VdexUpToDateNoOdex) { std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar"; std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; @@ -597,10 +650,9 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); ASSERT_EQ(0, unlink(odex_location.c_str())); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify)); @@ -620,7 +672,7 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { } // Case: We have a DEX file and empty VDEX and ODEX files. -TEST_F(OatFileAssistantTest, EmptyVdexOdex) { +TEST_P(OatFileAssistantTest, EmptyVdexOdex) { std::string dex_location = GetScratchDir() + "/EmptyVdexOdex.jar"; std::string odex_location = GetOdexDir() + "/EmptyVdexOdex.oat"; std::string vdex_location = GetOdexDir() + "/EmptyVdexOdex.vdex"; @@ -629,17 +681,16 @@ TEST_F(OatFileAssistantTest, EmptyVdexOdex) { ScratchFile vdex_file(vdex_location.c_str()); ScratchFile odex_file(odex_location.c_str()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); } // Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT // file. -TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { +TEST_P(OatFileAssistantTest, VdexUpToDateNoOat) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -650,7 +701,8 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { std::string oat_location; std::string error_msg; ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( - dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + dex_location, kRuntimeISA, /* deny_art_apex_data_files= */false, &oat_location, &error_msg)) + << error_msg; Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); @@ -658,10 +710,10 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -670,7 +722,7 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { // Case: We have a DEX file and speed-profile OAT file for it. // Expect: The status is kNoDexOptNeeded if the profile hasn't changed, but // kDex2Oat if the profile has changed. -TEST_F(OatFileAssistantTest, ProfileOatUpToDate) { +TEST_P(OatFileAssistantTest, ProfileOatUpToDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -684,10 +736,9 @@ TEST_F(OatFileAssistantTest, ProfileOatUpToDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile, false)); @@ -706,7 +757,7 @@ TEST_F(OatFileAssistantTest, ProfileOatUpToDate) { // 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) { +TEST_P(OatFileAssistantTest, MultiDexOatUpToDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -720,10 +771,11 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); EXPECT_TRUE(oat_file_assistant.HasDexFiles()); @@ -731,7 +783,9 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { // Verify we can load both dex files. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(2u, dex_files.size()); @@ -739,7 +793,7 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { // Case: We have a MultiDEX file where the non-main multdex entry is out of date. // Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) { +TEST_P(OatFileAssistantTest, MultiDexNonMainOutOfDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -759,10 +813,9 @@ TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); EXPECT_TRUE(oat_file_assistant.HasDexFiles()); @@ -770,7 +823,7 @@ TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) { // Case: We have a DEX file and an OAT file out of date with respect to the // dex checksum. -TEST_F(OatFileAssistantTest, OatDexOutOfDate) { +TEST_P(OatFileAssistantTest, OatDexOutOfDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -788,10 +841,9 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, @@ -812,7 +864,7 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { // Case: We have a DEX file and an (ODEX) VDEX file out of date with respect // to the dex checksum, but no ODEX file. -TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { +TEST_P(OatFileAssistantTest, VdexDexOutOfDate) { std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar"; std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; @@ -821,10 +873,9 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { ASSERT_EQ(0, unlink(odex_location.c_str())); Copy(GetDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -832,7 +883,7 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { // Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry // is out of date and there is no corresponding ODEX file. -TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { +TEST_P(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar"; std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex"; @@ -841,10 +892,9 @@ TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { ASSERT_EQ(0, unlink(odex_location.c_str())); Copy(GetMultiDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -852,7 +902,7 @@ TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { // Case: We have a DEX file and an OAT file out of date with respect to the // boot image. -TEST_F(OatFileAssistantTest, OatImageOutOfDate) { +TEST_P(OatFileAssistantTest, OatImageOutOfDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -869,10 +919,9 @@ TEST_F(OatFileAssistantTest, OatImageOutOfDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, @@ -897,7 +946,7 @@ TEST_F(OatFileAssistantTest, OatImageOutOfDate) { // respect to the boot image. // It shouldn't matter that the OAT file is out of date, because it is // verify-at-runtime. -TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { +TEST_P(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -914,10 +963,9 @@ TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, @@ -930,7 +978,7 @@ TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { } // Case: We have a DEX file and an ODEX file, but no OAT file. -TEST_F(OatFileAssistantTest, DexOdexNoOat) { +TEST_P(OatFileAssistantTest, DexOdexNoOat) { std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar"; std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; @@ -938,11 +986,10 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { Copy(GetDexSrc1(), dex_location); GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); @@ -961,16 +1008,15 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { // Case: We have a resource-only DEX file, no ODEX file and no // OAT file. Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, ResourceOnlyDex) { +TEST_P(OatFileAssistantTest, ResourceOnlyDex) { std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar"; Copy(GetResourceOnlySrc1(), dex_location); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -995,7 +1041,7 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { // Case: We have a DEX file, an ODEX file and an OAT file. // Expect: It shouldn't crash. We should load the odex file executable. -TEST_F(OatFileAssistantTest, OdexOatOverlap) { +TEST_P(OatFileAssistantTest, OdexOatOverlap) { std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar"; std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex"; @@ -1004,11 +1050,12 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Verify things don't go bad. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -1021,7 +1068,9 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(1u, dex_files.size()); @@ -1029,7 +1078,7 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // 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) { +TEST_P(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) { std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar"; std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex"; @@ -1037,11 +1086,10 @@ TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) { Copy(GetDexSrc1(), dex_location); GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kExtract); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); @@ -1056,7 +1104,7 @@ TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) { // Case: We have a DEX file and up-to-date OAT file for it. // Expect: We should load an executable dex file. -TEST_F(OatFileAssistantTest, LoadOatUpToDate) { +TEST_P(OatFileAssistantTest, LoadOatUpToDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -1071,15 +1119,18 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(1u, dex_files.size()); @@ -1087,7 +1138,7 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) { // Case: We have a DEX file and up-to-date quicken OAT file for it. // Expect: We should still load the oat file as executable. -TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) { +TEST_P(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -1102,15 +1153,18 @@ TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) { ScopedNonWritable scoped_non_writable(dex_location); ASSERT_TRUE(scoped_non_writable.IsSuccessful()); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(1u, dex_files.size()); @@ -1118,7 +1172,7 @@ TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) { // Case: We have a DEX file and up-to-date OAT file for it. // Expect: Loading non-executable should load the oat non-executable. -TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { +TEST_P(OatFileAssistantTest, LoadNoExecOatUpToDate) { if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -1134,15 +1188,18 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_FALSE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(1u, dex_files.size()); @@ -1186,15 +1243,14 @@ static std::string MakePathRelative(const std::string& target) { // Case: Non-absolute path to Dex location. // Expect: Not sure, but it shouldn't crash. -TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { +TEST_P(OatFileAssistantTest, NonAbsoluteDexLocation) { std::string abs_dex_location = GetScratchDir() + "/NonAbsoluteDexLocation.jar"; Copy(GetDexSrc1(), abs_dex_location); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + std::string dex_location = MakePathRelative(abs_dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, @@ -1205,13 +1261,12 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { // Case: Very short, non-existent Dex location. // Expect: kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, ShortDexLocation) { +TEST_P(OatFileAssistantTest, ShortDexLocation) { std::string dex_location = "/xx"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, @@ -1223,14 +1278,13 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) { // Case: Non-standard extension for dex file. // Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, LongDexExtension) { +TEST_P(OatFileAssistantTest, LongDexExtension) { std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed)); @@ -1243,7 +1297,7 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { // A task to generate a dex location. Used by the RaceToGenerate test. class RaceGenerateTask : public Task { public: - RaceGenerateTask(OatFileAssistantTest& test, + RaceGenerateTask(OatFileAssistantBaseTest& test, const std::string& dex_location, const std::string& oat_location, Mutex* lock) @@ -1251,8 +1305,7 @@ class RaceGenerateTask : public Task { dex_location_(dex_location), oat_location_(oat_location), lock_(lock), - loaded_oat_file_(nullptr) - {} + loaded_oat_file_(nullptr) {} void Run(Thread* self ATTRIBUTE_UNUSED) override { // Load the dex files, and save a pointer to the loaded oat file, so that @@ -1288,7 +1341,7 @@ class RaceGenerateTask : public Task { } private: - OatFileAssistantTest& test_; + OatFileAssistantBaseTest& test_; std::string dex_location_; std::string oat_location_; Mutex* lock_; @@ -1297,7 +1350,7 @@ class RaceGenerateTask : public Task { // Test the case where dex2oat invocations race with multiple processes trying to // load the oat file. -TEST_F(OatFileAssistantTest, RaceToGenerate) { +TEST_F(OatFileAssistantBaseTest, RaceToGenerate) { std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar"; std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat"; @@ -1336,7 +1389,7 @@ TEST_F(OatFileAssistantTest, RaceToGenerate) { // Case: We have a DEX file and an ODEX file, and no OAT file, // Expect: We should load the odex file executable. -TEST_F(OatFileAssistantTest, LoadDexOdexNoOat) { +TEST_P(OatFileAssistantTest, LoadDexOdexNoOat) { std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar"; std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex"; @@ -1344,15 +1397,18 @@ TEST_F(OatFileAssistantTest, LoadDexOdexNoOat) { Copy(GetDexSrc1(), dex_location); GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(1u, dex_files.size()); @@ -1360,7 +1416,7 @@ TEST_F(OatFileAssistantTest, LoadDexOdexNoOat) { // Case: We have a MultiDEX file and an ODEX file, and no OAT file. // Expect: We should load the odex file executable. -TEST_F(OatFileAssistantTest, LoadMultiDexOdexNoOat) { +TEST_P(OatFileAssistantTest, LoadMultiDexOdexNoOat) { std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar"; std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex"; @@ -1368,15 +1424,18 @@ TEST_F(OatFileAssistantTest, LoadMultiDexOdexNoOat) { Copy(GetMultiDexSrc1(), dex_location); GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), - kRuntimeISA, - default_context_.get(), - true); + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), + /*context=*/nullptr, + /*load_executable=*/true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); - EXPECT_TRUE(oat_file->IsExecutable()); + if (with_runtime_) { + EXPECT_TRUE(oat_file->IsExecutable()); + } std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(2u, dex_files.size()); @@ -1402,7 +1461,7 @@ TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) { // Verify the dexopt status values from dalvik.system.DexFile // match the OatFileAssistant::DexOptStatus values. -TEST_F(OatFileAssistantTest, DexOptStatusValues) { +TEST_F(OatFileAssistantBaseTest, DexOptStatusValues) { std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = { {OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"}, {OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"}, @@ -1426,7 +1485,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { } } -TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string odex_location = GetOdexDir() + "/TestDex.odex"; @@ -1455,8 +1514,11 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { ASSERT_TRUE(updated_context != nullptr); std::vector<int> context_fds; ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true)); - OatFileAssistant oat_file_assistant( - dex_location.c_str(), kRuntimeISA, updated_context.get(), false); + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = + CreateOatFileAssistant(dex_location.c_str(), updated_context.get()); // DexOptNeeded should advise compilation for filter when the context changes. EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kDefaultCompilerFilter)); @@ -1465,12 +1527,15 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str); ASSERT_TRUE(updated_context != nullptr); std::vector<int> context_fds; - ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true)); - OatFileAssistant oat_file_assistant( - dex_location.c_str(), kRuntimeISA, updated_context.get(), false); - // Now check that DexOptNeeded does not advise compilation if we only extracted the file. + ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true)); args.push_back("--compiler-filter=extract"); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = + CreateOatFileAssistant(dex_location.c_str(), updated_context.get()); + // Now check that DexOptNeeded does not advise compilation if we only extracted the file. EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); } @@ -1478,12 +1543,15 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str); ASSERT_TRUE(updated_context != nullptr); std::vector<int> context_fds; - ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true)); - OatFileAssistant oat_file_assistant( - dex_location.c_str(), kRuntimeISA, updated_context.get(), false); - // Now check that DexOptNeeded does not advise compilation if we only verify the file. + ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true)); args.push_back("--compiler-filter=verify"); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = + CreateOatFileAssistant(dex_location.c_str(), updated_context.get()); + // Now check that DexOptNeeded does not advise compilation if we only verify the file. EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract)); } @@ -1491,7 +1559,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { // Test that GetLocation of a dex file is the same whether the dex // filed is backed by an oat file or not. -TEST_F(OatFileAssistantTest, GetDexLocation) { +TEST_F(OatFileAssistantBaseTest, GetDexLocation) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string oat_location = GetOdexDir() + "/TestDex.odex"; std::string art_location = GetOdexDir() + "/TestDex.art"; @@ -1539,7 +1607,7 @@ TEST_F(OatFileAssistantTest, GetDexLocation) { // Test that a dex file on the platform location gets the right hiddenapi domain, // regardless of whether it has a backing oat file. -TEST_F(OatFileAssistantTest, SystemFrameworkDir) { +TEST_F(OatFileAssistantBaseTest, SystemFrameworkDir) { std::string filebase = "OatFileAssistantTestSystemFrameworkDir"; std::string dex_location = GetAndroidRoot() + "/framework/" + filebase + ".jar"; Copy(GetDexSrc1(), dex_location); @@ -1619,7 +1687,7 @@ TEST_F(OatFileAssistantTest, SystemFrameworkDir) { } // Make sure OAT files that require app images are not loaded as executable. -TEST_F(OatFileAssistantTest, LoadOatNoArt) { +TEST_F(OatFileAssistantBaseTest, LoadOatNoArt) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string odex_location = GetOdexDir() + "/TestDex.odex"; std::string art_location = GetOdexDir() + "/TestDex.art"; @@ -1653,7 +1721,7 @@ TEST_F(OatFileAssistantTest, LoadOatNoArt) { EXPECT_FALSE(oat_file->IsExecutable()); } -TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { +TEST_P(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string odex_location = GetOdexDir() + "/TestDex.odex"; Copy(GetDexSrc1(), dex_location); @@ -1667,8 +1735,9 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions()); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; - OatFileAssistant oat_file_assistant( - dex_location.c_str(), kRuntimeISA, default_context_.get(), false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); } @@ -1681,8 +1750,9 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions().substr(0, 1)); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; - OatFileAssistant oat_file_assistant( - dex_location.c_str(), kRuntimeISA, default_context_.get(), false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); } @@ -1695,8 +1765,9 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { args.push_back("--apex-versions=/1/2/3/4"); ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; - OatFileAssistant oat_file_assistant( - dex_location.c_str(), kRuntimeISA, default_context_.get(), false); + auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime(); + + OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus()); } } @@ -1713,4 +1784,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { // - Dex is stripped, don't have odex. // - Oat file corrupted after status check, before reload unexecutable // because it's unrelocated and no dex2oat + +INSTANTIATE_TEST_SUITE_P(WithOrWithoutRuntime, OatFileAssistantTest, testing::Values(true, false)); + } // namespace art diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index c3a268d8b7..60e981026a 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -196,11 +196,11 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( LOG(WARNING) << "Opening an oat file without a class loader. " << "Are you using the deprecated DexFile APIs?"; } else if (context != nullptr) { - OatFileAssistant oat_file_assistant(dex_location, - kRuntimeISA, - context.get(), - runtime->GetOatFilesExecutable(), - only_use_system_oat_files_); + auto oat_file_assistant = std::make_unique<OatFileAssistant>(dex_location, + kRuntimeISA, + context.get(), + runtime->GetOatFilesExecutable(), + only_use_system_oat_files_); // Get the current optimization status for trace debugging. // Implementation detail note: GetOptimizationStatus will select the same @@ -210,11 +210,8 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( std::string compilation_filter; std::string compilation_reason; std::string odex_status; - oat_file_assistant.GetOptimizationStatus( - &odex_location, - &compilation_filter, - &compilation_reason, - &odex_status); + oat_file_assistant->GetOptimizationStatus( + &odex_location, &compilation_filter, &compilation_reason, &odex_status); Runtime::Current()->GetAppInfo()->RegisterOdexStatus( dex_location, @@ -230,7 +227,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( compilation_reason.c_str())); // Proceed with oat file loading. - std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); + std::unique_ptr<const OatFile> oat_file(oat_file_assistant->GetBestOatFile().release()); VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()=" << (oat_file != nullptr ? oat_file->GetLocation() : "") << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; @@ -250,7 +247,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr<gc::space::ImageSpace> image_space; if (ShouldLoadAppImage(oat_file.get())) { - image_space = oat_file_assistant.OpenImageSpace(oat_file.get()); + image_space = oat_file_assistant->OpenImageSpace(oat_file.get()); } if (image_space != nullptr) { ScopedObjectAccess soa(self); @@ -310,12 +307,13 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( << oat_file->GetLocation() << " non-executable as it requires an image which we failed to load"; // file as non-executable. - OatFileAssistant nonexecutable_oat_file_assistant(dex_location, - kRuntimeISA, - context.get(), - /*load_executable=*/false, - only_use_system_oat_files_); - oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release()); + auto nonexecutable_oat_file_assistant = + std::make_unique<OatFileAssistant>(dex_location, + kRuntimeISA, + context.get(), + /*load_executable=*/false, + only_use_system_oat_files_); + oat_file.reset(nonexecutable_oat_file_assistant->GetBestOatFile().release()); // The file could be deleted concurrently (for example background // dexopt, or secondary oat file being deleted by the app). @@ -325,7 +323,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( } if (oat_file != nullptr) { - dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location); + dex_files = oat_file_assistant->LoadDexFiles(*oat_file.get(), dex_location); // Register for tracking. for (const auto& dex_file : dex_files) { @@ -365,7 +363,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // If so, report an error with the current stack trace. // Most likely the developer didn't intend to do this because it will waste // performance and memory. - if (oat_file_assistant.GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) { + if (oat_file_assistant->GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) { std::set<const DexFile*> already_exists_in_classpath = context->CheckForDuplicateDexFiles(MakeNonOwningPointerVector(dex_files)); if (!already_exists_in_classpath.empty()) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d2eb3bdf63..1579900a45 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1328,9 +1328,9 @@ static inline void CreatePreAllocatedException(Thread* self, detailMessageField->SetObject</* kTransactionActive= */ false>(exception->Read(), message); } -void Runtime::InitializeApexVersions() { +std::string Runtime::GetApexVersions(ArrayRef<const std::string> boot_class_path_locations) { std::vector<std::string_view> bcp_apexes; - for (std::string_view jar : Runtime::Current()->GetBootClassPathLocations()) { + for (std::string_view jar : boot_class_path_locations) { std::string_view apex = ApexNameFromLocation(jar); if (!apex.empty()) { bcp_apexes.push_back(apex); @@ -1338,20 +1338,20 @@ void Runtime::InitializeApexVersions() { } static const char* kApexFileName = "/apex/apex-info-list.xml"; // Start with empty markers. - apex_versions_ = std::string(bcp_apexes.size(), '/'); + std::string empty_apex_versions(bcp_apexes.size(), '/'); // When running on host or chroot, we just use empty markers. if (!kIsTargetBuild || !OS::FileExists(kApexFileName)) { - return; + return empty_apex_versions; } #ifdef ART_TARGET_ANDROID if (access(kApexFileName, R_OK) != 0) { PLOG(WARNING) << "Failed to read " << kApexFileName; - return; + return empty_apex_versions; } auto info_list = apex::readApexInfoList(kApexFileName); if (!info_list.has_value()) { LOG(WARNING) << "Failed to parse " << kApexFileName; - return; + return empty_apex_versions; } std::string result; @@ -1375,10 +1375,15 @@ void Runtime::InitializeApexVersions() { android::base::StringAppendF(&result, "/%" PRIu64, version); } } - apex_versions_ = result; + return result; #endif } +void Runtime::InitializeApexVersions() { + apex_versions_ = + GetApexVersions(ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations())); +} + void Runtime::ReloadAllFlags(const std::string& caller) { FlagBase::ReloadAllFlags(caller); } diff --git a/runtime/runtime.h b/runtime/runtime.h index e7b71e29f5..9c909c41de 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -257,6 +257,13 @@ class Runtime { return instance_; } + // Set the current runtime to be the given instance. + // Note that this function is not responsible for cleaning up the old instance or taking the + // ownership of the new instance. + // + // For test use only. + static void TestOnlySetCurrent(Runtime* instance) { instance_ = instance; } + // Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most // callers should prefer. NO_RETURN static void Abort(const char* msg) REQUIRES(!Locks::abort_lock_); @@ -1084,6 +1091,10 @@ class Runtime { // See Flags::ReloadAllFlags as well. static void ReloadAllFlags(const std::string& caller); + // Parses /apex/apex-info-list.xml to build a string containing apex versions of boot classpath + // jars, which is encoded into .oat files. + static std::string GetApexVersions(ArrayRef<const std::string> boot_class_path_locations); + private: static void InitPlatformSignalHandlers(); @@ -1124,8 +1135,7 @@ class Runtime { ThreadPool* AcquireThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); void ReleaseThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); - // Parses /apex/apex-info-list.xml to initialize a string containing versions - // of boot classpath jars and encoded into .oat files. + // Caches the apex versions produced by `GetApexVersions`. void InitializeApexVersions(); // A pointer to the active runtime or null. diff --git a/test/art-gtests-target-chroot.xml b/test/art-gtests-target-chroot.xml index 5fd76f8891..88c45b72ab 100644 --- a/test/art-gtests-target-chroot.xml +++ b/test/art-gtests-target-chroot.xml @@ -37,7 +37,7 @@ <test class="com.android.tradefed.testtype.ArtGTest" > <!-- TODO(b/147821328): These tests do not work since they need to write to /system --> - <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantTest.SystemFrameworkDir" /> + <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantBaseTest.SystemFrameworkDir" /> <option name="native-test-timeout" value="600000" /> <option name="native-test-device-path" value="/data/local/tmp/art-test-chroot/apex/com.android.art/bin/art" /> </test> diff --git a/test/art-gtests-target-install-apex.xml b/test/art-gtests-target-install-apex.xml index 240b441024..b030f26f67 100644 --- a/test/art-gtests-target-install-apex.xml +++ b/test/art-gtests-target-install-apex.xml @@ -23,7 +23,7 @@ <test class="com.android.tradefed.testtype.GTest" > <!-- TODO(b/147821328): These tests do not work since they need to write to /system --> - <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantTest.SystemFrameworkDir" /> + <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantBaseTest.SystemFrameworkDir" /> <option name="native-test-timeout" value="600000" /> <!-- 10 min --> <option name="native-test-device-path" value="/apex/com.android.art/bin/art" /> </test> |