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