Ignore checksum mismatch when dex2oat takes multiple profiles.
When we compile the entire bootclasspath with both the ART profile and
the framework profile, a checksum mismatch between the profiles can
happen because the framework profile refers to the old version of the
core libraries, while the ART profile refers to the new version.
This CL fixes the problem by re-defining the behavior: When dex2oat
takes multiple profiles, the order matters: If multiple profiles contain
classes and methods of the same dex file with different checksums, only
the classes and methods from the first profile will be used for that
particular dex file.
Bug: 203492478
Test: atest art_standalone_dex2oat_tests
Test: manual -
1. Install a new ART APEX on an old platform, and reboot.
2. Force JIT Zygote by providing a non-existing boot image path.
3. See the entire bootclasspath compiled successfully:
http://gpaste/6321173605384192
Change-Id: Ib5bda7aec58c1cf39f56c8c8a7a7c3ffb9835737
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 81db30d..123165d 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2351,29 +2351,48 @@
// Dex2oat only uses the reference profile and that is not updated concurrently by the app or
// other processes. So we don't need to lock (as we have to do in profman or when writing the
// profile info).
+ std::vector<std::unique_ptr<File>> profile_files;
if (!profile_file_fds_.empty()) {
for (int fd : profile_file_fds_) {
- std::unique_ptr<File> profile_file(new File(DupCloexec(fd),
- "profile",
- /* check_usage= */ false,
- /* read_only_mode= */ true));
- if (!profile_compilation_info_->Load(profile_file->Fd())) {
- return false;
- }
+ profile_files.push_back(std::make_unique<File>(DupCloexec(fd),
+ "profile",
+ /*check_usage=*/ false,
+ /*read_only_mode=*/ true));
}
} else {
for (const std::string& file : profile_files_) {
- std::unique_ptr<File> profile_file(OS::OpenFileForReading(file.c_str()));
- if (profile_file.get() == nullptr) {
+ profile_files.emplace_back(OS::OpenFileForReading(file.c_str()));
+ if (profile_files.back().get() == nullptr) {
PLOG(ERROR) << "Cannot open profiles";
return false;
}
- if (!profile_compilation_info_->Load(profile_file->Fd())) {
- return false;
- }
}
}
+ std::map<std::string, uint32_t> old_profile_keys, new_profile_keys;
+ auto filter_fn = [&](const std::string& profile_key, uint32_t checksum) {
+ auto it = old_profile_keys.find(profile_key);
+ if (it != old_profile_keys.end() && it->second != checksum) {
+ // Filter out this entry. We have already loaded data for the same profile key with a
+ // different checksum from an earlier profile file.
+ return false;
+ }
+ // Insert the new profile key and checksum.
+ // Note: If the profile contains the same key with different checksums, this insertion fails
+ // but we still return `true` and let the `ProfileCompilationInfo::Load()` report an error.
+ new_profile_keys.insert(std::make_pair(profile_key, checksum));
+ return true;
+ };
+ for (const std::unique_ptr<File>& profile_file : profile_files) {
+ if (!profile_compilation_info_->Load(profile_file->Fd(),
+ /*merge_classes=*/ true,
+ filter_fn)) {
+ return false;
+ }
+ old_profile_keys.merge(new_profile_keys);
+ new_profile_keys.clear();
+ }
+
cleanup.Disable();
return true;
}
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 2e5caf3..6749fc5 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -243,7 +243,11 @@
.IntoKey(M::Passes)
.Define("--profile-file=_")
.WithType<std::vector<std::string>>().AppendValues()
- .WithHelp("Specify profiler output file to use for compilation using a filename.")
+ .WithHelp("Specify profiler output file to use for compilation using a filename.\n"
+ "When multiple profiles are used, the order matters: If multiple profiles \n"
+ "contain classes and methods of the same dex file with different checksums, \n"
+ "only the classes and methods from the first profile will be used for that \n"
+ "particular dex file.")
.IntoKey(M::Profile)
.Define("--profile-file-fd=_")
.WithType<std::vector<int>>().AppendValues()
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 083e1c9..557675d 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -692,7 +692,7 @@
expect_success);
}
- uint64_t GetImageObjectSectionSize(const std::string& image_file_name) {
+ uint32_t GetImageObjectSectionSize(const std::string& image_file_name) {
EXPECT_FALSE(image_file_name.empty());
std::unique_ptr<File> file(OS::OpenFileForReading(image_file_name.c_str()));
CHECK(file != nullptr);
@@ -710,7 +710,7 @@
std::string app_image_file = app_image ? (GetOdexDir() + "/DexOdexNoOat.art"): "";
Copy(GetDexSrc2(), dex_location);
- uint64_t image_file_empty_profile = 0;
+ uint32_t image_file_empty_profile = 0;
if (app_image) {
CompileProfileOdex(dex_location,
odex_location,
@@ -736,7 +736,7 @@
if (app_image) {
// Test that the profile made a difference by adding more classes.
- const uint64_t image_file_small_profile = GetImageObjectSectionSize(app_image_file);
+ const uint32_t image_file_small_profile = GetImageObjectSectionSize(app_image_file);
ASSERT_LT(image_file_empty_profile, image_file_small_profile);
}
}
@@ -873,9 +873,9 @@
}
TEST_F(Dex2oatLayoutTest, TestLayoutMultipleProfiles) {
- std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
- std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
- std::string app_image_file = GetOdexDir() + "/DexOdexNoOat.art";
+ std::string dex_location = GetScratchDir() + "/Dex.jar";
+ std::string odex_location = GetOdexDir() + "/Dex.odex";
+ std::string app_image_file = GetOdexDir() + "/Dex.art";
Copy(GetDexSrc2(), dex_location);
const std::string profile1_location = GetScratchDir() + "/primary.prof";
@@ -885,7 +885,7 @@
app_image_file,
/*use_fd=*/false,
{profile1_location});
- uint64_t image_file_size_profile1 = GetImageObjectSectionSize(app_image_file);
+ uint32_t image_file_size_profile1 = GetImageObjectSectionSize(app_image_file);
const std::string profile2_location = GetScratchDir() + "/secondary.prof";
GenerateProfile(profile2_location, dex_location, /*num_classes=*/1, /*class_offset=*/1);
@@ -894,14 +894,14 @@
app_image_file,
/*use_fd=*/false,
{profile2_location});
- uint64_t image_file_size_profile2 = GetImageObjectSectionSize(app_image_file);
+ uint32_t image_file_size_profile2 = GetImageObjectSectionSize(app_image_file);
CompileProfileOdex(dex_location,
odex_location,
app_image_file,
/*use_fd=*/false,
{profile1_location, profile2_location});
- uint64_t image_file_size_multiple_profiles = GetImageObjectSectionSize(app_image_file);
+ uint32_t image_file_size_multiple_profiles = GetImageObjectSectionSize(app_image_file);
CheckCompilerFilter(dex_location, odex_location, CompilerFilter::Filter::kSpeedProfile);
@@ -911,6 +911,56 @@
ASSERT_GT(image_file_size_multiple_profiles, image_file_size_profile2);
}
+TEST_F(Dex2oatLayoutTest, TestLayoutMultipleProfilesChecksumMismatch) {
+ std::string dex_location = GetScratchDir() + "/Dex.jar";
+
+ // Create two profiles whose dex locations are the same but checksums are different.
+ Copy(GetDexSrc1(), dex_location);
+ const std::string profile_old = GetScratchDir() + "/profile_old.prof";
+ GenerateProfile(profile_old, dex_location, /*num_classes=*/1, /*class_offset=*/0);
+
+ Copy(GetDexSrc2(), dex_location);
+ const std::string profile_new = GetScratchDir() + "/profile_new.prof";
+ GenerateProfile(profile_new, dex_location, /*num_classes=*/1, /*class_offset=*/0);
+
+ // Create an empty profile for reference.
+ const std::string profile_empty = GetScratchDir() + "/profile_empty.prof";
+ GenerateProfile(profile_empty, dex_location, /*num_classes=*/0, /*class_offset=*/0);
+
+ std::string odex_location = GetOdexDir() + "/Dex.odex";
+ std::string app_image_file = GetOdexDir() + "/Dex.art";
+
+ // This should produce a normal image because only `profile_new` is used and it has the right
+ // checksum.
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file,
+ /*use_fd=*/false,
+ {profile_new, profile_old});
+ uint32_t image_size_right_checksum = GetImageObjectSectionSize(app_image_file);
+
+ // This should produce an empty image because only `profile_old` is used and it has the wrong
+ // checksum. Note that dex2oat does not abort compilation when the profile verification fails
+ // (b/62602192, b/65260586).
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file,
+ /*use_fd=*/false,
+ {profile_old, profile_new});
+ uint32_t image_size_wrong_checksum = GetImageObjectSectionSize(app_image_file);
+
+ // Create an empty image using an empty profile for reference.
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file,
+ /*use_fd=*/false,
+ {profile_empty});
+ uint32_t image_size_empty = GetImageObjectSectionSize(app_image_file);
+
+ EXPECT_GT(image_size_right_checksum, image_size_empty);
+ EXPECT_EQ(image_size_wrong_checksum, image_size_empty);
+}
+
TEST_F(Dex2oatLayoutTest, TestVdexLayout) {
RunTestVDex();
}