diff options
| -rw-r--r-- | runtime/dex2oat_environment_test.h | 11 | ||||
| -rw-r--r-- | runtime/dexopt_test.cc | 18 | ||||
| -rw-r--r-- | runtime/oat_file_assistant.cc | 286 | ||||
| -rw-r--r-- | runtime/oat_file_assistant.h | 37 | ||||
| -rw-r--r-- | runtime/oat_file_assistant_test.cc | 320 | ||||
| -rw-r--r-- | runtime/oat_file_manager.cc | 2 |
6 files changed, 210 insertions, 464 deletions
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 6765407949..e58c6f541e 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -42,16 +42,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); // Create a scratch directory to work from. - - // Get the realpath of the android data. The oat dir should always point to real location - // when generating oat files in dalvik-cache. This avoids complicating the unit tests - // when matching the expected paths. - UniqueCPtr<const char[]> android_data_real(realpath(android_data_.c_str(), nullptr)); - ASSERT_TRUE(android_data_real != nullptr) - << "Could not get the realpath of the android data" << android_data_ << strerror(errno); - - scratch_dir_.assign(android_data_real.get()); - scratch_dir_ += "/Dex2oatEnvironmentTest"; + scratch_dir_ = android_data_ + "/Dex2oatEnvironmentTest"; ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700)); // Create a subdirectory in scratch for odex files. diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 3c8243a6c5..24b1abbad4 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -45,23 +45,18 @@ void DexoptTest::PostRuntimeCreate() { } void DexoptTest::GenerateOatForTest(const std::string& dex_location, - const std::string& oat_location_in, - CompilerFilter::Filter filter, - bool relocate, - bool pic, - bool with_alternate_image) { + const std::string& oat_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_alternate_image) { std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA)); std::string dalvik_cache_tmp = dalvik_cache + ".redirected"; - std::string oat_location = oat_location_in; + if (!relocate) { // Temporarily redirect the dalvik cache so dex2oat doesn't find the // relocated image file. ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno); - // If the oat location is in dalvik cache, replace the cache path with the temporary one. - size_t pos = oat_location.find(dalvik_cache); - if (pos != std::string::npos) { - oat_location = oat_location.replace(pos, dalvik_cache.length(), dalvik_cache_tmp); - } } std::vector<std::string> args; @@ -95,7 +90,6 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, if (!relocate) { // Restore the dalvik cache if needed. ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno); - oat_location = oat_location_in; } // Verify the odex file was generated as expected. diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 603bbbf8bd..eafa77f1a2 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -68,34 +68,19 @@ std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStat OatFileAssistant::OatFileAssistant(const char* dex_location, const InstructionSet isa, bool load_executable) + : OatFileAssistant(dex_location, nullptr, isa, load_executable) +{ } + +OatFileAssistant::OatFileAssistant(const char* dex_location, + const char* oat_location, + const InstructionSet isa, + bool load_executable) : isa_(isa), load_executable_(load_executable), odex_(this, /*is_oat_location*/ false), oat_(this, /*is_oat_location*/ true) { CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location"; - - // Try to get the realpath for the dex location. - // - // This is OK with respect to dalvik cache naming scheme because we never - // generate oat files starting from symlinks which go into dalvik cache. - // (recall that the oat files in dalvik cache are encoded by replacing '/' - // with '@' in the path). - // The boot image oat files (which are symlinked in dalvik-cache) are not - // loaded via the oat file assistant. - // - // The only case when the dex location may resolve to a different path - // is for secondary dex files (e.g. /data/user/0 symlinks to /data/data and - // the app is free to create its own internal layout). Related to this it is - // worthwhile to mention that installd resolves the secondary dex location - // before calling dex2oat. - UniqueCPtr<const char[]> dex_location_real(realpath(dex_location, nullptr)); - if (dex_location_real != nullptr) { - dex_location_.assign(dex_location_real.get()); - } else { - // If we can't get the realpath of the location there's not much point in trying to move on. - PLOG(ERROR) << "Could not get the realpath of dex_location " << dex_location; - return; - } + dex_location_.assign(dex_location); if (load_executable_ && isa != kRuntimeISA) { LOG(WARNING) << "OatFileAssistant: Load executable specified, " @@ -113,27 +98,15 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, } // Get the oat filename. - std::string oat_file_name; - if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) { - oat_.Reset(oat_file_name); + if (oat_location != nullptr) { + oat_.Reset(oat_location); } else { - LOG(WARNING) << "Failed to determine oat file name for dex location " - << dex_location_ << ": " << error_msg; - } - - // Check if the dex directory is writable. - // This will be needed in most uses of OatFileAssistant and so it's OK to - // compute it eagerly. (the only use which will not make use of it is - // OatFileAssistant::GetStatusDump()) - size_t pos = dex_location_.rfind('/'); - if (pos == std::string::npos) { - LOG(WARNING) << "Failed to determine dex file parent directory: " << dex_location_; - } else { - std::string parent = dex_location_.substr(0, pos); - if (access(parent.c_str(), W_OK) == 0) { - dex_parent_writable_ = true; + std::string oat_file_name; + if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) { + oat_.Reset(oat_file_name); } else { - VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror(errno); + LOG(WARNING) << "Failed to determine oat file name for dex location " + << dex_location_ << ": " << error_msg; } } } @@ -166,17 +139,12 @@ bool OatFileAssistant::Lock(std::string* error_msg) { CHECK(error_msg != nullptr); CHECK(!flock_.HasFile()) << "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"; + const std::string* oat_file_name = oat_.Filename(); + if (oat_file_name == nullptr) { + *error_msg = "Failed to determine lock file"; + return false; + } + std::string lock_file_name = *oat_file_name + ".flock"; if (!flock_.Init(lock_file_name.c_str(), error_msg)) { unlink(lock_file_name.c_str()); @@ -202,7 +170,7 @@ static bool GetRuntimeCompilerFilterOption(CompilerFilter::Filter* filter, CHECK(filter != nullptr); CHECK(error_msg != nullptr); - *filter = OatFileAssistant::kDefaultCompilerFilterForDexLoading; + *filter = CompilerFilter::kDefaultCompilerFilter; for (StringPiece option : Runtime::Current()->GetCompilerOptions()) { if (option.starts_with("--compiler-filter=")) { const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data(); @@ -239,7 +207,7 @@ OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) { case kDex2OatForBootImage: case kDex2OatForRelocation: case kDex2OatForFilter: - return GenerateOatFileNoChecks(info, error_msg); + return GenerateOatFile(error_msg); } UNREACHABLE(); } @@ -511,110 +479,8 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& return kOatUpToDate; } -static bool DexLocationToOdexNames(const std::string& location, - InstructionSet isa, - std::string* odex_filename, - std::string* oat_dir, - std::string* isa_dir, - std::string* error_msg) { - CHECK(odex_filename != nullptr); - CHECK(error_msg != nullptr); - - // The odex file name is formed by replacing the dex_location extension with - // .odex and inserting an oat/<isa> directory. For example: - // location = /foo/bar/baz.jar - // odex_location = /foo/bar/oat/<isa>/baz.odex - - // Find the directory portion of the dex location and add the oat/<isa> - // directory. - size_t pos = location.rfind('/'); - if (pos == std::string::npos) { - *error_msg = "Dex location " + location + " has no directory."; - return false; - } - std::string dir = location.substr(0, pos+1); - // Add the oat directory. - dir += "oat"; - if (oat_dir != nullptr) { - *oat_dir = dir; - } - // Add the isa directory - dir += "/" + std::string(GetInstructionSetString(isa)); - if (isa_dir != nullptr) { - *isa_dir = dir; - } - - // Get the base part of the file without the extension. - std::string file = location.substr(pos+1); - pos = file.rfind('.'); - if (pos == std::string::npos) { - *error_msg = "Dex location " + location + " has no extension."; - return false; - } - std::string base = file.substr(0, pos); - - *odex_filename = dir + "/" + base + ".odex"; - 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; -} - -OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks( - OatFileAssistant::OatFileInfo& info, std::string* error_msg) { +OatFileAssistant::ResultOfAttemptToUpdate +OatFileAssistant::GenerateOatFile(std::string* error_msg) { CHECK(error_msg != nullptr); Runtime* runtime = Runtime::Current(); @@ -624,37 +490,22 @@ OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChe return kUpdateNotAttempted; } - if (info.Filename() == nullptr) { + if (oat_.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& oat_file_name = *oat_.Filename(); const std::string& vdex_file_name = ReplaceFileExtension(oat_file_name, "vdex"); // 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); + if (!OS::FileExists(dex_location_.c_str())) { + *error_msg = "Dex location " + dex_location_ + " does not exists."; 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); - std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_file_name.c_str())); if (vdex_file.get() == nullptr) { *error_msg = "Generation of oat file " + oat_file_name @@ -663,7 +514,7 @@ OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChe return kUpdateNotAttempted; } - if (fchmod(vdex_file->Fd(), file_mode) != 0) { + if (fchmod(vdex_file->Fd(), 0644) != 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."; @@ -677,7 +528,7 @@ OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChe return kUpdateNotAttempted; } - if (fchmod(oat_file->Fd(), file_mode) != 0) { + if (fchmod(oat_file->Fd(), 0644) != 0) { *error_msg = "Generation of oat file " + oat_file_name + " not attempted because the oat file could not be made world readable."; oat_file->Erase(); @@ -712,8 +563,8 @@ OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChe return kUpdateFailed; } - // Mark that the odex file has changed and we should try to reload. - info.Reset(); + // Mark that the oat file has changed and we should try to reload. + oat_.Reset(); return kUpdateSucceeded; } @@ -772,7 +623,35 @@ bool OatFileAssistant::DexLocationToOdexFilename(const std::string& location, InstructionSet isa, std::string* odex_filename, std::string* error_msg) { - return DexLocationToOdexNames(location, isa, odex_filename, nullptr, nullptr, error_msg); + CHECK(odex_filename != nullptr); + CHECK(error_msg != nullptr); + + // The odex file name is formed by replacing the dex_location extension with + // .odex and inserting an oat/<isa> directory. For example: + // location = /foo/bar/baz.jar + // odex_location = /foo/bar/oat/<isa>/baz.odex + + // Find the directory portion of the dex location and add the oat/<isa> + // directory. + size_t pos = location.rfind('/'); + if (pos == std::string::npos) { + *error_msg = "Dex location " + location + " has no directory."; + return false; + } + std::string dir = location.substr(0, pos+1); + dir += "oat/" + std::string(GetInstructionSetString(isa)); + + // Get the base part of the file without the extension. + std::string file = location.substr(pos+1); + pos = file.rfind('.'); + if (pos == std::string::npos) { + *error_msg = "Dex location " + location + " has no extension."; + return false; + } + std::string base = file.substr(0, pos); + + *odex_filename = dir + "/" + base + ".odex"; + return true; } bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, @@ -873,45 +752,8 @@ const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { } OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() { - // TODO(calin): Document the side effects of class loading when - // running dalvikvm command line. - if (dex_parent_writable_) { - // If the parent of the dex file is writable it means that we can - // create the odex file. In this case we unconditionally pick the odex - // as the best oat file. This corresponds to the regular use case when - // apps gets installed or when they load private, secondary dex file. - // For apps on the system partition the odex location will not be - // writable and thus the oat location might be more up to date. - return odex_; - } - - // We cannot write to the odex location. This must be a system app. - - // If the oat location is usable take it. - if (oat_.IsUseable()) { - return oat_; - } - - // The oat file is not usable but the odex file might be up to date. - // This is an indication that we are dealing with an up to date prebuilt - // (that doesn't need relocation). - if (odex_.Status() == kOatUpToDate) { - return odex_; - } - - // The oat file is not usable and the odex file is not up to date. - // However we have access to the original dex file which means we can make - // the oat location up to date. - if (HasOriginalDexFiles()) { - return oat_; - } - - // We got into the worst situation here: - // - the oat location is not usable - // - the prebuild odex location is not up to date - // - and we don't have the original dex file anymore (stripped). - // Pick the odex if it exists, or the oat if not. - return (odex_.Status() == kOatCannotOpen) ? oat_ : odex_; + bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen; + return use_oat ? oat_ : odex_; } std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) { diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 7e2385ec6c..b84e711daa 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -47,11 +47,6 @@ 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 @@ -122,6 +117,13 @@ class OatFileAssistant { const InstructionSet isa, bool load_executable); + // Constructs an OatFileAssistant, providing an explicit target oat_location + // to use instead of the standard oat location. + OatFileAssistant(const char* dex_location, + const char* oat_location, + const InstructionSet isa, + bool load_executable); + ~OatFileAssistant(); // Returns true if the dex location refers to an element of the boot class @@ -230,6 +232,16 @@ class OatFileAssistant { // Returns the status of the oat file for the dex location. OatStatus OatFileStatus(); + // Generate the oat file from the dex file using the current runtime + // compiler options. + // 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 GenerateOatFile(std::string* error_msg); + // Executes dex2oat using the current runtime configuration overridden with // the given arguments. This does not check to see if dex2oat is enabled in // the runtime configuration. @@ -365,16 +377,6 @@ class OatFileAssistant { bool file_released_ = false; }; - // Generate the oat file for the given info from the dex file using the - // current runtime compiler options. - // 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, std::string* error_msg); - // Return info for the best oat file. OatFileInfo& GetBestInfo(); @@ -420,9 +422,6 @@ class OatFileAssistant { std::string dex_location_; - // Whether or not the parent directory of the dex file is writable. - bool dex_parent_writable_ = false; - // In a properly constructed OatFileAssistant object, isa_ should be either // the 32 or 64 bit variant for the current device. const InstructionSet isa_ = kNone; @@ -447,8 +446,6 @@ class OatFileAssistant { bool image_info_load_attempted_ = false; std::unique_ptr<ImageInfo> cached_image_info_; - friend class OatFileAssistantTest; - DISALLOW_COPY_AND_ASSIGN(OatFileAssistant); }; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index fc1af628a4..18924e9654 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -43,33 +43,6 @@ class OatFileAssistantNoDex2OatTest : public DexoptTest { } }; -class ScopedNonWritable { - public: - explicit ScopedNonWritable(const std::string& dex_location) { - size_t pos = dex_location.rfind('/'); - if (pos != std::string::npos) { - dex_parent_ = dex_location.substr(0, pos); - is_valid_ = chmod(dex_parent_.c_str(), 0555) == 0; - if (!is_valid_) { - PLOG(ERROR) << "Could not change permissions on " << dex_parent_; - } - } - } - - bool IsValid() { return is_valid_; } - - ~ScopedNonWritable() { - if (is_valid_) { - if (chmod(dex_parent_.c_str(), 0777) != 0) { - PLOG(ERROR) << "Could not restore permissions on " << dex_parent_; - } - } - } - - private: - std::string dex_parent_; - bool is_valid_; -}; // Case: We have a DEX file, but no OAT file for it. // Expect: The status is kDex2OatNeeded. @@ -114,65 +87,6 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) { EXPECT_EQ(nullptr, oat_file.get()); } -// Case: We have a DEX file and a PIC ODEX file, but no OAT file. -// Expect: The status is kNoDexOptNeeded, because PIC needs no relocation. -TEST_F(OatFileAssistantTest, OdexUpToDate) { - std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar"; - std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex"; - Copy(GetDexSrc1(), dex_location); - GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); - - // For the use of oat location by making the dex parent not writable. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); - EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - -// 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, because PIC needs no relocation. -TEST_F(OatFileAssistantTest, OdexUpToDateSymLink) { - std::string scratch_dir = GetScratchDir(); - std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar"; - std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex"; - - Copy(GetDexSrc1(), dex_location); - GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); - - // Now replace the dex location with a symlink. - std::string link = scratch_dir + "/link"; - 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, false); - - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); - EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - // Case: We have a DEX file and up-to-date OAT file for it. // Expect: The status is kNoDexOptNeeded. TEST_F(OatFileAssistantTest, OatUpToDate) { @@ -180,48 +94,6 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - // For the use of oat location by making the dex parent not writable. - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); - EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - -// Case: We have a DEX file and up-to-date OAT file for it. We load the dex file -// via a symlink. -// Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OatUpToDateSymLink) { - std::string real = GetScratchDir() + "/real"; - ASSERT_EQ(0, mkdir(real.c_str(), 0700)); - std::string link = GetScratchDir() + "/link"; - ASSERT_EQ(0, symlink(real.c_str(), link.c_str())); - - std::string dex_location = real + "/OatUpToDate.jar"; - - Copy(GetDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - - // Update the dex location to point to the symlink. - dex_location = link + "/OatUpToDate.jar"; - - // For the use of oat location by making the dex parent not writable. - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, @@ -248,16 +120,19 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { } std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar"; - std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; + std::string oat_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; Copy(GetDexSrc1(), dex_location); // Generating and deleting the oat file should have the side effect of // creating an up-to-date vdex file. - GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); - ASSERT_EQ(0, unlink(odex_location.c_str())); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); // Even though the vdex file is up to date, because we don't have the oat // file, we can't know that the vdex depends on the boot image and is up to @@ -305,8 +180,6 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); ASSERT_EQ(0, unlink(oat_location.c_str())); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); // Even though the vdex file is up to date, because we don't have the oat @@ -326,9 +199,6 @@ TEST_F(OatFileAssistantTest, ProfileOatUpToDate) { Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, @@ -353,9 +223,6 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { Copy(GetMultiDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false)); @@ -383,9 +250,6 @@ TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) { // is out of date. Copy(GetMultiDexSrc2(), dex_location); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false)); @@ -423,12 +287,12 @@ 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 +// Case: We have a MultiDEX file and up-to-date OAT 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"; + std::string oat_location = GetOdexDir() + "/RelativeEncodedDexLocation.oat"; // Create the dex file Copy(GetMultiDexSrc1(), dex_location); @@ -437,15 +301,16 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { 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("--oat-file=" + oat_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); - + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_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()); @@ -465,9 +330,6 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); Copy(GetDexSrc2(), dex_location); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); @@ -489,14 +351,17 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { } std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar"; - std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; + std::string oat_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); - ASSERT_EQ(0, unlink(odex_location.c_str())); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); Copy(GetDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); @@ -511,14 +376,17 @@ TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { } std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar"; - std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex"; + std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat"; Copy(GetMultiDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); - ASSERT_EQ(0, unlink(odex_location.c_str())); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); Copy(GetMultiDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); @@ -536,9 +404,6 @@ TEST_F(OatFileAssistantTest, OatImageOutOfDate) { /*pic*/false, /*with_alternate_image*/true); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); @@ -567,9 +432,6 @@ TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { /*pic*/false, /*with_alternate_image*/true); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); @@ -724,23 +586,24 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { TEST_F(OatFileAssistantTest, OdexOatOverlap) { std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar"; std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex"; + std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat"; - // Create the dex, the odex and the oat files. + // Create the dex and odex files Copy(GetDexSrc1(), dex_location); GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); - GenerateOatForTest(dex_location.c_str(), - CompilerFilter::kSpeed, - /*relocate*/false, - /*pic*/false, - /*with_alternate_image*/false); + + // Create the oat file by copying the odex so they are located in the same + // place in memory. + Copy(odex_location, oat_location); // Verify things don't go bad. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), kRuntimeISA, true); - // -kDex2OatForRelocation is expected rather than kDex2OatForRelocation - // based on the assumption that the odex location is more up-to-date than the oat + // kDex2OatForRelocation is expected rather than -kDex2OatForRelocation + // based on the assumption that the oat location is more up-to-date than the odex // location, even if they both need relocation. - EXPECT_EQ(-OatFileAssistant::kDex2OatForRelocation, + EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); @@ -758,6 +621,30 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { EXPECT_EQ(1u, dex_files.size()); } +// Case: We have a DEX file and a PIC ODEX file, but no OAT file. +// Expect: The status is kNoDexOptNeeded, because PIC needs no relocation. +TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex"; + + // Create the dex and odex files + Copy(GetDexSrc1(), dex_location); + GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + + // Verify the status. + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + // 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) { @@ -790,9 +677,6 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) { Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - // Load the oat using an oat file assistant. OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); @@ -812,9 +696,6 @@ TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) { Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken); - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - // Load the oat using an oat file assistant. OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); @@ -832,10 +713,6 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { std::string dex_location = GetScratchDir() + "/LoadNoExecOatUpToDate.jar"; Copy(GetDexSrc1(), dex_location); - - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); - GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); // Load the oat using an oat file assistant. @@ -849,27 +726,70 @@ 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) { - std::string dex_location = GetScratchDir() + "/GenNoDex.jar"; +// Case: We have a DEX file. +// Expect: We should load an executable dex file from an alternative oat +// location. +TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { + std::string dex_location = GetScratchDir() + "/LoadDexNoAlternateOat.jar"; + std::string oat_location = GetScratchDir() + "/LoadDexNoAlternateOat.oat"; - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsValid()); + Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant( + dex_location.c_str(), oat_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, &error_msg)); + oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; - // Verify it didn't create an oat in the default location (dalvik-cache). + 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(1u, dex_files.size()); + + EXPECT_TRUE(OS::FileExists(oat_location.c_str())); + + // Verify it didn't create an oat in the default location. OatFileAssistant ofm(dex_location.c_str(), 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()); +} + +// Case: We have a DEX file but can't write the oat file. +// Expect: We should fail to make the oat file up to date. +TEST_F(OatFileAssistantTest, LoadDexUnwriteableAlternateOat) { + std::string dex_location = GetScratchDir() + "/LoadDexUnwriteableAlternateOat.jar"; + + // Make the oat location unwritable by inserting some non-existent + // intermediate directories. + std::string oat_location = GetScratchDir() + "/foo/bar/LoadDexUnwriteableAlternateOat.oat"; + + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant( + dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + std::string error_msg; + Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); + ASSERT_EQ(OatFileAssistant::kUpdateNotAttempted, + oat_file_assistant.MakeUpToDate(false, &error_msg)); + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + ASSERT_TRUE(oat_file.get() == nullptr); +} + +// 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) { + std::string dex_location = GetScratchDir() + "/GenNoDex.jar"; + std::string oat_location = GetScratchDir() + "/GenNoDex.oat"; + + OatFileAssistant oat_file_assistant( + dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + std::string error_msg; + Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); + EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted, + oat_file_assistant.GenerateOatFile(&error_msg)); } // Turn an absolute path into a path relative to the current working @@ -1086,9 +1006,9 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) { Runtime::Current()->AddCompilerOption("--compiler-filter=quicken"); EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; - EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); - EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index c1cf800e5d..932d5edbef 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -615,7 +615,9 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( Locks::mutator_lock_->AssertNotHeld(self); Runtime* const runtime = Runtime::Current(); + // TODO(calin): remove the explicit oat_location for OatFileAssistant OatFileAssistant oat_file_assistant(dex_location, + /*oat_location*/ nullptr, kRuntimeISA, !runtime->IsAotCompiler()); |