summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiakai Zhang <jiakaiz@google.com>2023-11-28 16:55:47 +0000
committerCherrypicker Worker <android-build-cherrypicker-worker@google.com>2023-11-30 11:10:34 +0000
commit157c0d834f75e4e8de3a42ce783ffbd93c1f04be (patch)
treebf8d1d91432e9f5bb38cac09e11b9e2694a914ca
parent4ab7a5df1ce72cceae1a28e2dfc960805e3b1186 (diff)
downloadart-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.cc12
-rw-r--r--artd/artd_test.cc17
-rw-r--r--artd/binder/com/android/server/art/MergeProfileOptions.aidl2
-rw-r--r--profman/include/profman/profman_result.h10
-rw-r--r--profman/profile_assistant.cc37
-rw-r--r--profman/profile_assistant.h12
-rw-r--r--profman/profile_assistant_test.cc189
-rw-r--r--profman/profman.cc9
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);
}