diff options
author | Jiakai Zhang <jiakaiz@google.com> | 2023-11-28 16:55:47 +0000 |
---|---|---|
committer | Cherrypicker Worker <android-build-cherrypicker-worker@google.com> | 2023-11-30 11:10:34 +0000 |
commit | 157c0d834f75e4e8de3a42ce783ffbd93c1f04be (patch) | |
tree | bf8d1d91432e9f5bb38cac09e11b9e2694a914ca | |
parent | 4ab7a5df1ce72cceae1a28e2dfc960805e3b1186 (diff) | |
download | art-157c0d834f75e4e8de3a42ce783ffbd93c1f04be.tar.gz |
Add a new profman flag --force-merge-and-analyze.
When the flag is set, profman merges profiles even if the difference
between before and after the merge doesn't meet the thresholds, and
returns `kCompile` if there is any difference or
`kSkipCompilationSmallDelta` if there is no difference.
The behavior of this flag is the same as --force-merge, except for that
it returns different codes to indicate whether there is any difference
or not. We can't change the return code of --force-merge because
installd on S and T relies on it to be `kSuccess`.
Bug: 242170869
Test: m test-art-host-gtest-art_profman_tests
(cherry picked from https://android-review.googlesource.com/q/commit:610a5839080aebefc9de95fdc90cdc3d847848b2)
Merged-In: I59fdc64a66284ec66c4b7a7c355df9e902dfee00
Change-Id: I59fdc64a66284ec66c4b7a7c355df9e902dfee00
-rw-r--r-- | artd/artd.cc | 12 | ||||
-rw-r--r-- | artd/artd_test.cc | 17 | ||||
-rw-r--r-- | artd/binder/com/android/server/art/MergeProfileOptions.aidl | 2 | ||||
-rw-r--r-- | profman/include/profman/profman_result.h | 10 | ||||
-rw-r--r-- | profman/profile_assistant.cc | 37 | ||||
-rw-r--r-- | profman/profile_assistant.h | 12 | ||||
-rw-r--r-- | profman/profile_assistant_test.cc | 189 | ||||
-rw-r--r-- | profman/profman.cc | 9 |
8 files changed, 216 insertions, 72 deletions
diff --git a/artd/artd.cc b/artd/artd.cc index 6132a22eab..04a01a9ca4 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -719,10 +719,9 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil OR_RETURN_NON_FATAL(NewFile::Create(output_profile_path, in_outputProfile->fsPermission)); if (in_referenceProfile.has_value()) { - if (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) { + if (in_options.dumpOnly || in_options.dumpClassesAndMethods) { return Fatal( - "Reference profile must not be set when 'forceMerge', 'dumpOnly', or " - "'dumpClassesAndMethods' is set"); + "Reference profile must not be set when 'dumpOnly' or 'dumpClassesAndMethods' is set"); } std::string reference_profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(*in_referenceProfile)); @@ -755,7 +754,7 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil props_->GetOrEmpty("dalvik.vm.bgdexopt.new-classes-percent")) .AddIfNonEmpty("--min-new-methods-percent-change=%s", props_->GetOrEmpty("dalvik.vm.bgdexopt.new-methods-percent")) - .AddIf(in_options.forceMerge, "--force-merge") + .AddIf(in_options.forceMerge, "--force-merge-and-analyze") .AddIf(in_options.forBootImage, "--boot-image-merge"); } @@ -778,9 +777,8 @@ ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profil } ProfmanResult::ProcessingResult expected_result = - (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) ? - ProfmanResult::kSuccess : - ProfmanResult::kCompile; + (in_options.dumpOnly || in_options.dumpClassesAndMethods) ? ProfmanResult::kSuccess : + ProfmanResult::kCompile; if (result.value() != expected_result) { return NonFatal(ART_FORMAT("profman returned an unexpected code: {}", result.value())); } diff --git a/artd/artd_test.cc b/artd/artd_test.cc index 5b6c5b2767..f1806e1c2d 100644 --- a/artd/artd_test.cc +++ b/artd/artd_test.cc @@ -1756,7 +1756,7 @@ TEST_F(ArtdTest, mergeProfiles) { Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))), Contains(Flag("--apk-fd=", FdOf(dex_file_1))), Contains(Flag("--apk-fd=", FdOf(dex_file_2))), - Not(Contains("--force-merge")), + Not(Contains("--force-merge-and-analyze")), Not(Contains("--boot-image-merge")))), HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")), _, @@ -1872,13 +1872,14 @@ TEST_F(ArtdTest, mergeProfilesWithOptionsForceMerge) { CreateFile(dex_file_); - EXPECT_CALL( - *mock_exec_utils_, - DoExecAndReturnCode( - WhenSplitBy("--", _, AllOf(Contains("--force-merge"), Contains("--boot-image-merge"))), - _, - _)) - .WillOnce(Return(ProfmanResult::kSuccess)); + EXPECT_CALL(*mock_exec_utils_, + DoExecAndReturnCode(WhenSplitBy("--", + _, + AllOf(Contains("--force-merge-and-analyze"), + Contains("--boot-image-merge"))), + _, + _)) + .WillOnce(Return(ProfmanResult::kCompile)); bool result; EXPECT_TRUE(artd_ diff --git a/artd/binder/com/android/server/art/MergeProfileOptions.aidl b/artd/binder/com/android/server/art/MergeProfileOptions.aidl index 2d007f9406..5d3591ccf2 100644 --- a/artd/binder/com/android/server/art/MergeProfileOptions.aidl +++ b/artd/binder/com/android/server/art/MergeProfileOptions.aidl @@ -28,7 +28,7 @@ package com.android.server.art; * @hide */ parcelable MergeProfileOptions { - /** --force-merge */ + /** --force-merge-and-analyze */ boolean forceMerge; /** --boot-image-merge */ boolean forBootImage; diff --git a/profman/include/profman/profman_result.h b/profman/include/profman/profman_result.h index 9c9aca9e05..c4ca98828d 100644 --- a/profman/include/profman/profman_result.h +++ b/profman/include/profman/profman_result.h @@ -40,9 +40,13 @@ class ProfmanResult { kSuccess = 0, // A merge has been performed, meaning the reference profile has been changed. kCompile = 1, - // `--profile-file(-fd)` is not specified, or the specified profiles are outdated (i.e., APK - // filename or checksum mismatch), empty, or don't contain enough number of new classes and - // methods that meets the threshold to trigger a merge. + // One of the following conditions is met: + // - `--profile-file(-fd)` is not specified. + // - The specified profiles are outdated (i.e., APK filename or checksum mismatch). + // - The specified profiles are empty. + // - The specified profiles don't contain any new class or method. + // - The specified profiles don't contain enough number of new classes and methods that meets + // the threshold to trigger a merge, and `--force-merge-and-analyze` is not set. kSkipCompilationSmallDelta = 2, // All the input profiles (including the reference profile) are either outdated (i.e., APK // filename or checksum mismatch) or empty. diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc index abbde2d527..4d98e4927d 100644 --- a/profman/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -54,7 +54,7 @@ ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal( ProfileCompilationInfo cur_info(options.IsBootImageMerge()); if (!cur_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) { LOG(WARNING) << "Could not load profile file at index " << i; - if (options.IsForceMerge()) { + if (options.IsForceMerge() || options.IsForceMergeAndAnalyze()) { // If we have to merge forcefully, ignore load failures. // This is useful for boot image profiles to ignore stale profiles which are // cleared lazily. @@ -79,17 +79,30 @@ ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal( if (info.IsEmpty()) { return ProfmanResult::kSkipCompilationEmptyProfiles; } - uint32_t min_change_in_methods_for_compilation = std::max( - (options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100, - kMinNewMethodsForCompilation); - uint32_t min_change_in_classes_for_compilation = std::max( - (options.GetMinNewClassesPercentChangeForCompilation() * number_of_classes) / 100, - kMinNewClassesForCompilation); - // Check if there is enough new information added by the current profiles. - if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) && - ((info.GetNumberOfResolvedClasses() - number_of_classes) - < min_change_in_classes_for_compilation)) { - return ProfmanResult::kSkipCompilationSmallDelta; + + if (options.IsForceMergeAndAnalyze()) { + // When we force merge and analyze, we want to always recompile unless there is absolutely no + // difference between before and after the merge (i.e., the classes and methods in the + // reference profile were already a superset of those in all current profiles before the + // merge.) + if (info.GetNumberOfMethods() == number_of_methods && + info.GetNumberOfResolvedClasses() == number_of_classes) { + return ProfmanResult::kSkipCompilationSmallDelta; + } + } else { + uint32_t min_change_in_methods_for_compilation = std::max( + (options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100, + kMinNewMethodsForCompilation); + uint32_t min_change_in_classes_for_compilation = std::max( + (options.GetMinNewClassesPercentChangeForCompilation() * number_of_classes) / 100, + kMinNewClassesForCompilation); + // Check if there is enough new information added by the current profiles. + if (((info.GetNumberOfMethods() - number_of_methods) < + min_change_in_methods_for_compilation) && + ((info.GetNumberOfResolvedClasses() - number_of_classes) < + min_change_in_classes_for_compilation)) { + return ProfmanResult::kSkipCompilationSmallDelta; + } } } diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h index 84f140f23e..5ef37b4137 100644 --- a/profman/profile_assistant.h +++ b/profman/profile_assistant.h @@ -44,7 +44,9 @@ class ProfileAssistant { kMinNewClassesPercentChangeForCompilation) { } + // Only for S and T uses. U+ should use `IsForceMergeAndAnalyze`. bool IsForceMerge() const { return force_merge_; } + bool IsForceMergeAndAnalyze() const { return force_merge_and_analyze_; } bool IsBootImageMerge() const { return boot_image_merge_; } uint32_t GetMinNewMethodsPercentChangeForCompilation() const { return min_new_methods_percent_change_for_compilation_; @@ -54,6 +56,7 @@ class ProfileAssistant { } void SetForceMerge(bool value) { force_merge_ = value; } + void SetForceMergeAndAnalyze(bool value) { force_merge_and_analyze_ = value; } void SetBootImageMerge(bool value) { boot_image_merge_ = value; } void SetMinNewMethodsPercentChangeForCompilation(uint32_t value) { min_new_methods_percent_change_for_compilation_ = value; @@ -63,10 +66,15 @@ class ProfileAssistant { } private: - // If true, performs a forced merge, without analyzing if there is a - // significant difference between the current profile and the reference profile. + // If true, performs a forced merge, without analyzing if there is a significant difference + // between before and after the merge. // See ProfileAssistant#ProcessProfile. + // Only for S and T uses. U+ should use `force_merge_and_analyze_`. bool force_merge_; + // If true, performs a forced merge and analyzes if there is any difference between before and + // after the merge. + // See ProfileAssistant#ProcessProfile. + bool force_merge_and_analyze_; // Signals that the merge is for boot image profiles. It will ignore differences // in profile versions (instead of aborting). bool boot_image_merge_; diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index d06571ef0d..93766e5d52 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -2058,38 +2058,60 @@ TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) { } TEST_F(ProfileAssistantTest, BootImageMerge) { - ScratchFile profile; - ScratchFile reference_profile; - std::vector<int> profile_fds({GetFd(profile)}); - int reference_profile_fd = GetFd(reference_profile); - std::vector<uint32_t> hot_methods_cur; - std::vector<uint32_t> hot_methods_ref; - std::vector<uint32_t> empty_vector; - size_t num_methods = 100; - for (size_t i = 0; i < num_methods; ++i) { - hot_methods_cur.push_back(i); + ScratchFile profile1; + ScratchFile profile2; + ScratchFile profile3; + ScratchFile output_profile; + std::vector<uint32_t> hot_methods_1; + std::vector<uint32_t> hot_methods_2; + std::vector<uint32_t> hot_methods_3; + for (size_t i = 0; i < 100; ++i) { + hot_methods_1.push_back(i); } - for (size_t i = 0; i < num_methods; ++i) { - hot_methods_ref.push_back(i); + for (size_t i = 50; i < 150; ++i) { + hot_methods_2.push_back(i); } - ProfileCompilationInfo info1(/*for_boot_image=*/ true); - SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector, - profile, &info1); + for (size_t i = 100; i < 200; ++i) { + hot_methods_3.push_back(i); + } + ProfileCompilationInfo info1(/*for_boot_image=*/false); + SetupBasicProfile( + dex1, hot_methods_1, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile1, &info1); ProfileCompilationInfo info2(/*for_boot_image=*/true); - SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector, - reference_profile, &info2); - - std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"}); + SetupBasicProfile( + dex1, hot_methods_2, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile2, &info2); + ProfileCompilationInfo info3(/*for_boot_image=*/true); + SetupBasicProfile( + dex1, hot_methods_3, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile3, &info3); - int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args); - - ASSERT_EQ(return_code, ProfmanResult::kSuccess); + { + int return_code = ProcessProfiles({profile1.GetFd(), profile2.GetFd(), profile3.GetFd()}, + output_profile.GetFd(), + {"--force-merge-and-analyze", "--boot-image-merge"}); + ASSERT_EQ(return_code, ProfmanResult::kCompile); + + // Verify the result: it should be equal to info2 union info3 since info1 is a regular profile + // and should be ignored. + ProfileCompilationInfo result(/*for_boot_image=*/true); + ASSERT_TRUE(result.Load(output_profile.GetFd())); + ASSERT_TRUE(info2.MergeWith(info3)); + ASSERT_TRUE(result.Equals(info2)); + } - // Verify the result: it should be equal to info2 since info1 is a regular profile - // and should be ignored. - ProfileCompilationInfo result(/*for_boot_image=*/ true); - ASSERT_TRUE(result.Load(reference_profile.GetFd())); - ASSERT_TRUE(result.Equals(info2)); + // Same for the legacy force merge mode. + { + int return_code = ProcessProfiles({profile1.GetFd(), profile2.GetFd(), profile3.GetFd()}, + output_profile.GetFd(), + {"--force-merge", "--boot-image-merge"}); + ASSERT_EQ(return_code, ProfmanResult::kSuccess); + + // Verify the result: it should be equal to info2 union info3 since info1 is a regular profile + // and should be ignored. + ProfileCompilationInfo result(/*for_boot_image=*/true); + ASSERT_TRUE(result.Load(output_profile.GetFd())); + ASSERT_TRUE(info2.MergeWith(info3)); + ASSERT_TRUE(result.Equals(info2)); + } } // Under default behaviour we should not advice compilation @@ -2134,6 +2156,82 @@ TEST_F(ProfileAssistantTest, ForceMerge) { ASSERT_TRUE(result.Equals(info1)); } +TEST_F(ProfileAssistantTest, ForceMergeAndAnalyze) { + const uint16_t kNumberOfMethodsInRefProfile = 600; + const uint16_t kNumberOfMethodsInCurProfile = 601; + + ScratchFile ref_profile; + ScratchFile cur_profile; + + ProfileCompilationInfo ref_info; + SetupProfile( + dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info); + ProfileCompilationInfo cur_info; + SetupProfile( + dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info); + + std::vector<const std::string> extra_args({"--force-merge-and-analyze"}); + int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args); + + ASSERT_EQ(return_code, ProfmanResult::kCompile); + + // Check that the result is the aggregation. + ProfileCompilationInfo result; + ASSERT_TRUE(result.Load(ref_profile.GetFd())); + ASSERT_TRUE(ref_info.MergeWith(cur_info)); + ASSERT_TRUE(result.Equals(ref_info)); +} + +TEST_F(ProfileAssistantTest, ForceMergeAndAnalyzeNoDelta) { + const uint16_t kNumberOfMethodsInRefProfile = 600; + const uint16_t kNumberOfMethodsInCurProfile = 600; + + ScratchFile ref_profile; + ScratchFile cur_profile; + + ProfileCompilationInfo ref_info; + SetupProfile( + dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info); + ProfileCompilationInfo cur_info; + SetupProfile( + dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info); + + std::vector<const std::string> extra_args({"--force-merge-and-analyze"}); + int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args); + + ASSERT_EQ(return_code, ProfmanResult::kSkipCompilationSmallDelta); + + // Check that the reference profile is unchanged. + ProfileCompilationInfo result; + ASSERT_TRUE(result.Load(ref_profile.GetFd())); + ASSERT_TRUE(result.Equals(ref_info)); +} + +TEST_F(ProfileAssistantTest, ForceMergeAndAnalyzeEmptyProfiles) { + const uint16_t kNumberOfMethodsInRefProfile = 0; + const uint16_t kNumberOfMethodsInCurProfile = 0; + + ScratchFile ref_profile; + ScratchFile cur_profile; + + ProfileCompilationInfo ref_info; + SetupProfile( + dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info); + ProfileCompilationInfo cur_info; + SetupProfile( + dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info); + + std::vector<const std::string> extra_args({"--force-merge-and-analyze"}); + int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args); + + ASSERT_EQ(return_code, ProfmanResult::kSkipCompilationEmptyProfiles); + + // Check that the reference profile is unchanged. + ProfileCompilationInfo result; + ASSERT_TRUE(result.Load(ref_profile.GetFd())); + ASSERT_TRUE(result.Equals(ref_info)); +} + // Test that we consider the annations when we merge boot image profiles. TEST_F(ProfileAssistantTest, BootImageMergeWithAnnotations) { ScratchFile profile; @@ -2216,25 +2314,40 @@ TEST_F(ProfileAssistantTest, ForceMergeIgnoreProfilesItCannotLoad) { std::string content = "giberish"; ASSERT_TRUE(profile1.GetFile()->WriteFully(content.c_str(), content.length())); - ProfileCompilationInfo info2(/*for_boot_image=*/ true); + ProfileCompilationInfo info2(/*for_boot_image=*/true); info2.Save(profile2.GetFd()); - std::vector<int> profile_fds({ GetFd(profile1)}); + std::vector<int> profile_fds({GetFd(profile1)}); int reference_profile_fd = GetFd(profile2); // With force-merge we should merge successfully. - std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"}); - ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args), - ProfmanResult::kSuccess); + { + ASSERT_EQ( + ProcessProfiles( + profile_fds, reference_profile_fd, {"--force-merge-and-analyze", "--boot-image-merge"}), + ProfmanResult::kSkipCompilationEmptyProfiles); + + ProfileCompilationInfo result(/*for_boot_image=*/true); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ASSERT_TRUE(info2.Equals(result)); + } - ProfileCompilationInfo result(/*for_boot_image=*/ true); - ASSERT_TRUE(result.Load(reference_profile_fd)); - ASSERT_TRUE(info2.Equals(result)); + // Same for the legacy force merge mode. + { + ASSERT_EQ( + ProcessProfiles(profile_fds, reference_profile_fd, {"--force-merge", "--boot-image-merge"}), + ProfmanResult::kSuccess); + + ProfileCompilationInfo result(/*for_boot_image=*/true); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ASSERT_TRUE(info2.Equals(result)); + } // Without force-merge we should fail. - std::vector<const std::string> extra_args2({"--boot-image-merge"}); - ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args2), - ProfmanResult::kErrorBadProfiles); + { + ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, {"--boot-image-merge"}), + ProfmanResult::kErrorBadProfiles); + } } } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index 3ce5d699b4..0958f4b01c 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -190,7 +190,10 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --boot-image-merge: indicates that this merge is for a boot image profile."); UsageError(" In this case, the reference profile must have a boot profile version."); UsageError(" --force-merge: performs a forced merge, without analyzing if there is a"); - UsageError(" significant difference between the current profile and the reference profile."); + UsageError(" significant difference between before and after the merge."); + UsageError(" Deprecated. Use --force-merge-and-analyze instead."); + UsageError(" --force-merge-and-analyze: performs a forced merge and analyzes if there is any"); + UsageError(" difference between before and after the merge."); UsageError(" --min-new-methods-percent-change=percentage between 0 and 100 (default 2)"); UsageError(" the min percent of new methods to trigger a compilation."); UsageError(" --min-new-classes-percent-change=percentage between 0 and 100 (default 2)"); @@ -469,7 +472,11 @@ class ProfMan final { } else if (option == "--boot-image-merge") { profile_assistant_options_.SetBootImageMerge(true); } else if (option == "--force-merge") { + // For backward compatibility only. + // TODO(jiakaiz): Remove this when S and T are no longer supported. profile_assistant_options_.SetForceMerge(true); + } else if (option == "--force-merge-and-analyze") { + profile_assistant_options_.SetForceMergeAndAnalyze(true); } else { Usage("Unknown argument '%s'", raw_option); } |