summaryrefslogtreecommitdiff
path: root/runtime/class_loader_context_test.cc
diff options
context:
space:
mode:
author Calin Juravle <calin@google.com> 2020-12-15 19:13:19 -0800
committer Calin Juravle <calin@google.com> 2021-01-05 21:51:00 +0000
commit6e6f1b2ffb243b3e5ae112bba3cd52031deb31ba (patch)
treedf7254d763ca274ed23b012e47e825a4444d1bfd /runtime/class_loader_context_test.cc
parent8de1fc3a75dcc900aed271a4f654d912a8838c1d (diff)
Do not open dex files in CLC if we only need to get dexopt status
The verifying the class loader context when calling GetDexOptNeeded we only need the dex locations and the checksums. Opening the full dex files may lead to in memory extraction which is expensive and unnecessary. Add a special path in ClassLoaderContext::OpenDexFiles which will extract the locations and the checksums from the apk instead of actually opening the dex files. We re-uses the same logic in OpenDexFiles in order to avoid implementing the opening algorithm twice (which, given all the edge cases is not trivial). Bug: 169869944 Test: test-art-host Change-Id: Ic327889677ce697cd60c5c688281515b932a2a76
Diffstat (limited to 'runtime/class_loader_context_test.cc')
-rw-r--r--runtime/class_loader_context_test.cc305
1 files changed, 193 insertions, 112 deletions
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 987f0d14fd..ec51dee06c 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -142,22 +142,38 @@ class ClassLoaderContextTest : public CommonRuntimeTest {
ClassLoaderContext* context,
size_t index,
std::vector<std::unique_ptr<const DexFile>>* all_dex_files,
- bool classpath_matches_dex_location = true) {
+ bool classpath_matches_dex_location = true,
+ bool only_read_checksums = false) {
ASSERT_TRUE(context != nullptr);
- ASSERT_TRUE(context->dex_files_open_attempted_);
- ASSERT_TRUE(context->dex_files_open_result_);
+ if (only_read_checksums) {
+ ASSERT_EQ(context->dex_files_state_,
+ ClassLoaderContext::ContextDexFilesState::kDexFilesChecksumsRead);
+ } else {
+ ASSERT_EQ(context->dex_files_state_,
+ ClassLoaderContext::ContextDexFilesState::kDexFilesOpened);
+ }
ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index);
ASSERT_EQ(all_dex_files->size(), info.classpath.size());
- ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size());
- size_t cur_open_dex_index = 0;
- for (size_t k = 0; k < all_dex_files->size(); k++) {
- std::unique_ptr<const DexFile>& opened_dex_file =
- info.opened_dex_files[cur_open_dex_index++];
- std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k];
+ ASSERT_EQ(all_dex_files->size(), info.checksums.size());
+ if (only_read_checksums) {
+ ASSERT_EQ(0u, info.opened_dex_files.size());
+ } else {
+ ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size());
+ }
+ for (size_t k = 0, cur_open_dex_index = 0;
+ k < all_dex_files->size();
+ k++, cur_open_dex_index++) {
+ const std::string& opened_location = only_read_checksums
+ ? info.classpath[cur_open_dex_index]
+ : info.opened_dex_files[cur_open_dex_index]->GetLocation();
+ uint32_t opened_checksum = only_read_checksums
+ ? info.checksums[cur_open_dex_index]
+ : info.opened_dex_files[cur_open_dex_index]->GetLocationChecksum();
+
+ std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k];
std::string expected_location = expected_dex_file->GetLocation();
- const std::string& opened_location = opened_dex_file->GetLocation();
if (!IsAbsoluteLocation(opened_location)) {
// If the opened location is relative (it was open from a relative path without a
// classpath_dir) it might not match the expected location which is absolute in tests).
@@ -169,7 +185,7 @@ class ClassLoaderContextTest : public CommonRuntimeTest {
} else {
ASSERT_EQ(expected_location, opened_location);
}
- ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
+ ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_checksum);
if (classpath_matches_dex_location) {
ASSERT_EQ(info.classpath[k], opened_location);
}
@@ -190,8 +206,7 @@ class ClassLoaderContextTest : public CommonRuntimeTest {
void VerifyContextForClassLoader(ClassLoaderContext* context) {
ASSERT_TRUE(context != nullptr);
- ASSERT_TRUE(context->dex_files_open_attempted_);
- ASSERT_TRUE(context->dex_files_open_result_);
+ ASSERT_EQ(context->dex_files_state_, ClassLoaderContext::ContextDexFilesState::kDexFilesOpened);
ASSERT_FALSE(context->owns_the_dex_files_);
ASSERT_FALSE(context->special_shared_library_);
}
@@ -215,8 +230,106 @@ class ClassLoaderContextTest : public CommonRuntimeTest {
}
void PretendContextOpenedDexFiles(ClassLoaderContext* context) {
- context->dex_files_open_attempted_ = true;
- context->dex_files_open_result_ = true;
+ context->dex_files_state_ = ClassLoaderContext::ContextDexFilesState::kDexFilesOpened;
+ }
+
+ void PretendContextOpenedDexFilesForChecksums(ClassLoaderContext* context) {
+ context->dex_files_state_ = ClassLoaderContext::ContextDexFilesState::kDexFilesChecksumsRead;
+ }
+
+ void TestOpenDexFiles(bool only_read_checksums) {
+ std::string multidex_name = GetTestDexFileName("MultiDex");
+ std::string myclass_dex_name = GetTestDexFileName("MyClass");
+ std::string dex_name = GetTestDexFileName("Main");
+
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create(
+ "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
+ "DLC[" + dex_name + "]");
+
+ ASSERT_TRUE(context->OpenDexFiles(
+ /*classpath_dir=*/ "",
+ /*context_fds=*/ std::vector<int>(),
+ only_read_checksums));
+
+ VerifyContextSize(context.get(), 2);
+
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
+ std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+ for (size_t i = 0; i < myclass_dex_files.size(); i++) {
+ all_dex_files0.emplace_back(myclass_dex_files[i].release());
+ }
+ VerifyOpenDexFiles(context.get(),
+ /*index=*/ 0,
+ &all_dex_files0,
+ /*classpath_matches_dex_location=*/ false,
+ only_read_checksums);
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
+ VerifyOpenDexFiles(context.get(),
+ /*index=*/ 1,
+ &all_dex_files1,
+ /*classpath_matches_dex_location=*/ false,
+ only_read_checksums);
+ }
+
+ void TestOpenValidDexFilesRelative(bool use_classpath_dir, bool only_read_checksums) {
+ char cwd_buf[4096];
+ if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) {
+ PLOG(FATAL) << "Could not get working directory";
+ }
+ std::string multidex_name;
+ std::string myclass_dex_name;
+ std::string dex_name;
+ if (!CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf, &multidex_name) ||
+ !CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf, &myclass_dex_name) ||
+ !CreateRelativeString(GetTestDexFileName("Main"), cwd_buf, &dex_name)) {
+ LOG(ERROR) << "Test OpenValidDexFilesRelative cannot be run because target dex files have no "
+ << "relative path.";
+ SUCCEED();
+ return;
+ }
+
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create(
+ "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
+ "DLC[" + dex_name + "]");
+
+ ASSERT_TRUE(context->OpenDexFiles(
+ /*classpath_dir=*/ use_classpath_dir ? cwd_buf : "",
+ /*context_fds=*/ std::vector<int>(),
+ only_read_checksums));
+ VerifyContextSize(context.get(), 2);
+
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
+ std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+ for (size_t i = 0; i < myclass_dex_files.size(); i++) {
+ all_dex_files0.emplace_back(myclass_dex_files[i].release());
+ }
+ VerifyOpenDexFiles(context.get(),
+ /*index=*/ 0,
+ &all_dex_files0,
+ /*classpath_matches_dex_location=*/ false,
+ only_read_checksums);
+
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
+ VerifyOpenDexFiles(context.get(),
+ /*index=*/ 1,
+ &all_dex_files1,
+ /*classpath_matches_dex_location=*/ false,
+ only_read_checksums);
+ }
+
+ // Creates a relative path from cwd to 'in'. Returns false if it cannot be done.
+ // TODO We should somehow support this in all situations. b/72042237.
+ bool CreateRelativeString(const std::string& in, const char* cwd, std::string* out) {
+ int cwd_len = strlen(cwd);
+ if (!android::base::StartsWith(in, cwd) || (cwd_len < 1)) {
+ return false;
+ }
+ bool contains_trailing_slash = (cwd[cwd_len - 1] == '/');
+ int start_position = cwd_len + (contains_trailing_slash ? 0 : 1);
+ *out = in.substr(start_position);
+ return true;
}
private:
@@ -404,113 +517,38 @@ TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) {
ASSERT_FALSE(context->OpenDexFiles("."));
}
-TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
- std::string multidex_name = GetTestDexFileName("MultiDex");
- std::string myclass_dex_name = GetTestDexFileName("MyClass");
- std::string dex_name = GetTestDexFileName("Main");
-
-
+TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFiles) {
std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create(
- "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
- "DLC[" + dex_name + "]");
-
- ASSERT_TRUE(context->OpenDexFiles());
-
- VerifyContextSize(context.get(), 2);
-
- std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
- std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
- for (size_t i = 0; i < myclass_dex_files.size(); i++) {
- all_dex_files0.emplace_back(myclass_dex_files[i].release());
- }
- VerifyOpenDexFiles(context.get(), 0, &all_dex_files0);
+ ClassLoaderContext::Create("PCL[does_not_exist.dex]");
+ VerifyContextSize(context.get(), 1);
+ ASSERT_FALSE(context->OpenDexFiles(
+ /*classpath_dir=*/ ".",
+ /*context_fds=*/ std::vector<int>(),
+ /*only_read_checksums=*/ true));
+}
- std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
- VerifyOpenDexFiles(context.get(), 1, &all_dex_files1);
+TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
+ TestOpenDexFiles(/*only_read_checksums=*/ false);
}
-// Creates a relative path from cwd to 'in'. Returns false if it cannot be done.
-// TODO We should somehow support this in all situations. b/72042237.
-static bool CreateRelativeString(const std::string& in, const char* cwd, std::string* out) {
- int cwd_len = strlen(cwd);
- if (!android::base::StartsWith(in, cwd) || (cwd_len < 1)) {
- return false;
- }
- bool contains_trailing_slash = (cwd[cwd_len - 1] == '/');
- int start_position = cwd_len + (contains_trailing_slash ? 0 : 1);
- *out = in.substr(start_position);
- return true;
+TEST_F(ClassLoaderContextTest, ReadDexFileChecksums) {
+ TestOpenDexFiles(/*only_read_checksums=*/ true);
}
TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) {
- char cwd_buf[4096];
- if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) {
- PLOG(FATAL) << "Could not get working directory";
- }
- std::string multidex_name;
- std::string myclass_dex_name;
- std::string dex_name;
- if (!CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf, &multidex_name) ||
- !CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf, &myclass_dex_name) ||
- !CreateRelativeString(GetTestDexFileName("Main"), cwd_buf, &dex_name)) {
- LOG(ERROR) << "Test OpenValidDexFilesRelative cannot be run because target dex files have no "
- << "relative path.";
- SUCCEED();
- return;
- }
-
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create(
- "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
- "DLC[" + dex_name + "]");
-
- ASSERT_TRUE(context->OpenDexFiles());
-
- std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
- std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
- for (size_t i = 0; i < myclass_dex_files.size(); i++) {
- all_dex_files0.emplace_back(myclass_dex_files[i].release());
- }
- VerifyOpenDexFiles(context.get(), 0, &all_dex_files0);
+ TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ false, /*only_read_checksums=*/ false);
+}
- std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
- VerifyOpenDexFiles(context.get(), 1, &all_dex_files1);
+TEST_F(ClassLoaderContextTest, ReadChecksumsValidDexFilesRelative) {
+ TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ false, /*only_read_checksums=*/ true);
}
TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) {
- char cwd_buf[4096];
- if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) {
- PLOG(FATAL) << "Could not get working directory";
- }
- std::string multidex_name;
- std::string myclass_dex_name;
- std::string dex_name;
- if (!CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf, &multidex_name) ||
- !CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf, &myclass_dex_name) ||
- !CreateRelativeString(GetTestDexFileName("Main"), cwd_buf, &dex_name)) {
- LOG(ERROR) << "Test OpenValidDexFilesClasspathDir cannot be run because target dex files have "
- << "no relative path.";
- SUCCEED();
- return;
- }
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create(
- "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
- "DLC[" + dex_name + "]");
-
- ASSERT_TRUE(context->OpenDexFiles(cwd_buf));
-
- VerifyContextSize(context.get(), 2);
- std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
- std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
- for (size_t i = 0; i < myclass_dex_files.size(); i++) {
- all_dex_files0.emplace_back(myclass_dex_files[i].release());
- }
- VerifyOpenDexFiles(context.get(), 0, &all_dex_files0);
+ TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ true, /*only_read_checksums=*/ false);
+}
- std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
- VerifyOpenDexFiles(context.get(), 1, &all_dex_files1);
+TEST_F(ClassLoaderContextTest, ReadChecksumsValidDexFilesClasspathDir) {
+ TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ true, /*only_read_checksums=*/ true);
}
TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
@@ -520,6 +558,16 @@ TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
ASSERT_FALSE(context->OpenDexFiles());
}
+TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFilesMix) {
+ std::string dex_name = GetTestDexFileName("Main");
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create("PCL[does_not_exist.dex];DLC[" + dex_name + "]");
+ ASSERT_FALSE(context->OpenDexFiles(
+ /*classpath_dir=*/ "",
+ /*context_fds=*/ std::vector<int>(),
+ /*only_read_checksums=*/ true));
+}
+
TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) {
std::unique_ptr<ClassLoaderContext> context;
std::string dex_name = GetTestDexFileName("Main");
@@ -529,6 +577,39 @@ TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) {
ASSERT_FALSE(context->OpenDexFiles("."));
}
+// Verify that we can fully open the dex files after only reading their checksums.
+TEST_F(ClassLoaderContextTest, SubsequentOpenDexFilesOperations) {
+ std::string dex_name = GetTestDexFileName("Main");
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create("PCL[" + dex_name + "]");
+
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("Main");
+
+ ASSERT_TRUE(context->OpenDexFiles(
+ /*classpath_dir=*/ "",
+ /*context_fds=*/ std::vector<int>(),
+ /*only_read_checksums=*/ true));
+
+ VerifyOpenDexFiles(
+ context.get(),
+ /*index=*/ 0,
+ &all_dex_files0,
+ /*classpath_matches_dex_location=*/ false,
+ /*only_read_checksums=*/ true);
+
+ ASSERT_TRUE(context->OpenDexFiles(
+ /*classpath_dir=*/ "",
+ /*context_fds=*/ std::vector<int>(),
+ /*only_read_checksums=*/ false));
+
+ VerifyOpenDexFiles(
+ context.get(),
+ /*index=*/ 0,
+ &all_dex_files0,
+ /*classpath_matches_dex_location=*/ false,
+ /*only_read_checksums=*/ false);
+}
+
TEST_F(ClassLoaderContextTest, CreateClassLoader) {
std::string dex_name = GetTestDexFileName("Main");
std::unique_ptr<ClassLoaderContext> context =
@@ -1393,7 +1474,7 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) {
std::string context_spec = "PCL[]";
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
ASSERT_TRUE(context != nullptr);
- PretendContextOpenedDexFiles(context.get());
+ PretendContextOpenedDexFilesForChecksums(context.get());
// Ensure that the special shared library marks as verified for the first thing in the class path.
ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary),
ClassLoaderContext::VerificationResult::kVerifies);
@@ -1404,7 +1485,7 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
// Pretend that we successfully open the dex files to pass the DCHECKS.
// (as it's much easier to test all the corner cases without relying on actual dex files).
- PretendContextOpenedDexFiles(context.get());
+ PretendContextOpenedDexFilesForChecksums(context.get());
VerifyContextSize(context.get(), 2);
VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");