Provide an option to pass odex and vdex fds in dexoptanalyzer
Test: test-art-host-gtest-oat_file_assistant
Bug: 67111829
Change-Id: I10b23e665987d4a4a7d0eab67f11bda3d5809554
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 51a67ca..08d38d5 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -97,6 +97,10 @@
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 @@
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 @@
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 @@
}
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 @@
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 3f4cb94..734e700 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 04cb3a0..7d4e6df 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -89,6 +89,18 @@
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 f3a0725..e3c4cff 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -69,7 +69,9 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 6dc3c19..0f74ca4 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -121,7 +121,9 @@
// 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 @@
// 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 @@
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 3ecd1b5..d99036d 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 @@
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.