diff options
| -rw-r--r-- | dexoptanalyzer/dexoptanalyzer.cc | 33 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 157 | ||||
| -rw-r--r-- | runtime/oat_file.h | 12 | ||||
| -rw-r--r-- | runtime/oat_file_assistant.cc | 68 | ||||
| -rw-r--r-- | runtime/oat_file_assistant.h | 9 | ||||
| -rw-r--r-- | runtime/oat_file_assistant_test.cc | 120 |
6 files changed, 377 insertions, 22 deletions
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 51a67ca45e..08d38d5925 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -97,6 +97,10 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --android-data=<directory>: optional, the directory which should be used as"); UsageError(" android-data. By default ANDROID_DATA env variable is used."); UsageError(""); + UsageError(" --oat-fd=number: file descriptor of the oat file which should be analyzed"); + UsageError(""); + UsageError(" --vdex-fd=number: file descriptor of the vdex file corresponding to the oat file"); + UsageError(""); UsageError(" --downgrade: optional, if the purpose of dexopt is to downgrade the dex file"); UsageError(" By default, dexopt considers upgrade case."); UsageError(""); @@ -167,6 +171,10 @@ class DexoptAnalyzer FINAL { setenv("ANDROID_DATA", new_android_data.c_str(), 1); } else if (option.starts_with("--downgrade")) { downgrade_ = true; + } else if (option.starts_with("--oat-fd")) { + oat_fd_ = std::stoi(option.substr(strlen("--oat-fd=")).ToString(), nullptr, 0); + } else if (option.starts_with("--vdex-fd")) { + vdex_fd_ = std::stoi(option.substr(strlen("--vdex-fd=")).ToString(), nullptr, 0); } else { Usage("Unknown argument '%s'", option.data()); } } @@ -181,6 +189,12 @@ class DexoptAnalyzer FINAL { Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist."); } } + if (oat_fd_ > 0 && vdex_fd_ < 0) { + Usage("A valid --vdex-fd must also be provided with --oat-fd."); + } + if (oat_fd_ < 0 && vdex_fd_ > 0) { + Usage("A valid --oat-fd must also be provided with --vdex-fd."); + } } bool CreateRuntime() { @@ -223,15 +237,26 @@ class DexoptAnalyzer FINAL { } std::unique_ptr<Runtime> runtime(Runtime::Current()); - OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false); + std::unique_ptr<OatFileAssistant> oat_file_assistant; + if (oat_fd_ != -1 && vdex_fd_ != -1) { + oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(), + isa_, + false /*load_executable*/, + vdex_fd_, + oat_fd_); + } else { + oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(), + isa_, + false /*load_executable*/); + } // Always treat elements of the bootclasspath as up-to-date. // TODO(calin): this check should be in OatFileAssistant. - if (oat_file_assistant.IsInBootClassPath()) { + if (oat_file_assistant->IsInBootClassPath()) { return kNoDexOptNeeded; } // TODO(calin): Pass the class loader context as an argument to dexoptanalyzer. b/62269291. - int dexoptNeeded = oat_file_assistant.GetDexOptNeeded( + int dexoptNeeded = oat_file_assistant->GetDexOptNeeded( compiler_filter_, assume_profile_changed_, downgrade_); // Convert OatFileAssitant codes to dexoptanalyzer codes. @@ -258,6 +283,8 @@ class DexoptAnalyzer FINAL { bool assume_profile_changed_; bool downgrade_; std::string image_; + int oat_fd_ = -1; + int vdex_fd_ = -1; }; static int dexoptAnalyze(int argc, char** argv) { diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 3f4cb942fc..734e700c81 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -26,6 +26,7 @@ #include <cstring> #include <sstream> #include <type_traits> +#include <sys/stat.h> // dlopen_ext support from bionic. #ifdef ART_TARGET_ANDROID @@ -105,6 +106,19 @@ class OatFileBase : public OatFile { const char* abs_dex_location, std::string* error_msg); + template <typename kOatFileBaseSubType> + static OatFileBase* OpenOatFile(int vdex_fd, + int oat_fd, + const std::string& vdex_filename, + const std::string& oat_filename, + uint8_t* requested_base, + uint8_t* oat_file_begin, + bool writable, + bool executable, + bool low_4gb, + const char* abs_dex_location, + std::string* error_msg); + protected: OatFileBase(const std::string& filename, bool executable) : OatFile(filename, executable) {} @@ -118,6 +132,12 @@ class OatFileBase : public OatFile { bool low_4gb, std::string* error_msg); + bool LoadVdex(int vdex_fd, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg); + virtual bool Load(const std::string& elf_filename, uint8_t* oat_file_begin, bool writable, @@ -125,6 +145,13 @@ class OatFileBase : public OatFile { bool low_4gb, std::string* error_msg) = 0; + virtual bool Load(int oat_fd, + uint8_t* oat_file_begin, + bool writable, + bool executable, + bool low_4gb, + std::string* error_msg) = 0; + bool ComputeFields(uint8_t* requested_base, const std::string& file_path, std::string* error_msg); @@ -192,6 +219,46 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, return ret.release(); } +template <typename kOatFileBaseSubType> +OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, + int oat_fd, + const std::string& vdex_location, + const std::string& oat_location, + uint8_t* requested_base, + uint8_t* oat_file_begin, + bool writable, + bool executable, + bool low_4gb, + const char* abs_dex_location, + std::string* error_msg) { + std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable)); + + if (kIsVdexEnabled && !ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { + return nullptr; + } + + if (!ret->Load(oat_fd, + oat_file_begin, + writable, + executable, + low_4gb, + error_msg)) { + return nullptr; + } + + if (!ret->ComputeFields(requested_base, oat_location, error_msg)) { + return nullptr; + } + + ret->PreSetup(oat_location); + + if (!ret->Setup(abs_dex_location, error_msg)) { + return nullptr; + } + + return ret.release(); +} + bool OatFileBase::LoadVdex(const std::string& vdex_filename, bool writable, bool low_4gb, @@ -206,6 +273,33 @@ bool OatFileBase::LoadVdex(const std::string& vdex_filename, return true; } +bool OatFileBase::LoadVdex(int vdex_fd, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg) { + if (vdex_fd != -1) { + struct stat s; + int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd, &s)); + if (rc == -1) { + PLOG(WARNING) << "Failed getting length of vdex file"; + } else { + vdex_ = VdexFile::Open(vdex_fd, + s.st_size, + vdex_filename, + writable, + low_4gb, + false /* unquicken */, + error_msg); + if (vdex_.get() == nullptr) { + *error_msg = "Failed opening vdex file."; + return false; + } + } + } + return true; +} + bool OatFileBase::ComputeFields(uint8_t* requested_base, const std::string& file_path, std::string* error_msg) { @@ -712,6 +806,10 @@ class DlOpenOatFile FINAL : public OatFileBase { bool low_4gb, std::string* error_msg) OVERRIDE; + bool Load(int, uint8_t*, bool, bool, bool, std::string*) { + return false; + } + // Ask the linker where it mmaped the file and notify our mmap wrapper of the regions. void PreSetup(const std::string& elf_filename) OVERRIDE; @@ -973,6 +1071,13 @@ class ElfOatFile FINAL : public OatFileBase { bool low_4gb, std::string* error_msg) OVERRIDE; + bool Load(int oat_fd, + uint8_t* oat_file_begin, // Override where the file is loaded to if not null + bool writable, + bool executable, + bool low_4gb, + std::string* error_msg) OVERRIDE; + void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) OVERRIDE { } @@ -1065,6 +1170,31 @@ bool ElfOatFile::Load(const std::string& elf_filename, error_msg); } +bool ElfOatFile::Load(int oat_fd, + uint8_t* oat_file_begin, // Override where the file is loaded to if not null + bool writable, + bool executable, + bool low_4gb, + std::string* error_msg) { + ScopedTrace trace(__PRETTY_FUNCTION__); + if (oat_fd != -1) { + std::unique_ptr<File> file = std::make_unique<File>(oat_fd, false); + file->DisableAutoClose(); + if (file == nullptr) { + *error_msg = StringPrintf("Failed to open oat filename for reading: %s", + strerror(errno)); + return false; + } + return ElfOatFile::ElfFileOpen(file.get(), + oat_file_begin, + writable, + executable, + low_4gb, + error_msg); + } + return false; +} + bool ElfOatFile::ElfFileOpen(File* file, uint8_t* oat_file_begin, bool writable, @@ -1194,6 +1324,33 @@ OatFile* OatFile::Open(const std::string& oat_filename, return with_internal; } +OatFile* OatFile::Open(int vdex_fd, + int oat_fd, + const std::string& oat_location, + uint8_t* requested_base, + uint8_t* oat_file_begin, + bool executable, + bool low_4gb, + const char* abs_dex_location, + std::string* error_msg) { + CHECK(!oat_location.empty()) << oat_location; + + std::string vdex_location = GetVdexFilename(oat_location); + + OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(vdex_fd, + oat_fd, + vdex_location, + oat_location, + requested_base, + oat_file_begin, + false /* writable */, + executable, + low_4gb, + abs_dex_location, + error_msg); + return with_internal; +} + OatFile* OatFile::OpenWritable(File* file, const std::string& location, const char* abs_dex_location, diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 04cb3a0a6e..7d4e6dfd6b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -89,6 +89,18 @@ class OatFile { const char* abs_dex_location, std::string* error_msg); + // Similar to OatFile::Open(const std::string...), but accepts input vdex and + // odex files as file descriptors. + static OatFile* Open(int vdex_fd, + int oat_fd, + const std::string& oat_location, + uint8_t* requested_base, + uint8_t* oat_file_begin, + bool executable, + bool low_4gb, + const char* abs_dex_location, + std::string* error_msg); + // Open an oat file from an already opened File. // Does not use dlopen underneath so cannot be used for runtime use // where relocations may be required. Currently used from diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index f3a0725f79..e3c4cffaa8 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -69,7 +69,9 @@ std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStat OatFileAssistant::OatFileAssistant(const char* dex_location, const InstructionSet isa, - bool load_executable) + bool load_executable, + int vdex_fd, + int oat_fd) : isa_(isa), load_executable_(load_executable), odex_(this, /*is_oat_location*/ false), @@ -109,7 +111,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, std::string error_msg; std::string odex_file_name; if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) { - odex_.Reset(odex_file_name); + odex_.Reset(odex_file_name, vdex_fd, oat_fd); } else { LOG(WARNING) << "Failed to determine odex file name: " << error_msg; } @@ -132,7 +134,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, 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) { + if (access(parent.c_str(), W_OK) == 0 || oat_fd > 0) { dex_parent_writable_ = true; } else { VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror(errno); @@ -1016,11 +1018,28 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { // Check to see if there is a vdex file we can make use of. std::string error_msg; std::string vdex_filename = GetVdexFilename(filename_); - std::unique_ptr<VdexFile> vdex = VdexFile::Open(vdex_filename, - /*writeable*/false, - /*low_4gb*/false, - /*unquicken*/false, - &error_msg); + std::unique_ptr<VdexFile> vdex; + if (vdex_fd_ == -1) { + vdex = VdexFile::Open(vdex_filename, + false /*writeable*/, + false /*low_4gb*/, + false /*unquicken*/, + &error_msg); + } else { + struct stat s; + int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s)); + if (rc == -1) { + PLOG(WARNING) << "Failed getting length of vdex file"; + } else { + vdex = VdexFile::Open(vdex_fd_, + s.st_size, + vdex_filename, + false /*writable*/, + false /*low_4gb*/, + false /* unquicken */, + &error_msg); + } + } if (vdex == nullptr) { status_ = kOatCannotOpen; VLOG(oat) << "unable to open vdex file " << vdex_filename << ": " << error_msg; @@ -1095,14 +1114,26 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() { load_attempted_ = true; if (filename_provided_) { std::string error_msg; - file_.reset(OatFile::Open(filename_.c_str(), - filename_.c_str(), - nullptr, - nullptr, - oat_file_assistant_->load_executable_, - /*low_4gb*/false, - oat_file_assistant_->dex_location_.c_str(), - &error_msg)); + if (oat_fd_ != -1 && vdex_fd_ != -1) { + file_.reset(OatFile::Open(vdex_fd_, + oat_fd_, + filename_.c_str(), + nullptr, + nullptr, + oat_file_assistant_->load_executable_, + false /* low_4gb */, + oat_file_assistant_->dex_location_.c_str(), + &error_msg)); + } else { + file_.reset(OatFile::Open(filename_.c_str(), + filename_.c_str(), + nullptr, + nullptr, + oat_file_assistant_->load_executable_, + false /* low_4gb */, + oat_file_assistant_->dex_location_.c_str(), + &error_msg)); + } if (file_.get() == nullptr) { VLOG(oat) << "OatFileAssistant test for existing oat file " << filename_ << ": " << error_msg; @@ -1169,9 +1200,12 @@ void OatFileAssistant::OatFileInfo::Reset() { status_attempted_ = false; } -void OatFileAssistant::OatFileInfo::Reset(const std::string& filename) { +void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, int vdex_fd, + int oat_fd) { filename_provided_ = true; filename_ = filename; + vdex_fd_ = vdex_fd; + oat_fd_ = oat_fd; Reset(); } diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 6dc3c197b2..0f74ca4b02 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -121,7 +121,9 @@ class OatFileAssistant { // executable code for this dex location. OatFileAssistant(const char* dex_location, const InstructionSet isa, - bool load_executable); + bool load_executable, + int vdex_fd = -1, + int oat_fd = -1); ~OatFileAssistant(); @@ -349,7 +351,7 @@ class OatFileAssistant { // Clear any cached information and switch to getting info about the oat // file with the given filename. - void Reset(const std::string& filename); + void Reset(const std::string& filename, int vdex_fd = -1, int oat_fd = -1); // Release the loaded oat file for runtime use. // Returns null if the oat file hasn't been loaded or is out of date. @@ -386,6 +388,9 @@ class OatFileAssistant { bool filename_provided_ = false; std::string filename_; + int oat_fd_ = -1; + int vdex_fd_ = -1; + bool load_attempted_ = false; std::unique_ptr<OatFile> file_; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 3ecd1b516d..d99036df7e 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -20,6 +20,7 @@ #include <string> #include <vector> +#include <fcntl.h> #include <gtest/gtest.h> @@ -222,6 +223,125 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: Passing valid file descriptors of updated odex/vdex filesalong with +// the dex file. +// Expect: The status is kNoDexOptNeeded. +TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) { + std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; + std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; + std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + odex_location.c_str(), + CompilerFilter::kSpeed, + true, + false, + false); + + android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY)); + android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY)); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + kRuntimeISA, + false, + vdex_fd.get(), + odex_fd.get()); + 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: Passing valid odex fd, however, invalid fd for vdex with +// the dex file. +// Expect: The status is kDex2oatFromScratch. +TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) { + std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; + std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + odex_location.c_str(), + CompilerFilter::kSpeed, + true, + false, + false); + + android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY)); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + kRuntimeISA, + false, + -1, + odex_fd.get()); + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); +} + +// Case: Passing valid vdex fd, however, invalid fd for odex with +// the dex file. +// Expect: The status is kDex2oatFromScratch. +TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) { + std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; + std::string odex_location = GetScratchDir() + "/OatUpToDate.odex"; + std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + odex_location.c_str(), + CompilerFilter::kSpeed, + true, + false, + false); + + android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY)); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + kRuntimeISA, + false, + vdex_fd.get(), + -1); + // 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 + // date with respect to the boot image. Instead we must assume the vdex file + // depends on the boot image and is out of date with respect to the boot + // image. + EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); +} + +// Case: Passing invalid vdex and odex fd with the dex file. +// Expect: The status is kDex2oatFromScratch. +TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) { + std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; + + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + kRuntimeISA, + false, + -1, + -1); + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); +} + // 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. |