summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ulya Trafimovich <skvadrik@google.com> 2024-10-14 20:19:02 +0100
committer Ulya Trofimovich <skvadrik@google.com> 2024-10-18 14:07:03 +0000
commite7fffca5a424b368b44a6c84e6003be3524f2088 (patch)
treecbd0086d8f70ac2bfd6d5d5c5e1ce7b243f3e78f
parent1ef51a583cd9cd4e2f7a0aae0161dafe231df34c (diff)
Don't crash when generating image for nonexistent dex files.
Add more fine-grained status to dex2oat gtests, so that we can distingush between a compilation error (e.g. dex2oat crash) and a later error when trying to open the generated oat file. Test: m test-art-host-gtest Test: Added a new gtest that failed (dex2oat crashed) without the fix: m art_dex2oat_tests out/host/linux-x86/nativetest/art_dex2oat_tests/art_dex2oat_tests \ --gtest_filter='*AppImageNonexistentDex*' Change-Id: I65ae9badfc552ecf90ffde63f923bfb914c6c773
-rw-r--r--dex2oat/dex2oat_test.cc113
-rw-r--r--dex2oat/linker/image_writer.cc26
2 files changed, 82 insertions, 57 deletions
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index ddc91323dc..f81345b345 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -68,6 +68,8 @@ using ::testing::Not;
class Dex2oatTest : public Dex2oatEnvironmentTest {
public:
+ enum class Status { kFailCompile, kFailOpenOat, kSuccess };
+
void TearDown() override {
Dex2oatEnvironmentTest::TearDown();
@@ -122,14 +124,14 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
const std::string& odex_location,
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args = {},
- bool expect_success = true,
+ Status expect_status = Status::kSuccess,
bool use_fd = false,
bool use_zip_fd = false) WARN_UNUSED {
return GenerateOdexForTest(dex_location,
odex_location,
filter,
extra_args,
- expect_success,
+ expect_status,
use_fd,
use_zip_fd,
[](const OatFile&) {});
@@ -142,7 +144,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
const std::string& odex_location,
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args,
- bool expect_success,
+ Status expect_status,
bool use_fd,
bool use_zip_fd,
T check_oat) WARN_UNUSED {
@@ -163,7 +165,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
GenerateOdexForTestWithStatus(dex_locations, odex_location, filter, extra_args, use_fd);
bool success = status.ok() && status.value() == 0;
- if (expect_success) {
+ if (expect_status != Status::kFailCompile) {
if (!success) {
return AssertionFailure() << "Failed to compile odex ("
<< (status.ok() ? StringPrintf("status=%d", status.value()) :
@@ -180,6 +182,13 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
/*low_4gb=*/false,
dex_location,
&error_msg));
+
+ if (expect_status == Status::kFailOpenOat) {
+ return (odex_file == nullptr) ?
+ AssertionSuccess() :
+ AssertionFailure() << "Unexpectedly was able to open odex file";
+ }
+
if (odex_file == nullptr) {
return AssertionFailure() << "Could not open odex file: " << error_msg;
}
@@ -649,7 +658,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
bool use_fd,
const std::vector<std::string>& profile_locations,
const std::vector<std::string>& extra_args = {},
- bool expect_success = true) {
+ Status expect_status = Status::kSuccess) {
std::vector<std::string> copy(extra_args);
for (const std::string& profile_location : profile_locations) {
copy.push_back("--profile-file=" + profile_location);
@@ -664,7 +673,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
}
}
ASSERT_TRUE(GenerateOdexForTest(
- dex_location, odex_location, CompilerFilter::kSpeedProfile, copy, expect_success, use_fd));
+ dex_location, odex_location, CompilerFilter::kSpeedProfile, copy, expect_status, use_fd));
if (app_image_file != nullptr) {
ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file";
}
@@ -678,7 +687,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
bool use_fd,
size_t num_profile_classes,
const std::vector<std::string>& extra_args = {},
- bool expect_success = true) {
+ Status expect_status = Status::kSuccess) {
const std::string profile_location = GetScratchDir() + "/primary.prof";
GenerateProfile(profile_location, dex_location, num_profile_classes);
CompileProfileOdex(dex_location,
@@ -687,7 +696,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
use_fd,
{profile_location},
extra_args,
- expect_success);
+ expect_status);
}
uint32_t GetImageObjectSectionSize(const std::string& image_file_name) {
@@ -784,7 +793,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
/*use_fd=*/true,
/*num_profile_classes=*/1,
{input_vdex, output_vdex},
- /*expect_success=*/true);
+ /*expect_status=*/Status::kSuccess);
EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u);
}
ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
@@ -877,7 +886,7 @@ TEST_F(Dex2oatLayoutTest, TestLayoutAppImageMissingBootImage) {
/*use_fd=*/false,
/*num_profile_classes=*/1,
/*extra_args=*/{"--boot-image=/nonx/boot.art"},
- /*expect_success=*/true);
+ /*expect_status=*/Status::kSuccess);
// Verify the odex file does not require an image.
std::string error_msg;
@@ -987,7 +996,7 @@ TEST_F(Dex2oatLayoutTest, TestVdexLayout) { RunTestVDex(); }
class Dex2oatWatchdogTest : public Dex2oatTest {
protected:
- void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) {
+ void RunTest(Status expect_status, const std::vector<std::string>& extra_args = {}) {
std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
@@ -999,7 +1008,7 @@ class Dex2oatWatchdogTest : public Dex2oatTest {
copy.push_back("--swap-file=" + swap_location);
copy.push_back("-j512"); // Excessive idle threads just slow down dex2oat.
ASSERT_TRUE(GenerateOdexForTest(
- dex_location, odex_location, CompilerFilter::kSpeed, copy, expect_success));
+ dex_location, odex_location, CompilerFilter::kSpeed, copy, expect_status));
}
std::string GetTestDexFileName() { return GetDexSrc1(); }
@@ -1007,10 +1016,10 @@ class Dex2oatWatchdogTest : public Dex2oatTest {
TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) {
// Check with default.
- RunTest(/*expect_success=*/true);
+ RunTest(/*expect_status=*/Status::kSuccess);
// Check with ten minutes.
- RunTest(/*expect_success=*/true, {"--watchdog-timeout=600000"});
+ RunTest(/*expect_status=*/Status::kSuccess, {"--watchdog-timeout=600000"});
}
TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) {
@@ -1024,14 +1033,14 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) {
test_accepts_odex_file_on_failure = true;
// Check with ten milliseconds.
- RunTest(/*expect_success=*/false, {"--watchdog-timeout=10"});
+ RunTest(/*expect_status=*/Status::kFailCompile, {"--watchdog-timeout=10"});
}
class Dex2oatClassLoaderContextTest : public Dex2oatTest {
protected:
void RunTest(const char* class_loader_context,
const char* expected_classpath_key,
- bool expect_success,
+ Status expect_status,
bool use_second_source = false,
bool generate_image = false) {
std::string dex_location = GetUsedDexLocation();
@@ -1057,7 +1066,7 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest {
odex_location,
CompilerFilter::kVerify,
extra_args,
- expect_success,
+ expect_status,
/*use_fd=*/false,
/*use_zip_fd=*/false,
check_oat));
@@ -1073,16 +1082,16 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest {
};
TEST_F(Dex2oatClassLoaderContextTest, InvalidContext) {
- RunTest("Invalid[]", /*expected_classpath_key=*/nullptr, /*expect_success=*/false);
+ RunTest("Invalid[]", /*expected_classpath_key=*/nullptr, /*expect_status=*/Status::kFailCompile);
}
TEST_F(Dex2oatClassLoaderContextTest, EmptyContext) {
- RunTest("PCL[]", kEmptyClassPathKey, /*expect_success=*/true);
+ RunTest("PCL[]", kEmptyClassPathKey, /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ContextWithTheSourceDexFiles) {
std::string context = "PCL[" + GetUsedDexLocation() + "]";
- RunTest(context.c_str(), kEmptyClassPathKey, /*expect_success=*/true);
+ RunTest(context.c_str(), kEmptyClassPathKey, /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) {
@@ -1093,7 +1102,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) {
std::string context = "PCL[" + dex_files[0]->GetLocation() + "]";
std::string expected_classpath_key =
"PCL[" + dex_files[0]->GetLocation() + "*" + std::to_string(expected_checksum) + "]";
- RunTest(context.c_str(), expected_classpath_key.c_str(), /*expect_success=*/true);
+ RunTest(context.c_str(), expected_classpath_key.c_str(), /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ContextWithResourceOnlyDexFiles) {
@@ -1102,13 +1111,13 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithResourceOnlyDexFiles) {
std::string context = "PCL[" + resource_only_classpath + "]";
// Expect an empty context because resource only dex files cannot be open.
- RunTest(context.c_str(), kEmptyClassPathKey, /*expect_success=*/true);
+ RunTest(context.c_str(), kEmptyClassPathKey, /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ContextWithNotExistentDexFiles) {
std::string context = "PCL[does_not_exists.dex]";
// Expect an empty context because stripped dex files cannot be open.
- RunTest(context.c_str(), kEmptyClassPathKey, /*expect_success=*/true);
+ RunTest(context.c_str(), kEmptyClassPathKey, /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ChainContext) {
@@ -1120,7 +1129,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ChainContext) {
std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "];" +
"DLC[" + CreateClassPathWithChecksums(dex_files2) + "]";
- RunTest(context.c_str(), expected_classpath_key.c_str(), /*expect_success=*/true);
+ RunTest(context.c_str(), expected_classpath_key.c_str(), /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) {
@@ -1131,7 +1140,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) {
"PCL[" + GetTestDexFileName("Nested") + "]" + "{PCL[" + GetTestDexFileName("MultiDex") + "]}";
std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" +
"{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}";
- RunTest(context.c_str(), expected_classpath_key.c_str(), /*expect_success=*/true);
+ RunTest(context.c_str(), expected_classpath_key.c_str(), /*expect_status=*/Status::kSuccess);
}
TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibraryAndImage) {
@@ -1144,7 +1153,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibraryAndImage) {
"{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}";
RunTest(context.c_str(),
expected_classpath_key.c_str(),
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_second_source=*/false,
/*generate_image=*/true);
}
@@ -1161,7 +1170,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSameSharedLibrariesAndImage) {
"#PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}";
RunTest(context.c_str(),
expected_classpath_key.c_str(),
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_second_source=*/false,
/*generate_image=*/true);
}
@@ -1178,7 +1187,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrariesDependenciesAndI
"{PCL[" + CreateClassPathWithChecksums(dex_files1) + "]}}";
RunTest(context.c_str(),
expected_classpath_key.c_str(),
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_second_source=*/false,
/*generate_image=*/true);
}
@@ -1270,7 +1279,7 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) {
base_oat_name,
CompilerFilter::Filter::kSpeed,
{"--deduplicate-code=false"},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[&no_dedupe_size](const OatFile& o) { no_dedupe_size = o.Size(); }));
@@ -1280,7 +1289,7 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) {
base_oat_name,
CompilerFilter::Filter::kSpeed,
{"--deduplicate-code=true"},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[&dedupe_size](const OatFile& o) { dedupe_size = o.Size(); }));
@@ -1296,7 +1305,7 @@ TEST_F(Dex2oatTest, UncompressedTest) {
base_oat_name,
CompilerFilter::Filter::kVerify,
{},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile& o) { CHECK(!o.ContainsDexCode()); }));
@@ -1349,7 +1358,7 @@ TEST_F(Dex2oatTest, StderrLoggerOutput) {
odex_location,
CompilerFilter::kVerify,
{"--runtime-arg", "-Xuse-stderr-logger"},
- /*expect_success=*/true));
+ /*expect_status=*/Status::kSuccess));
// Look for some random part of dex2oat logging. With the stderr logger this should be captured,
// even on device.
EXPECT_NE(std::string::npos, output_.find("dex2oat took"));
@@ -1366,7 +1375,7 @@ TEST_F(Dex2oatTest, VerifyCompilationReason) {
odex_location,
CompilerFilter::kVerify,
{"--compilation-reason=install"},
- /*expect_success=*/true));
+ /*expect_status=*/Status::kSuccess));
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1,
odex_location,
@@ -1390,7 +1399,7 @@ TEST_F(Dex2oatTest, VerifyNoCompilationReason) {
odex_location,
CompilerFilter::kVerify,
/*extra_args=*/{},
- /*expect_success=*/true));
+ /*expect_status=*/Status::kSuccess));
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1,
odex_location,
@@ -1414,7 +1423,7 @@ TEST_F(Dex2oatTest, DontExtract) {
odex_location,
CompilerFilter::Filter::kVerify,
{"--copy-dex-files=false"},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile&) {}));
@@ -1475,7 +1484,7 @@ TEST_F(Dex2oatTest, DontExtract) {
// target.
"--runtime-arg",
"-Xuse-stderr-logger"},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile& o) { CHECK(o.ContainsDexCode()); }));
@@ -1591,7 +1600,7 @@ TEST_F(Dex2oatWithExpectedFilterTest, AppImageNoProfile) {
odex_location,
CompilerFilter::Filter::kSpeedProfile,
{"--app-image-fd=" + std::to_string(app_image_file.GetFd())},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile&) {}));
@@ -1627,7 +1636,7 @@ TEST_F(Dex2oatTest, ZipFd) {
base_oat_name,
CompilerFilter::Filter::kVerify,
extra_args,
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/true));
}
@@ -1685,7 +1694,7 @@ TEST_F(Dex2oatWithExpectedFilterTest, AppImageEmptyDex) {
{"--app-image-file=" + app_image_location,
"--resolve-startup-const-strings=true",
"--profile-file=" + profile_file.GetFilename()},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile&) {}));
@@ -1700,6 +1709,20 @@ TEST_F(Dex2oatWithExpectedFilterTest, AppImageEmptyDex) {
ASSERT_TRUE(odex_file != nullptr);
}
+TEST_F(Dex2oatWithExpectedFilterTest, AppImageNonexistentDex) {
+ const std::string out_dir = GetScratchDir();
+ // Test that dex2oat does not crash trying to compile app image with zero DEX files.
+ ASSERT_TRUE(GenerateOdexForTest(
+ out_dir + "/base.apk",
+ out_dir + "/base.odex",
+ CompilerFilter::Filter::kSpeedProfile,
+ {"--dex-file=nonexistent.apk", "--app-image-file=" + out_dir + "/base.art"},
+ /*expect_status=*/Status::kFailOpenOat,
+ /*use_fd=*/false,
+ /*use_zip_fd=*/false,
+ [](const OatFile&) {}));
+}
+
TEST_F(Dex2oatTest, DexFileFd) {
std::string error_msg;
std::string zip_location = GetTestDexFileName("Main");
@@ -1730,7 +1753,7 @@ TEST_F(Dex2oatTest, DexFileFd) {
base_oat_name,
CompilerFilter::Filter::kVerify,
extra_args,
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/true));
}
@@ -1746,7 +1769,7 @@ TEST_F(Dex2oatTest, DontCopyPlainDex) {
odex_location,
CompilerFilter::Filter::kVerify,
/*extra_args=*/{},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile&) {}));
@@ -1831,7 +1854,7 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) {
{"--app-image-file=" + app_image_location,
"--resolve-startup-const-strings=true",
"--profile-file=" + profile_file.GetFilename()},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[](const OatFile&) {}));
@@ -1906,7 +1929,7 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) {
odex_location,
CompilerFilter::Filter::kVerify,
{"--class-loader-context=" + stored_context},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[&](const OatFile& oat_file) {
@@ -1922,7 +1945,7 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) {
CompilerFilter::Filter::kVerify,
{"--class-loader-context=" + valid_context,
"--stored-class-loader-context=" + stored_context},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false,
[&](const OatFile& oat_file) {
@@ -1990,7 +2013,7 @@ TEST_F(Dex2oatTest, LoadOutOfDateOatFile) {
base_oat_name,
CompilerFilter::Filter::kSpeed,
{"--deduplicate-code=false"},
- /*expect_success=*/true,
+ /*expect_status=*/Status::kSuccess,
/*use_fd=*/false,
/*use_zip_fd=*/false));
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 41cf1edaf8..f507fe1f38 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1715,20 +1715,22 @@ class ImageWriter::LayoutHelper::CollectClassesVisitor {
auto app_class_loader = DecodeGlobalWithoutRB<mirror::ClassLoader>(
vm, image_writer->app_class_loader_);
ClassTable* app_class_table = app_class_loader->GetClassTable();
- ReaderMutexLock lock(self, app_class_table->lock_);
- DCHECK_EQ(app_class_table->classes_.size(), 1u);
- const ClassTable::ClassSet& app_class_set = app_class_table->classes_[0];
- DCHECK_GE(app_class_set.size(), image_info.class_table_size_);
- boot_image_classes.reserve(app_class_set.size() - image_info.class_table_size_);
- for (const ClassTable::TableSlot& slot : app_class_set) {
- mirror::Class* klass = slot.Read<kWithoutReadBarrier>().Ptr();
- if (image_writer->IsInBootImage(klass)) {
- boot_image_classes.push_back(klass);
+ if (app_class_table != nullptr) {
+ ReaderMutexLock lock(self, app_class_table->lock_);
+ DCHECK_EQ(app_class_table->classes_.size(), 1u);
+ const ClassTable::ClassSet& app_class_set = app_class_table->classes_[0];
+ DCHECK_GE(app_class_set.size(), image_info.class_table_size_);
+ boot_image_classes.reserve(app_class_set.size() - image_info.class_table_size_);
+ for (const ClassTable::TableSlot& slot : app_class_set) {
+ mirror::Class* klass = slot.Read<kWithoutReadBarrier>().Ptr();
+ if (image_writer->IsInBootImage(klass)) {
+ boot_image_classes.push_back(klass);
+ }
}
+ DCHECK_EQ(app_class_set.size() - image_info.class_table_size_, boot_image_classes.size());
+ // Increase the app class table size to include referenced boot image classes.
+ image_info.class_table_size_ = app_class_set.size();
}
- DCHECK_EQ(app_class_set.size() - image_info.class_table_size_, boot_image_classes.size());
- // Increase the app class table size to include referenced boot image classes.
- image_info.class_table_size_ = app_class_set.size();
}
for (ImageInfo& image_info : image_writer->image_infos_) {
if (image_info.class_table_size_ != 0u) {