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";
- dex_location_.assign(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;
+ }
if (load_executable_ && isa != kRuntimeISA) {
LOG(WARNING) << "OatFileAssistant: Load executable specified, "
}
// Get the oat filename.
- if (oat_location != nullptr) {
- oat_.Reset(oat_location);
+ std::string oat_file_name;
+ if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
+ oat_.Reset(oat_file_name);
} else {
- std::string oat_file_name;
- if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
- oat_.Reset(oat_file_name);
- } else {
- LOG(WARNING) << "Failed to determine oat file name for dex location "
+ 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;
+ } else {
+ VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror(errno);
}
}
}
CHECK(error_msg != nullptr);
CHECK(!flock_.HasFile()) << "OatFileAssistant::Lock already acquired";
- 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";
+ // 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";
if (!flock_.Init(lock_file_name.c_str(), error_msg)) {
unlink(lock_file_name.c_str());
CHECK(filter != nullptr);
CHECK(error_msg != nullptr);
- *filter = CompilerFilter::kDefaultCompilerFilter;
+ *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();
case kDex2OatForBootImage:
case kDex2OatForRelocation:
case kDex2OatForFilter:
- return GenerateOatFile(error_msg);
+ return GenerateOatFileNoChecks(info, error_msg);
}
UNREACHABLE();
}
return kOatUpToDate;
}
-OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::GenerateOatFile(std::string* error_msg) {
+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) {
CHECK(error_msg != nullptr);
Runtime* runtime = Runtime::Current();
return kUpdateNotAttempted;
}
- if (oat_.Filename() == nullptr) {
+ 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 = *oat_.Filename();
+ const std::string& oat_file_name = *info.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?
- if (!OS::FileExists(dex_location_.c_str())) {
- *error_msg = "Dex location " + dex_location_ + " does not exists.";
+ 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);
+
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
return kUpdateNotAttempted;
}
- if (fchmod(vdex_file->Fd(), 0644) != 0) {
+ 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;
}
- if (fchmod(oat_file->Fd(), 0644) != 0) {
+ 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.";
oat_file->Erase();
return kUpdateFailed;
}
- // Mark that the oat file has changed and we should try to reload.
- oat_.Reset();
+ // Mark that the odex file has changed and we should try to reload.
+ info.Reset();
return kUpdateSucceeded;
}
InstructionSet isa,
std::string* odex_filename,
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);
- 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;
+ return DexLocationToOdexNames(location, isa, odex_filename, nullptr, nullptr, error_msg);
}
bool OatFileAssistant::DexLocationToOatFilename(const std::string& location,
}
OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
- bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen;
- return use_oat ? oat_ : odex_;
+ // 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_;
}
std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
}
};
+class ScopedNonWritable {
+ public:
+ explicit ScopedNonWritable(const std::string& dex_location) {
+ is_valid_ = false;
+ size_t pos = dex_location.rfind('/');
+ if (pos != std::string::npos) {
+ is_valid_ = true;
+ dex_parent_ = dex_location.substr(0, pos);
+ if (chmod(dex_parent_.c_str(), 0555) != 0) {
+ PLOG(ERROR) << "Could not change permissions on " << dex_parent_;
+ }
+ }
+ }
+
+ bool IsSuccessful() { return is_valid_ && (access(dex_parent_.c_str(), W_OK) != 0); }
+
+ ~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_;
+};
+
+static bool IsExecutedAsRoot() {
+ return geteuid() == 0;
+}
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
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) {
+ 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() + "/OatUpToDate.jar";
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.IsSuccessful());
+
+ 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) {
+ 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 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.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
}
std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar";
- std::string oat_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat";
+ std::string odex_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, oat_location, CompilerFilter::kSpeed);
- ASSERT_EQ(0, unlink(oat_location.c_str()));
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(),
- kRuntimeISA,
- false);
+ 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
// file, we can't know that the vdex depends on the boot image and is up to
if (!kIsVdexEnabled) {
return;
}
+ 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() + "/VdexUpToDateNoOat.jar";
std::string oat_location;
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.IsSuccessful());
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
// Expect: The status is kNoDexOptNeeded if the profile hasn't changed, but
// kDex2Oat if the profile has changed.
TEST_F(OatFileAssistantTest, ProfileOatUpToDate) {
+ if (IsExecutedAsRoot()) {
+ // We cannot simulate non writable locations when executed as root: b/38000545.
+ LOG(ERROR) << "Test skipped because it's running as root";
+ return;
+ }
+
std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
// Case: We have a MultiDEX file and up-to-date OAT file for it.
// Expect: The status is kNoDexOptNeeded and we load all dex files.
TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
+ 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() + "/MultiDexOatUpToDate.jar";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false));
// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+ 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() + "/MultiDexNonMainOutOfDate.jar";
// Compile code for GetMultiDexSrc1.
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false));
EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
}
-// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
+// 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 oat_location = GetOdexDir() + "/RelativeEncodedDexLocation.oat";
+ std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
// Create the dex file
Copy(GetMultiDexSrc1(), 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=" + oat_location);
+ 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(),
- oat_location.c_str(),
- kRuntimeISA, true);
+ 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());
// Case: We have a DEX file and an OAT file out of date with respect to the
// dex checksum.
TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+ if (IsExecutedAsRoot()) {
+ // We cannot simulate non writable locations when executed as root: b/38000545.
+ LOG(ERROR) << "Test skipped because it's running as root";
+ return;
+ }
+
std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
// We create a dex, generate an oat for it, then overwrite the dex with a
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
Copy(GetDexSrc2(), dex_location);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
}
std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar";
- std::string oat_location = GetOdexDir() + "/VdexDexOutOfDate.oat";
+ std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat";
Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
- ASSERT_EQ(0, unlink(oat_location.c_str()));
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
Copy(GetDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(),
- kRuntimeISA,
- false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
}
std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
- std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat";
+ std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex";
Copy(GetMultiDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
- ASSERT_EQ(0, unlink(oat_location.c_str()));
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
Copy(GetMultiDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(),
- kRuntimeISA,
- false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
// Case: We have a DEX file and an OAT file out of date with respect to the
// boot image.
TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+ if (IsExecutedAsRoot()) {
+ // We cannot simulate non writable locations when executed as root: b/38000545.
+ LOG(ERROR) << "Test skipped because it's running as root";
+ return;
+ }
+
std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
Copy(GetDexSrc1(), dex_location);
/*pic*/false,
/*with_alternate_image*/true);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
// It shouldn't matter that the OAT file is out of date, because it is
// verify-at-runtime.
TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+ if (IsExecutedAsRoot()) {
+ // We cannot simulate non writable locations when executed as root: b/38000545.
+ LOG(ERROR) << "Test skipped because it's running as root";
+ return;
+ }
+
std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
Copy(GetDexSrc1(), dex_location);
/*pic*/false,
/*with_alternate_image*/true);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
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 and odex files
+ // Create the dex, the odex and the oat files.
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
-
- // Create the oat file by copying the odex so they are located in the same
- // place in memory.
- Copy(odex_location, oat_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kSpeed,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_alternate_image*/false);
// Verify things don't go bad.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- // kDex2OatForRelocation is expected rather than -kDex2OatForRelocation
- // based on the assumption that the oat location is more up-to-date than the odex
+ // -kDex2OatForRelocation is expected rather than kDex2OatForRelocation
+ // based on the assumption that the odex location is more up-to-date than the oat
// 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());
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) {
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: We should load an executable dex file.
TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
+ 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() + "/LoadOatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
// Load the oat using an oat file assistant.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
// Case: We have a DEX file and up-to-date quicken OAT file for it.
// Expect: We should still load the oat file as executable.
TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
+ 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() + "/LoadExecInterpretOnlyOatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
// Load the oat using an oat file assistant.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: Loading non-executable should load the oat non-executable.
TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
+ 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() + "/LoadNoExecOatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
+
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
// Load the oat using an oat file assistant.
EXPECT_EQ(1u, dex_files.size());
}
-// 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";
+// 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";
- Copy(GetDexSrc1(), dex_location);
+ ScopedNonWritable scoped_non_writable(dex_location);
+ ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ 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, &error_msg)) << error_msg;
-
- 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()));
+ oat_file_assistant.MakeUpToDate(false, &error_msg));
- // Verify it didn't create an oat in the default location.
+ // 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());
-}
-
-// 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));
+ // 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
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");