diff options
author | 2018-08-29 20:29:28 +0000 | |
---|---|---|
committer | 2018-08-29 20:29:28 +0000 | |
commit | c497fca9aa25a331af6428aa0fb6b083c6292089 (patch) | |
tree | ff93ffbc3e8d568eac71de496aa3834ebd0519ab | |
parent | ecbb5701276dd5965d68fe825da31637ca62d8c1 (diff) | |
parent | baf3761013a30b8c5dc1d6179cddaeee0a764311 (diff) |
Merge "Revert "Remove OatFileAssistant::MakeUpToDate and friends.""
-rw-r--r-- | runtime/dexopt_test.cc | 45 | ||||
-rw-r--r-- | runtime/dexopt_test.h | 2 | ||||
-rw-r--r-- | runtime/gc/space/image_space_test.cc | 2 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 334 | ||||
-rw-r--r-- | runtime/oat_file_assistant.h | 83 | ||||
-rw-r--r-- | runtime/oat_file_assistant_test.cc | 445 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 46 | ||||
-rwxr-xr-x | test/138-duplicate-classes-check2/run | 19 | ||||
-rw-r--r-- | test/677-fsi/expected.txt | 1 |
9 files changed, 808 insertions, 169 deletions
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 127e14e239..9e3159d5e7 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -20,8 +20,6 @@ #include <backtrace/BacktraceMap.h> #include <gtest/gtest.h> -#include "android-base/stringprintf.h" -#include "android-base/strings.h" #include "base/file_utils.h" #include "base/mem_map.h" #include "common_runtime_test.h" @@ -29,7 +27,6 @@ #include "dex2oat_environment_test.h" #include "dexopt_test.h" #include "gc/space/image_space.h" -#include "hidden_api.h" namespace art { void DexoptTest::SetUp() { @@ -48,46 +45,6 @@ void DexoptTest::PostRuntimeCreate() { ReserveImageSpace(); } -static std::string ImageLocation() { - Runtime* runtime = Runtime::Current(); - const std::vector<gc::space::ImageSpace*>& image_spaces = - runtime->GetHeap()->GetBootImageSpaces(); - if (image_spaces.empty()) { - return ""; - } - return image_spaces[0]->GetImageLocation(); -} - -bool DexoptTest::Dex2Oat(const std::vector<std::string>& args, std::string* error_msg) { - Runtime* runtime = Runtime::Current(); - - std::vector<std::string> argv; - argv.push_back(runtime->GetCompilerExecutable()); - if (runtime->IsJavaDebuggable()) { - argv.push_back("--debuggable"); - } - runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - - if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) { - argv.push_back("--runtime-arg"); - argv.push_back("-Xhidden-api-checks"); - } - - if (!kIsTargetBuild) { - argv.push_back("--host"); - } - - argv.push_back("--boot-image=" + ImageLocation()); - - std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); - argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); - - argv.insert(argv.end(), args.begin(), args.end()); - - std::string command_line(android::base::Join(argv, ' ')); - return Exec(argv, error_msg); -} - void DexoptTest::GenerateOatForTest(const std::string& dex_location, const std::string& oat_location_in, CompilerFilter::Filter filter, @@ -139,7 +96,7 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, } std::string error_msg; - ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; if (!relocate) { // Restore the dalvik cache if needed. diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h index 5dff379a32..df7181a1e7 100644 --- a/runtime/dexopt_test.h +++ b/runtime/dexopt_test.h @@ -71,8 +71,6 @@ class DexoptTest : public Dex2oatEnvironmentTest { // Generate a standard oat file in the oat location. void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter); - static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg); - private: // Pre-Relocate the image to a known non-zero offset so we don't have to // deal with the runtime randomly relocating the image by 0 and messing up diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index 347af4e1fd..d93385de3a 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -41,7 +41,7 @@ TEST_F(DexoptTest, ValidateOatFile) { args.push_back("--dex-file=" + multidex1); args.push_back("--dex-file=" + dex2); args.push_back("--oat-file=" + oat_location); - ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1, oat_location.c_str(), diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index e262ff7150..f7c74cc23b 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -36,6 +36,7 @@ #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" +#include "hidden_api.h" #include "image.h" #include "oat.h" #include "runtime.h" @@ -181,6 +182,30 @@ bool OatFileAssistant::IsInBootClassPath() { return false; } +bool OatFileAssistant::Lock(std::string* error_msg) { + CHECK(error_msg != nullptr); + CHECK(flock_.get() == nullptr) << "OatFileAssistant::Lock already acquired"; + + // Note the lock will only succeed for secondary dex files and in test + // environment. + // + // The lock *will fail* for all primary apks in a production environment. + // The app does not have permissions to create locks next to its dex location + // (be it system, data or vendor parition). We also cannot use the odex or + // oat location for the same reasoning. + // + // This is best effort and if it fails it's unlikely that we will be able + // to generate oat files anyway. + std::string lock_file_name = dex_location_ + "." + GetInstructionSetString(isa_) + ".flock"; + + flock_ = LockedFile::Open(lock_file_name.c_str(), error_msg); + if (flock_.get() == nullptr) { + unlink(lock_file_name.c_str()); + return false; + } + return true; +} + int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed, bool downgrade, @@ -196,10 +221,72 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, return -dexopt_needed; } +// Figure out the currently specified compile filter option in the runtime. +// Returns true on success, false if the compiler filter is invalid, in which +// case error_msg describes the problem. +static bool GetRuntimeCompilerFilterOption(CompilerFilter::Filter* filter, + std::string* error_msg) { + CHECK(filter != nullptr); + CHECK(error_msg != nullptr); + + *filter = OatFileAssistant::kDefaultCompilerFilterForDexLoading; + for (StringPiece option : Runtime::Current()->GetCompilerOptions()) { + if (option.starts_with("--compiler-filter=")) { + const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data(); + if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, filter)) { + *error_msg = std::string("Unknown --compiler-filter value: ") + + std::string(compiler_filter_string); + return false; + } + } + } + return true; +} + bool OatFileAssistant::IsUpToDate() { return GetBestInfo().Status() == kOatUpToDate; } +OatFileAssistant::ResultOfAttemptToUpdate +OatFileAssistant::MakeUpToDate(bool profile_changed, + ClassLoaderContext* class_loader_context, + std::string* error_msg) { + // The method doesn't use zip_fd_ and directly opens dex files at dex_locations_. + CHECK_EQ(-1, zip_fd_) << "MakeUpToDate should not be called with zip_fd"; + + CompilerFilter::Filter target; + if (!GetRuntimeCompilerFilterOption(&target, error_msg)) { + return kUpdateNotAttempted; + } + + OatFileInfo& info = GetBestInfo(); + // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291. + // This is actually not trivial in the current logic as it will interact with the collision + // check: + // - currently, if the context does not match but we have no collisions we still accept the + // oat file. + // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make + // the oat code up to date the collision check becomes useless. + // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files + // loaded in other processes). So it boils down to how far do we want to complicate + // the logic in order to enable the use of oat files. Maybe its time to try simplify it. + switch (info.GetDexOptNeeded( + target, profile_changed, /*downgrade*/ false, class_loader_context)) { + case kNoDexOptNeeded: + return kUpdateSucceeded; + + // TODO: For now, don't bother with all the different ways we can call + // dex2oat to generate the oat file. Always generate the oat file as if it + // were kDex2OatFromScratch. + case kDex2OatFromScratch: + case kDex2OatForBootImage: + case kDex2OatForRelocation: + case kDex2OatForFilter: + return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg); + } + UNREACHABLE(); +} + std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() { return GetBestInfo().ReleaseFileForUse(); } @@ -528,6 +615,243 @@ static bool DexLocationToOdexNames(const std::string& location, return true; } +// Prepare a subcomponent of the odex directory. +// (i.e. create and set the expected permissions on the path `dir`). +static bool PrepareDirectory(const std::string& dir, std::string* error_msg) { + struct stat dir_stat; + if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &dir_stat)) == 0) { + // The directory exists. Check if it is indeed a directory. + if (!S_ISDIR(dir_stat.st_mode)) { + *error_msg = dir + " is not a dir"; + return false; + } else { + // The dir is already on disk. + return true; + } + } + + // Failed to stat. We need to create the directory. + if (errno != ENOENT) { + *error_msg = "Could not stat isa dir " + dir + ":" + strerror(errno); + return false; + } + + mode_t mode = S_IRWXU | S_IXGRP | S_IXOTH; + if (mkdir(dir.c_str(), mode) != 0) { + *error_msg = "Could not create dir " + dir + ":" + strerror(errno); + return false; + } + if (chmod(dir.c_str(), mode) != 0) { + *error_msg = "Could not create the oat dir " + dir + ":" + strerror(errno); + return false; + } + return true; +} + +// Prepares the odex directory for the given dex location. +static bool PrepareOdexDirectories(const std::string& dex_location, + const std::string& expected_odex_location, + InstructionSet isa, + std::string* error_msg) { + std::string actual_odex_location; + std::string oat_dir; + std::string isa_dir; + if (!DexLocationToOdexNames( + dex_location, isa, &actual_odex_location, &oat_dir, &isa_dir, error_msg)) { + return false; + } + DCHECK_EQ(expected_odex_location, actual_odex_location); + + if (!PrepareDirectory(oat_dir, error_msg)) { + return false; + } + if (!PrepareDirectory(isa_dir, error_msg)) { + return false; + } + return true; +} + +class Dex2oatFileWrapper { + public: + explicit Dex2oatFileWrapper(File* file) + : file_(file), + unlink_file_at_destruction_(true) { + } + + ~Dex2oatFileWrapper() { + if (unlink_file_at_destruction_ && (file_ != nullptr)) { + file_->Erase(/*unlink*/ true); + } + } + + File* GetFile() { return file_.get(); } + + void DisableUnlinkAtDestruction() { + unlink_file_at_destruction_ = false; + } + + private: + std::unique_ptr<File> file_; + bool unlink_file_at_destruction_; +}; + +OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks( + OatFileAssistant::OatFileInfo& info, + CompilerFilter::Filter filter, + const ClassLoaderContext* class_loader_context, + std::string* error_msg) { + CHECK(error_msg != nullptr); + + Runtime* runtime = Runtime::Current(); + if (!runtime->IsDex2OatEnabled()) { + *error_msg = "Generation of oat file for dex location " + dex_location_ + + " not attempted because dex2oat is disabled."; + return kUpdateNotAttempted; + } + + if (info.Filename() == nullptr) { + *error_msg = "Generation of oat file for dex location " + dex_location_ + + " not attempted because the oat file name could not be determined."; + return kUpdateNotAttempted; + } + const std::string& oat_file_name = *info.Filename(); + const std::string& vdex_file_name = GetVdexFilename(oat_file_name); + + // dex2oat ignores missing dex files and doesn't report an error. + // Check explicitly here so we can detect the error properly. + // TODO: Why does dex2oat behave that way? + struct stat dex_path_stat; + if (TEMP_FAILURE_RETRY(stat(dex_location_.c_str(), &dex_path_stat)) != 0) { + *error_msg = "Could not access dex location " + dex_location_ + ":" + strerror(errno); + return kUpdateNotAttempted; + } + + // If this is the odex location, we need to create the odex file layout (../oat/isa/..) + if (!info.IsOatLocation()) { + if (!PrepareOdexDirectories(dex_location_, oat_file_name, isa_, error_msg)) { + return kUpdateNotAttempted; + } + } + + // Set the permissions for the oat and the vdex files. + // The user always gets read and write while the group and others propagate + // the reading access of the original dex file. + mode_t file_mode = S_IRUSR | S_IWUSR | + (dex_path_stat.st_mode & S_IRGRP) | + (dex_path_stat.st_mode & S_IROTH); + + Dex2oatFileWrapper vdex_file_wrapper(OS::CreateEmptyFile(vdex_file_name.c_str())); + File* vdex_file = vdex_file_wrapper.GetFile(); + if (vdex_file == nullptr) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the vdex file " + vdex_file_name + + " could not be opened."; + return kUpdateNotAttempted; + } + + if (fchmod(vdex_file->Fd(), file_mode) != 0) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the vdex file " + vdex_file_name + + " could not be made world readable."; + return kUpdateNotAttempted; + } + + Dex2oatFileWrapper oat_file_wrapper(OS::CreateEmptyFile(oat_file_name.c_str())); + File* oat_file = oat_file_wrapper.GetFile(); + if (oat_file == nullptr) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the oat file could not be created."; + return kUpdateNotAttempted; + } + + if (fchmod(oat_file->Fd(), file_mode) != 0) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the oat file could not be made world readable."; + return kUpdateNotAttempted; + } + + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location_); + args.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd())); + args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); + args.push_back("--oat-location=" + oat_file_name); + args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + const std::string dex2oat_context = class_loader_context == nullptr + ? OatFile::kSpecialSharedLibrary + : class_loader_context->EncodeContextForDex2oat(/*base_dir*/ ""); + args.push_back("--class-loader-context=" + dex2oat_context); + + if (!Dex2Oat(args, error_msg)) { + return kUpdateFailed; + } + + if (vdex_file->FlushCloseOrErase() != 0) { + *error_msg = "Unable to close vdex file " + vdex_file_name; + return kUpdateFailed; + } + + if (oat_file->FlushCloseOrErase() != 0) { + *error_msg = "Unable to close oat file " + oat_file_name; + return kUpdateFailed; + } + + // Mark that the odex file has changed and we should try to reload. + info.Reset(); + // We have compiled successfully. Disable the auto-unlink. + vdex_file_wrapper.DisableUnlinkAtDestruction(); + oat_file_wrapper.DisableUnlinkAtDestruction(); + + return kUpdateSucceeded; +} + +bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, + std::string* error_msg) { + Runtime* runtime = Runtime::Current(); + std::string image_location = ImageLocation(); + if (image_location.empty()) { + *error_msg = "No image location found for Dex2Oat."; + return false; + } + + std::vector<std::string> argv; + argv.push_back(runtime->GetCompilerExecutable()); + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + if (!runtime->IsVerificationEnabled()) { + argv.push_back("--compiler-filter=verify-none"); + } + + if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xhidden-api-checks"); + } + + if (runtime->MustRelocateIfPossible()) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xrelocate"); + } else { + argv.push_back("--runtime-arg"); + argv.push_back("-Xnorelocate"); + } + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.push_back("--boot-image=" + image_location); + + std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + argv.insert(argv.end(), args.begin(), args.end()); + + std::string command_line(android::base::Join(argv, ' ')); + return Exec(argv, error_msg); +} + bool OatFileAssistant::DexLocationToOdexFilename(const std::string& location, InstructionSet isa, std::string* odex_filename, @@ -561,6 +885,16 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg); } +std::string OatFileAssistant::ImageLocation() { + Runtime* runtime = Runtime::Current(); + const std::vector<gc::space::ImageSpace*>& image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + if (image_spaces.empty()) { + return ""; + } + return image_spaces[0]->GetImageLocation(); +} + const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { if (!required_dex_checksums_attempted_) { required_dex_checksums_attempted_ = true; diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index dbfbdf9fbc..a6d0961835 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -48,6 +48,11 @@ class ImageSpace; // dex location is in the boot class path. class OatFileAssistant { public: + // The default compile filter to use when optimizing dex file at load time if they + // are out of date. + static const CompilerFilter::Filter kDefaultCompilerFilterForDexLoading = + CompilerFilter::kQuicken; + enum DexOptNeeded { // No dexopt should (or can) be done to update the apk/jar. // Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0 @@ -139,6 +144,24 @@ class OatFileAssistant { // path. bool IsInBootClassPath(); + // Obtains a lock on the target oat file. + // Only one OatFileAssistant object can hold the lock for a target oat file + // at a time. The Lock is released automatically when the OatFileAssistant + // object goes out of scope. The Lock() method must not be called if the + // lock has already been acquired. + // + // Returns true on success. + // Returns false on error, in which case error_msg will contain more + // information on the error. + // + // The 'error_msg' argument must not be null. + // + // This is intended to be used to avoid race conditions when multiple + // processes generate oat files, such as when a foreground Activity and + // a background Service both use DexClassLoaders pointing to the same dex + // file. + bool Lock(std::string* error_msg); + // Return what action needs to be taken to produce up-to-date code for this // dex location. If "downgrade" is set to false, it verifies if the current // compiler filter is at least as good as an oat file generated with the @@ -164,6 +187,33 @@ class OatFileAssistant { // irrespective of the compiler filter of the up-to-date code. bool IsUpToDate(); + // Return code used when attempting to generate updated code. + enum ResultOfAttemptToUpdate { + kUpdateFailed, // We tried making the code up to date, but + // encountered an unexpected failure. + kUpdateNotAttempted, // We wanted to update the code, but determined we + // should not make the attempt. + kUpdateSucceeded // We successfully made the code up to date + // (possibly by doing nothing). + }; + + // Attempts to generate or relocate the oat file as needed to make it up to + // date based on the current runtime and compiler options. + // profile_changed should be true to indicate the profile has recently + // changed for this dex location. + // + // If the dex files need to be made up to date, class_loader_context will be + // passed to dex2oat. + // + // Returns the result of attempting to update the code. + // + // If the result is not kUpdateSucceeded, the value of error_msg will be set + // to a string describing why there was a failure or the update was not + // attempted. error_msg must not be null. + ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed, + ClassLoaderContext* class_loader_context, + std::string* error_msg); + // Returns an oat file that can be used for loading dex files. // Returns null if no suitable oat file was found. // @@ -234,6 +284,18 @@ class OatFileAssistant { // Returns the status of the oat file for the dex location. OatStatus OatFileStatus(); + // Executes dex2oat using the current runtime configuration overridden with + // the given arguments. This does not check to see if dex2oat is enabled in + // the runtime configuration. + // Returns true on success. + // + // If there is a failure, the value of error_msg will be set to a string + // describing why there was failure. error_msg must not be null. + // + // TODO: The OatFileAssistant probably isn't the right place to have this + // function. + static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg); + // Constructs the odex file name for the given dex location. // Returns true on success, in which case odex_filename is set to the odex // file name. @@ -374,6 +436,20 @@ class OatFileAssistant { bool file_released_ = false; }; + // Generate the oat file for the given info from the dex file using the + // current runtime compiler options, the specified filter and class loader + // context. + // This does not check the current status before attempting to generate the + // oat file. + // + // If the result is not kUpdateSucceeded, the value of error_msg will be set + // to a string describing why there was a failure or the update was not + // attempted. error_msg must not be null. + ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info, + CompilerFilter::Filter target, + const ClassLoaderContext* class_loader_context, + std::string* error_msg); + // Return info for the best oat file. OatFileInfo& GetBestInfo(); @@ -397,6 +473,13 @@ class OatFileAssistant { // location. OatStatus GivenOatFileStatus(const OatFile& file); + // Returns the current image location. + // Returns an empty string if the image location could not be retrieved. + // + // TODO: This method should belong with an image file manager, not + // the oat file assistant. + static std::string ImageLocation(); + // Gets the dex checksums required for an up-to-date oat file. // Returns cached_required_dex_checksums if the required checksums were // located. Returns null if the required checksums were not found. The diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 5889f8cc3e..0b3c61d474 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -41,6 +41,11 @@ namespace art { +static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4] +static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr; + +static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks"; + class OatFileAssistantTest : public DexoptTest { public: void VerifyOptimizationStatus(const std::string& file, @@ -104,97 +109,6 @@ static bool IsExecutedAsRoot() { return geteuid() == 0; } -// 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) { - std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar"; - std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex"; - - // Create the dex file - Copy(GetMultiDexSrc1(), dex_location); - - // Create the oat file with relative encoded dex location. - std::vector<std::string> args = { - "--dex-file=" + dex_location, - "--dex-location=" + std::string("RelativeEncodedDexLocation.jar"), - "--oat-file=" + odex_location, - "--compiler-filter=speed" - }; - - std::string error_msg; - ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; - - // Verify we can load both dex files. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); - - std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); - ASSERT_TRUE(oat_file.get() != nullptr); - 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) { - std::string dex_location = GetScratchDir() + "/TestDex.jar"; - std::string odex_location = GetOdexDir() + "/TestDex.odex"; - std::string context_location = GetScratchDir() + "/ContextDex.jar"; - Copy(GetDexSrc1(), dex_location); - Copy(GetDexSrc2(), context_location); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - - std::string context_str = "PCL[" + context_location + "]"; - std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str); - ASSERT_TRUE(context != nullptr); - ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, "")); - - std::string error_msg; - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--class-loader-context=" + context_str); - ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; - - std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); - EXPECT_NE(nullptr, oat_file.get()); - EXPECT_EQ(context->EncodeContextForOatFile(""), - oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); -} - -TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) { - std::string dex_location = GetScratchDir() + "/TestDex.jar"; - std::string odex_location = GetOdexDir() + "/TestDex.odex"; - std::string context_location = GetScratchDir() + "/ContextDex.jar"; - Copy(GetDexSrc1(), dex_location); - Copy(GetDexSrc2(), context_location); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - - std::string context_str = "PCL[" + context_location + "]"; - std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str); - ASSERT_TRUE(context != nullptr); - ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, "")); - - std::string error_msg; - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--class-loader-context=" + context_str); - ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; - - // A relative context simulates a dependent split context. - std::unique_ptr<ClassLoaderContext> relative_context = - ClassLoaderContext::Create("PCL[ContextDex.jar]"); - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded( - CompilerFilter::kDefaultCompilerFilter, - /* downgrade */ false, - /* profile_changed */ false, - relative_context.get())); -} - // Case: We have a DEX file, but no OAT file for it. // Expect: The status is kDex2OatNeeded. TEST_F(OatFileAssistantTest, DexNoOat) { @@ -231,6 +145,11 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); + // Trying to make the oat file up to date should not fail or crash. + std::string error_msg; + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)); + // Trying to get the best oat file should fail, but not crash. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); EXPECT_EQ(nullptr, oat_file.get()); @@ -665,6 +584,37 @@ TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) { EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus()); } +// 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) { + std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar"; + std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex"; + + // Create the dex file + Copy(GetMultiDexSrc1(), dex_location); + + // Create the oat file with relative encoded dex location. + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar")); + args.push_back("--oat-file=" + odex_location); + args.push_back("--compiler-filter=speed"); + + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + // Verify we can load both dex files. + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + ASSERT_TRUE(oat_file.get() != nullptr); + 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()); +} + // Case: We have a DEX file and an OAT file out of date with respect to the // dex checksum. TEST_F(OatFileAssistantTest, OatDexOutOfDate) { @@ -922,6 +872,13 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); + // Make the oat file up to date. This should have no effect. + std::string error_msg; + Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) << + error_msg; + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); @@ -1080,6 +1037,35 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { EXPECT_EQ(1u, dex_files.size()); } +// Case: We don't have a DEX file and can't write the oat file. +// Expect: We should fail to generate the oat file without crashing. +TEST_F(OatFileAssistantTest, GenNoDex) { + if (IsExecutedAsRoot()) { + // We cannot simulate non writable locations when executed as root: b/38000545. + LOG(ERROR) << "Test skipped because it's running as root"; + return; + } + + std::string dex_location = GetScratchDir() + "/GenNoDex.jar"; + + ScopedNonWritable scoped_non_writable(dex_location); + ASSERT_TRUE(scoped_non_writable.IsSuccessful()); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + std::string error_msg; + Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); + // We should get kUpdateSucceeded from MakeUpToDate since there's nothing + // that can be done in this situation. + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)); + + // Verify it didn't create an oat in the default location (dalvik-cache). + OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus()); + // Verify it didn't create the odex file in the default location (../oat/isa/...odex) + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OdexFileStatus()); +} + // Turn an absolute path into a path relative to the current working // directory. static std::string MakePathRelative(const std::string& target) { @@ -1145,6 +1131,13 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); + + // Trying to make it up to date should have no effect. + std::string error_msg; + Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)); + EXPECT_TRUE(error_msg.empty()); } // Case: Non-standard extension for dex file. @@ -1163,12 +1156,11 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); } - // A task to generate a dex location. Used by the RaceToGenerate test. class RaceGenerateTask : public Task { public: - RaceGenerateTask(const std::string& dex_location, const std::string& oat_location) - : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr) + explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location) + : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr) {} void Run(Thread* self ATTRIBUTE_UNUSED) { @@ -1177,15 +1169,6 @@ class RaceGenerateTask : public Task { std::vector<std::unique_ptr<const DexFile>> dex_files; std::vector<std::string> error_msgs; const OatFile* oat_file = nullptr; - { - // Create the oat file. - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location_); - args.push_back("--oat-file=" + oat_location_); - std::string error_msg; - ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg; - } - dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat( dex_location_.c_str(), Runtime::Current()->GetSystemClassLoader(), @@ -1193,9 +1176,8 @@ class RaceGenerateTask : public Task { &oat_file, &error_msgs); CHECK(!dex_files.empty()) << android::base::Join(error_msgs, '\n'); - if (dex_files[0]->GetOatDexFile() != nullptr) { - loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile(); - } + CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation(); + loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile(); CHECK_EQ(loaded_oat_file_, oat_file); } @@ -1209,8 +1191,12 @@ class RaceGenerateTask : public Task { const OatFile* loaded_oat_file_; }; -// Test the case where dex2oat invocations race with multiple processes trying to -// load the oat file. +// Test the case where multiple processes race to generate an oat file. +// This simulates multiple processes using multiple threads. +// +// We want unique Oat files to be loaded even when there is a race to load. +// TODO: The test case no longer tests locking the way it was intended since we now get multiple +// copies of the same Oat files mapped at different locations. TEST_F(OatFileAssistantTest, RaceToGenerate) { std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar"; std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat"; @@ -1223,26 +1209,24 @@ TEST_F(OatFileAssistantTest, RaceToGenerate) { // take a while to generate. Copy(GetLibCoreDexFileNames()[0], dex_location); - const size_t kNumThreads = 32; + const int kNumThreads = 32; Thread* self = Thread::Current(); ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads); std::vector<std::unique_ptr<RaceGenerateTask>> tasks; - for (size_t i = 0; i < kNumThreads; i++) { + for (int i = 0; i < kNumThreads; i++) { std::unique_ptr<RaceGenerateTask> task(new RaceGenerateTask(dex_location, oat_location)); thread_pool.AddTask(self, task.get()); tasks.push_back(std::move(task)); } thread_pool.StartWorkers(self); - thread_pool.Wait(self, /* do_work */ true, /* may_hold_locks */ false); + thread_pool.Wait(self, true, false); - // Verify that tasks which got an oat file got a unique one. + // Verify every task got a unique oat file. std::set<const OatFile*> oat_files; for (auto& task : tasks) { const OatFile* oat_file = task->GetLoadedOatFile(); - if (oat_file != nullptr) { - EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end()); - oat_files.insert(oat_file); - } + EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end()); + oat_files.insert(oat_file); } } @@ -1290,6 +1274,36 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) { EXPECT_EQ(2u, dex_files.size()); } +TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) { + std::string dex_location = GetScratchDir() + "/RuntimeCompilerFilterOptionUsed.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + std::string error_msg; + Runtime::Current()->AddCompilerOption("--compiler-filter=quicken"); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) << + error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); + EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + + Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) + << error_msg; + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + + Runtime::Current()->AddCompilerOption("--compiler-filter=bogus"); + EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)); +} + TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) { std::string error_msg; std::string odex_file; @@ -1336,6 +1350,112 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { } } +// Verify that when no compiler filter is passed the default one from OatFileAssistant is used. +TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; + std::string error_msg; + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) << + error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(default_filter)); + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + EXPECT_EQ(default_filter, oat_file->GetCompilerFilter()); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(default_filter)); + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + EXPECT_EQ(kSpecialSharedLibrary, + oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + std::string context_location = GetScratchDir() + "/ContextDex.jar"; + Copy(GetDexSrc1(), dex_location); + Copy(GetDexSrc2(), context_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; + std::string error_msg; + std::string context_str = "PCL[" + context_location + "]"; + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str); + ASSERT_TRUE(context != nullptr); + ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, "")); + + int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get())); + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + EXPECT_EQ(context->EncodeContextForOatFile(""), + oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) { + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kNoChecks); + + std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + + const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + EXPECT_NE(nullptr, cmd_line); + EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg)); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) { + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kBlacklistOnly); + + std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + + const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + EXPECT_NE(nullptr, cmd_line); + EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg)); +} + TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string context_location = GetScratchDir() + "/ContextDex.jar"; @@ -1344,12 +1464,19 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; std::string error_msg; std::string context_str = "PCL[" + context_location + "]"; std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str); ASSERT_TRUE(context != nullptr); ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, "")); + int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get())); + // Update the context by overriding the jar file. Copy(GetMultiDexSrc2(), context_location); std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str); @@ -1357,10 +1484,88 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { // DexOptNeeded should advise compilation from scratch. EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded( - CompilerFilter::kDefaultCompilerFilter, - /* downgrade */ false, - /* profile_changed */ false, - updated_context.get())); + default_filter, false, false, updated_context.get())); +} + +TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + std::string context_location = GetScratchDir() + "/ContextDex.jar"; + Copy(GetDexSrc1(), dex_location); + Copy(GetDexSrc2(), context_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; + std::string error_msg; + std::string context_str = "PCL[" + context_location + "]"; + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str); + ASSERT_TRUE(context != nullptr); + ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, "")); + + int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + // A relative context simulates a dependent split context. + std::unique_ptr<ClassLoaderContext> relative_context = + ClassLoaderContext::Create("PCL[ContextDex.jar]"); + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded( + default_filter, false, false, relative_context.get())); +} + +TEST_F(OatFileAssistantTest, SystemOdex) { + std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; + std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; + std::string system_location = GetAndroidRoot() + "/OatUpToDate.jar"; + + std::string error_msg; + + Copy(GetDexSrc1(), dex_location); + EXPECT_FALSE(LocationIsOnSystem(dex_location.c_str())); + + { + OatFileAssistant oat_file_assistant(dex_location.c_str(), + kRuntimeISA, + true, + false); + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); + } + + { + OatFileAssistant oat_file_assistant(dex_location.c_str(), + kRuntimeISA, + true, + true); + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable()); + } + + Copy(GetDexSrc1(), system_location); + EXPECT_TRUE(LocationIsOnSystem(system_location.c_str())); + + { + OatFileAssistant oat_file_assistant(system_location.c_str(), + kRuntimeISA, + true, + false); + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); + } + + { + OatFileAssistant oat_file_assistant(system_location.c_str(), + kRuntimeISA, + true, + true); + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); + } } // TODO: More Tests: diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index bcad4a3428..59a1045ba2 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -465,15 +465,57 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( !runtime->IsAotCompiler(), only_use_system_oat_files_); + // Lock the target oat location to avoid races generating and loading the + // oat file. + std::string error_msg; + if (!oat_file_assistant.Lock(/*out*/&error_msg)) { + // Don't worry too much if this fails. If it does fail, it's unlikely we + // can generate an oat file anyway. + VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg; + } + + const OatFile* source_oat_file = nullptr; + + if (!oat_file_assistant.IsUpToDate()) { + // Update the oat file on disk if we can, based on the --compiler-filter + // option derived from the current runtime options. + // This may fail, but that's okay. Best effort is all that matters here. + // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile + // secondary dex files in isolation (and avoid to extract/verify the main apk + // if it's in the class path). Note this trades correctness for performance + // since the resulting slow down is unacceptable in some cases until b/64530081 + // is fixed. + // We still pass the class loader context when the classpath string of the runtime + // is not empty, which is the situation when ART is invoked standalone. + ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty() + ? nullptr + : context.get(); + switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false, + actual_context, + /*out*/ &error_msg)) { + case OatFileAssistant::kUpdateFailed: + LOG(WARNING) << error_msg; + break; + + case OatFileAssistant::kUpdateNotAttempted: + // Avoid spamming the logs if we decided not to attempt making the oat + // file up to date. + VLOG(oat) << error_msg; + break; + + case OatFileAssistant::kUpdateSucceeded: + // Nothing to do. + break; + } + } + // Get the oat file on disk. std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()=" << reinterpret_cast<uintptr_t>(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; - const OatFile* source_oat_file = nullptr; CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; - std::string error_msg; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.<init>(String) is called directly, and it diff --git a/test/138-duplicate-classes-check2/run b/test/138-duplicate-classes-check2/run new file mode 100755 index 0000000000..8494ad9aad --- /dev/null +++ b/test/138-duplicate-classes-check2/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# We want to run as no-dex-file-fallback to confirm that even though the -ex file has a symbolic +# reference to A, there's no class-def, so we don't detect a collision. +exec ${RUN} --runtime-option -Xno-dex-file-fallback "${@}" diff --git a/test/677-fsi/expected.txt b/test/677-fsi/expected.txt index 2b073430b6..c7fb8fed77 100644 --- a/test/677-fsi/expected.txt +++ b/test/677-fsi/expected.txt @@ -1,2 +1,3 @@ oat file has dex code, but APK has uncompressed dex code +oat file has dex code, but APK has uncompressed dex code Hello World |