Replace the static version of GetOptimizationStatus with a factory.
The static version of GetOptimizationStatus is not extensible. We need a
function that can be used in both `GetOptimizationStatus` and
`GetDexOptNeeded` use cases.
Also:
- Add DCHECK to GetOptimizationStatus to make sure it doesn't return
unexpected values.
Bug: 229268202
Test: m test-art-host-gtest-art_runtime_tests
Change-Id: I3c6401c9d52e0766766e3ae7ee19c62af5e35a4a
Merged-In: I71d546ec25703b7ffe3a6f20de3af1cb72f3fc19
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index cf0155f..f7c9e03 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -22,6 +22,7 @@
#include <sstream>
#include "android-base/file.h"
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set.h"
@@ -234,6 +235,47 @@
}
}
+std::unique_ptr<OatFileAssistant> OatFileAssistant::Create(
+ const std::string& filename,
+ const std::string& isa_str,
+ const std::string& context_str,
+ bool load_executable,
+ bool only_load_trusted_executable,
+ std::unique_ptr<RuntimeOptions> runtime_options,
+ std::string* error_msg) {
+ InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
+ if (isa == InstructionSet::kNone) {
+ *error_msg = StringPrintf("Instruction set '%s' is invalid", isa_str.c_str());
+ return nullptr;
+ }
+
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str.c_str());
+ if (context == nullptr) {
+ *error_msg = StringPrintf("Class loader context '%s' is invalid", context_str.c_str());
+ return nullptr;
+ }
+
+ if (!context->OpenDexFiles(android::base::Dirname(filename.c_str()),
+ /*context_fds=*/{},
+ /*only_read_checksums=*/true)) {
+ *error_msg =
+ StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
+ filename.c_str(),
+ context_str.c_str());
+ return nullptr;
+ }
+
+ auto assistant = std::make_unique<OatFileAssistant>(filename.c_str(),
+ isa,
+ context.get(),
+ load_executable,
+ only_load_trusted_executable,
+ std::move(runtime_options));
+
+ assistant->owned_context_ = std::move(context);
+ return assistant;
+}
+
bool OatFileAssistant::UseFdToReadFiles() {
return zip_fd_ >= 0;
}
@@ -1141,57 +1183,6 @@
&out_odex_status);
}
-bool OatFileAssistant::GetOptimizationStatus(const std::string& filename,
- const std::string& isa_str,
- const std::string& context_str,
- std::unique_ptr<RuntimeOptions> runtime_options,
- /*out*/ std::string* compiler_filter,
- /*out*/ std::string* compilation_reason,
- /*out*/ std::string* odex_location,
- /*out*/ std::string* error_msg) {
- InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
- if (isa == InstructionSet::kNone) {
- *error_msg = StringPrintf("Instruction set '%s' is invalid", isa_str.c_str());
- return false;
- }
-
- std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str.c_str());
- if (context == nullptr) {
- *error_msg = StringPrintf("Class loader context '%s' is invalid", context_str.c_str());
- return false;
- }
-
- std::vector<int> context_fds;
- if (!context->OpenDexFiles(android::base::Dirname(filename.c_str()),
- context_fds,
- /*only_read_checksums=*/true)) {
- *error_msg =
- StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
- filename.c_str(),
- context_str.c_str());
- return false;
- }
-
- OatFileAssistant oat_file_assistant(filename.c_str(),
- isa,
- context.get(),
- /*load_executable=*/false,
- /*only_load_trusted_executable=*/true,
- std::move(runtime_options));
-
- // We ignore the odex_status because it is not meaningful. It can never be
- // "boot-image-more-recent" or "context-mismatch". In the case where the boot image has changed or
- // there is a context mismatch, the value is "up-to-date" because the vdex file is still usable.
- // I.e., it can only be either "up-to-date" or "apk-more-recent", which means it doesn't give us
- // information in addition to what we can learn from compiler_filter.
- std::string ignored_odex_status;
-
- oat_file_assistant.GetOptimizationStatus(
- odex_location, compiler_filter, compilation_reason, &ignored_odex_status);
-
- return true;
-}
-
void OatFileAssistant::GetOptimizationStatus(
std::string* out_odex_location,
std::string* out_compilation_filter,
@@ -1218,28 +1209,25 @@
OatStatus status = oat_file_info.Status();
const char* reason = oat_file->GetCompilationReason();
*out_compilation_reason = reason == nullptr ? "unknown" : reason;
+
+ // If the oat file is invalid, the vdex file will be picked, so the status is `kOatUpToDate`. If
+ // the vdex file is also invalid, then either `oat_file` is nullptr, or `status` is
+ // `kOatDexOutOfDate`.
+ DCHECK(status == kOatUpToDate || status == kOatDexOutOfDate);
+
switch (status) {
case kOatUpToDate:
*out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter());
*out_odex_status = "up-to-date";
return;
- case kOatCannotOpen: // This should never happen, but be robust.
- *out_compilation_filter = "error";
- *out_compilation_reason = "error";
- // This mostly happens when we cannot open the vdex file,
- // or the file is corrupt.
- *out_odex_status = "io-error-or-corruption";
- return;
-
+ case kOatCannotOpen:
case kOatBootImageOutOfDate:
- *out_compilation_filter = "run-from-apk-fallback";
- *out_odex_status = "boot-image-more-recent";
- return;
-
case kOatContextOutOfDate:
- *out_compilation_filter = "run-from-apk-fallback";
- *out_odex_status = "context-mismatch";
+ // These should never happen, but be robust.
+ *out_compilation_filter = "unexpected";
+ *out_compilation_reason = "unexpected";
+ *out_odex_status = "unexpected";
return;
case kOatDexOutOfDate:
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 527f473..1198832 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -156,6 +156,17 @@
int oat_fd,
int zip_fd);
+ // A convenient factory function that accepts ISA, class loader context, and compiler filter in
+ // strings. Returns the created instance on success, or returns nullptr and outputs an error
+ // message if it fails to parse the input strings.
+ static std::unique_ptr<OatFileAssistant> Create(const std::string& filename,
+ const std::string& isa_str,
+ const std::string& context_str,
+ bool load_executable,
+ bool only_load_trusted_executable,
+ std::unique_ptr<RuntimeOptions> runtime_options,
+ std::string* error_msg);
+
// Returns true if the dex location refers to an element of the boot class
// path.
bool IsInBootClassPath();
@@ -204,7 +215,7 @@
// - out_compilation_reason: the optimization reason. The reason might
// be "unknown" if the compiler artifacts were not annotated during optimizations.
// - out_odex_status: a human readable refined status of the validity of the odex file.
- // E.g. up-to-date, apk-more-recent.
+ // Possible values are: "up-to-date", "apk-more-recent", and "io-error-no-oat".
//
// This method will try to mimic the runtime effect of loading the dex file.
// For example, if there is no usable oat file, the compiler filter will be set
@@ -220,18 +231,6 @@
std::string* out_compilation_reason,
std::unique_ptr<RuntimeOptions> runtime_options = nullptr);
- // A convenient version of `GetOptimizationStatus` that accepts ISA and class loader context in
- // strings. Returns true on success, or returns false and outputs an error message if it fails to
- // parse the input strings.
- static bool GetOptimizationStatus(const std::string& filename,
- const std::string& isa_str,
- const std::string& context_str,
- std::unique_ptr<RuntimeOptions> runtime_options,
- /*out*/ std::string* compiler_filter,
- /*out*/ std::string* compilation_reason,
- /*out*/ std::string* odex_location,
- /*out*/ std::string* error_msg);
-
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
@@ -472,6 +471,7 @@
std::string dex_location_;
ClassLoaderContext* context_;
+ std::unique_ptr<ClassLoaderContext> owned_context_;
// Whether or not the parent directory of the dex file is writable.
bool dex_parent_writable_ = false;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index c0662aa..e414e4e 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -88,26 +88,7 @@
ASSERT_EQ(expected_reason, compilation_reason1);
}
- // Verify the static method (called from artd).
- std::string compilation_filter2;
- std::string compilation_reason2;
- std::string odex_location2; // ignored
- std::string error_msg; // ignored
-
- ASSERT_TRUE(
- OatFileAssistant::GetOptimizationStatus(file,
- GetInstructionSetString(kRuntimeISA),
- context->EncodeContextForDex2oat(/*base_dir=*/""),
- MaybeCreateRuntimeOptions(),
- &compilation_filter2,
- &compilation_reason2,
- &odex_location2,
- &error_msg));
-
- ASSERT_EQ(expected_filter_name, compilation_filter2);
- ASSERT_EQ(expected_reason, compilation_reason2);
-
- // Verify the instance methods (called at runtime).
+ // Verify the instance methods (called at runtime and from artd).
OatFileAssistant assistant = CreateOatFileAssistant(file.c_str(), context);
std::string odex_location3; // ignored
@@ -1796,6 +1777,29 @@
}
}
+TEST_P(OatFileAssistantTest, Create) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::string error_msg;
+ std::unique_ptr<OatFileAssistant> oat_file_assistant =
+ OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeCreateRuntimeOptions(),
+ &error_msg);
+ ASSERT_NE(oat_file_assistant, nullptr);
+
+ // Verify that the created instance is usable.
+ VerifyOptimizationStatus(dex_location, default_context_.get(), "speed", "install", "up-to-date");
+}
+
TEST_P(OatFileAssistantTest, ErrorOnInvalidIsaString) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
@@ -1804,19 +1808,15 @@
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- std::string ignored_compilation_filter;
- std::string ignored_compilation_reason;
- std::string ignored_odex_location;
std::string error_msg;
- EXPECT_FALSE(OatFileAssistant::GetOptimizationStatus(
- dex_location,
- /*isa_str=*/"foo",
- default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
- MaybeCreateRuntimeOptions(),
- &ignored_compilation_filter,
- &ignored_compilation_reason,
- &ignored_odex_location,
- &error_msg));
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ /*isa_str=*/"foo",
+ default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeCreateRuntimeOptions(),
+ &error_msg),
+ nullptr);
EXPECT_EQ(error_msg, "Instruction set 'foo' is invalid");
}
@@ -1828,18 +1828,15 @@
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- std::string ignored_compilation_filter;
- std::string ignored_compilation_reason;
- std::string ignored_odex_location;
std::string error_msg;
- EXPECT_FALSE(OatFileAssistant::GetOptimizationStatus(dex_location,
- GetInstructionSetString(kRuntimeISA),
- /*context_str=*/"foo",
- MaybeCreateRuntimeOptions(),
- &ignored_compilation_filter,
- &ignored_compilation_reason,
- &ignored_odex_location,
- &error_msg));
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/"foo",
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeCreateRuntimeOptions(),
+ &error_msg),
+ nullptr);
EXPECT_EQ(error_msg, "Class loader context 'foo' is invalid");
}
@@ -1856,19 +1853,15 @@
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- std::string ignored_compilation_filter;
- std::string ignored_compilation_reason;
- std::string ignored_odex_location;
std::string error_msg;
- EXPECT_FALSE(
- OatFileAssistant::GetOptimizationStatus(dex_location,
- GetInstructionSetString(kRuntimeISA),
- /*context_str=*/"PCL[" + context_location + "]",
- MaybeCreateRuntimeOptions(),
- &ignored_compilation_filter,
- &ignored_compilation_reason,
- &ignored_odex_location,
- &error_msg));
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/"PCL[" + context_location + "]",
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeCreateRuntimeOptions(),
+ &error_msg),
+ nullptr);
EXPECT_EQ(error_msg,
"Failed to load class loader context files for '" + dex_location +
"' with context 'PCL[" + context_location + "]'");