Update profman to support artd use cases.
1. Return 100 instead of 1 for usage error. 1 is already used to
represent kCompile.
2. Move profman return codes to a separate header file so that artd can
reference them.
3. Allow running profman with `--reference-profile-file(-fd)` but no
`--profile-file(-fd)`. In this case, profman returns either
`kSkipCompilationSmallDelta`, `kSkipCompilationEmptyProfiles`, or
errors. It is essentially a profile checker that checks whether the
reference profile is usable or not.
4. Add a new return code kCopyAndUpdateNoUpdate for the case where
profman is run with `--copy-and-update-profile-key` and the copy
succeeds but nothing has been updated.
5. Use positive values for `--copy-and-update-profile-key` errors
instead of negative values because the exit code range is 0~255.
- Change #1 is safe because there is no one checking the exit code
against 1 for usage error. (If there were, the check would be wrong
anyway.)
- Change #4 and #5 are safe because only installd calls profman with
`--copy-and-update-profile-key`, and installd doesn't check the exit
code.
Bug: 229268202
Test: m test-art-host-gtest-art_profman_tests
Change-Id: I8e82c14955519244852166bc7753df4a2bd102ef
Merged-In: I8e82c14955519244852166bc7753df4a2bd102ef
(cherry picked from commit e3d589c26d6f97c4031986132afd4eb1ef00fe4c)
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index f135805..83db0a0 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -2387,7 +2387,8 @@
}
bool ProfileCompilationInfo::UpdateProfileKeys(
- const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+ const std::vector<std::unique_ptr<const DexFile>>& dex_files, /*out*/ bool* updated) {
+ *updated = false;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
if (dex_data->checksum == dex_file->GetLocationChecksum() &&
@@ -2407,6 +2408,7 @@
// form the old key.
dex_data->profile_key = MigrateAnnotationInfo(new_profile_key, dex_data->profile_key);
profile_key_map_.Put(dex_data->profile_key, dex_data->profile_index);
+ *updated = true;
}
}
}
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 4366078..76cbf9a 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -645,7 +645,10 @@
//
// If the new profile key would collide with an existing key (for a different dex)
// the method returns false. Otherwise it returns true.
- bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+ //
+ // `updated` is set to true if any profile key has been updated by this method.
+ bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ /*out*/ bool* updated);
// Checks if the profile is empty.
bool IsEmpty() const;
diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
index 39e8b4f..2ee34f2 100644
--- a/libprofile/profile/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -956,7 +956,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0);
// Update the profile keys based on the original dex files
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_TRUE(updated);
// Verify that we find the methods when searched with the original dex files.
for (const std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -982,7 +984,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0, Hotness::kFlagHot, annotation);
// Update the profile keys based on the original dex files
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_TRUE(updated);
// Verify that we find the methods when searched with the original dex files.
for (const std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -1005,7 +1009,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0);
// Update the profile keys based on the original dex files.
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_FALSE(updated);
// Verify that we did not perform any update and that we cannot find anything with the new
// location.
@@ -1037,7 +1043,9 @@
// This will cause the rename to fail because an existing entry would already have that name.
AddMethod(&info, dex1_renamed, /*method_idx=*/ 0);
- ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_FALSE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_FALSE(updated);
// Release the ownership as this is held by the test class;
for (std::unique_ptr<const DexFile>& dex : dex_files) {
diff --git a/profman/Android.bp b/profman/Android.bp
index b231499..8e76bc2 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -32,6 +32,7 @@
"profman.cc",
"profile_assistant.cc",
],
+ header_libs: ["profman_headers"],
target: {
android: {
@@ -160,11 +161,31 @@
},
}
+cc_library_headers {
+ name: "profman_headers",
+ defaults: ["art_defaults"],
+ export_include_dirs: ["include"],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
art_cc_defaults {
name: "art_profman_tests_defaults",
data: [
":art-gtest-jars-ProfileTestMultiDex",
],
+ header_libs: ["profman_headers"],
tidy_timeout_srcs: ["profile_assistant_test.cc"],
srcs: ["profile_assistant_test.cc"],
}
diff --git a/profman/include/profman/profman_result.h b/profman/include/profman/profman_result.h
new file mode 100644
index 0000000..2e962e8
--- /dev/null
+++ b/profman/include/profman/profman_result.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
+#define ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
+
+namespace art {
+
+class ProfmanResult {
+ public:
+ static constexpr int kErrorUsage = 100;
+
+ // The return codes of processing profiles (running profman in normal mode).
+ //
+ // Note that installd consumes the returns codes with its own copy of these values
+ // (frameworks/native/cmds/installd/dexopt.cpp).
+ enum ProcessingResult {
+ kSuccess = 0, // Generic success code for non-analysis runs.
+ kCompile = 1,
+ kSkipCompilationSmallDelta = 2,
+ kErrorBadProfiles = 3,
+ kErrorIO = 4,
+ kErrorCannotLock = 5,
+ kErrorDifferentVersions = 6,
+ kSkipCompilationEmptyProfiles = 7,
+ };
+
+ // The return codes of running profman with `--copy-and-update-profile-key`.
+ enum CopyAndUpdateResult {
+ kCopyAndUpdateSuccess = 0,
+ kCopyAndUpdateNoUpdate = 21,
+ kCopyAndUpdateErrorFailedToUpdateProfile = 22,
+ kCopyAndUpdateErrorFailedToSaveProfile = 23,
+ kCopyAndUpdateErrorFailedToLoadProfile = 24,
+ };
+};
+
+} // namespace art
+
+#endif // ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index d098738..abbde2d 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -18,6 +18,7 @@
#include "base/os.h"
#include "base/unix_file/fd_file.h"
+#include "profman/profman_result.h"
namespace art {
@@ -26,25 +27,22 @@
static constexpr const uint32_t kMinNewMethodsForCompilation = 100;
static constexpr const uint32_t kMinNewClassesForCompilation = 50;
-
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const ScopedFlock& reference_profile_file,
- const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
- const Options& options) {
- DCHECK(!profile_files.empty());
-
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
+ const std::vector<ScopedFlock>& profile_files,
+ const ScopedFlock& reference_profile_file,
+ const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
+ const Options& options) {
ProfileCompilationInfo info(options.IsBootImageMerge());
// Load the reference profile.
if (!info.Load(reference_profile_file->Fd(), /*merge_classes=*/ true, filter_fn)) {
LOG(WARNING) << "Could not load reference profile file";
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
if (options.IsBootImageMerge() && !info.IsForBootImage()) {
LOG(WARNING) << "Requested merge for boot image profile but the reference profile is regular.";
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
// Store the current state of the reference profile before merging with the current profiles.
@@ -65,21 +63,21 @@
// TODO: Do we really need to use a different error code for version mismatch?
ProfileCompilationInfo wrong_info(!options.IsBootImageMerge());
if (wrong_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) {
- return kErrorDifferentVersions;
+ return ProfmanResult::kErrorDifferentVersions;
}
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
if (!info.MergeWith(cur_info)) {
LOG(WARNING) << "Could not merge profile file at index " << i;
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
}
// If we perform a forced merge do not analyze the difference between profiles.
if (!options.IsForceMerge()) {
if (info.IsEmpty()) {
- return kSkipCompilationEmptyProfiles;
+ return ProfmanResult::kSkipCompilationEmptyProfiles;
}
uint32_t min_change_in_methods_for_compilation = std::max(
(options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100,
@@ -91,21 +89,21 @@
if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) &&
((info.GetNumberOfResolvedClasses() - number_of_classes)
< min_change_in_classes_for_compilation)) {
- return kSkipCompilationSmallDelta;
+ return ProfmanResult::kSkipCompilationSmallDelta;
}
}
// We were successful in merging all profile information. Update the reference profile.
if (!reference_profile_file->ClearContent()) {
PLOG(WARNING) << "Could not clear reference profile file";
- return kErrorIO;
+ return ProfmanResult::kErrorIO;
}
if (!info.Save(reference_profile_file->Fd())) {
LOG(WARNING) << "Could not save reference profile file";
- return kErrorIO;
+ return ProfmanResult::kErrorIO;
}
- return options.IsForceMerge() ? kSuccess : kCompile;
+ return options.IsForceMerge() ? ProfmanResult::kSuccess : ProfmanResult::kCompile;
}
class ScopedFlockList {
@@ -144,7 +142,7 @@
std::vector<ScopedFlock> flocks_;
};
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<int>& profile_files_fd,
int reference_profile_file_fd,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
@@ -155,7 +153,7 @@
ScopedFlockList profile_files(profile_files_fd.size());
if (!profile_files.Init(profile_files_fd, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
// The reference_profile_file is opened in read/write mode because it's
@@ -166,7 +164,7 @@
&error);
if (reference_profile_file.get() == nullptr) {
LOG(WARNING) << "Could not lock reference profiled files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files.Get(),
@@ -175,7 +173,7 @@
options);
}
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<std::string>& profile_files,
const std::string& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
@@ -185,14 +183,14 @@
ScopedFlockList profile_files_list(profile_files.size());
if (!profile_files_list.Init(profile_files, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
ScopedFlock locked_reference_profile_file = LockedFile::Open(
reference_profile_file.c_str(), O_RDWR, /* block= */ true, &error);
if (locked_reference_profile_file.get() == nullptr) {
LOG(WARNING) << "Could not lock reference profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files_list.Get(),
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
index 0ef4f88..6b7a7a6 100644
--- a/profman/profile_assistant.h
+++ b/profman/profile_assistant.h
@@ -22,24 +22,12 @@
#include "base/scoped_flock.h"
#include "profile/profile_compilation_info.h"
+#include "profman/profman_result.h"
namespace art {
class ProfileAssistant {
public:
- // These also serve as return codes of profman and are processed by installd
- // (frameworks/native/cmds/installd/commands.cpp)
- enum ProcessingResult {
- kSuccess = 0, // Generic success code for non-analysis runs.
- kCompile = 1,
- kSkipCompilationSmallDelta = 2,
- kErrorBadProfiles = 3,
- kErrorIO = 4,
- kErrorCannotLock = 5,
- kErrorDifferentVersions = 6,
- kSkipCompilationEmptyProfiles = 7,
- };
-
class Options {
public:
static constexpr bool kForceMergeDefault = false;
@@ -101,14 +89,14 @@
// this case no file will be updated. A variation of this code is
// kSkipCompilationEmptyProfiles which indicates that all the profiles are empty.
// This allow the caller to make fine grain decisions on the compilation strategy.
- static ProcessingResult ProcessProfiles(
+ static ProfmanResult::ProcessingResult ProcessProfiles(
const std::vector<std::string>& profile_files,
const std::string& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn
= ProfileCompilationInfo::ProfileFilterFnAcceptAll,
const Options& options = Options());
- static ProcessingResult ProcessProfiles(
+ static ProfmanResult::ProcessingResult ProcessProfiles(
const std::vector<int>& profile_files_fd_,
int reference_profile_file_fd,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn
@@ -116,7 +104,7 @@
const Options& options = Options());
private:
- static ProcessingResult ProcessProfilesInternal(
+ static ProfmanResult::ProcessingResult ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
const ScopedFlock& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index d337419..01b385a 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include "profile_assistant.h"
+
#include <sstream>
#include <string>
@@ -31,12 +32,13 @@
#include "dex/dex_instruction_iterator.h"
#include "dex/type_reference.h"
#include "exec_utils.h"
+#include "gtest/gtest.h"
#include "linear_alloc.h"
#include "mirror/class-inl.h"
#include "obj_ptr-inl.h"
#include "profile/profile_compilation_info.h"
#include "profile/profile_test_helper.h"
-#include "profile_assistant.h"
+#include "profman/profman_result.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
@@ -492,8 +494,7 @@
SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -533,8 +534,7 @@
SetupProfile(dex1_100, dex2_100, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -572,8 +572,7 @@
&reference_info, kNumberOfMethodsToEnableCompilation / 2);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs
ProfileCompilationInfo result;
@@ -606,7 +605,7 @@
SetupProfile(dex3, dex4, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile2, &info2);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationEmptyProfiles,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationEmptyProfiles,
ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
@@ -643,7 +642,7 @@
SetupProfile(dex3, dex4, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
@@ -669,10 +668,9 @@
std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
+ CheckCompilationMethodPercentChange(
+ kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
@@ -681,10 +679,9 @@
std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kCompile,
+ CheckCompilationMethodPercentChange(
+ kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentageWithNewMin) {
@@ -692,7 +689,7 @@
const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 20%.
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
kNumberOfMethodsInRefProfile));
}
@@ -703,10 +700,9 @@
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
+ CheckCompilationClassPercentChange(
+ kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
@@ -715,10 +711,9 @@
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kCompile,
+ CheckCompilationClassPercentChange(
+ kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentageWithNewMin) {
@@ -726,7 +721,7 @@
const uint16_t kNumberOfClassesInCurProfile = 6200; // Threshold is 20%.
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
kNumberOfClassesInRefProfile));
}
@@ -750,8 +745,7 @@
dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should fail processing.
- ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
CheckProfileInfo(profile1, info1);
@@ -782,8 +776,7 @@
&reference_info);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
CheckProfileInfo(profile1, info1);
@@ -1531,8 +1524,7 @@
&reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order=*/true);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
@@ -1793,7 +1785,7 @@
argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kCompile) << error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCompile) << error;
// Verify that we can load the result.
@@ -1826,6 +1818,165 @@
ASSERT_TRUE(expected.Equals(result));
}
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfile) {
+ ScratchFile reference_profile;
+
+ // Use a real dex file to generate profile test data.
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+ const DexFile& d1 = *dex_files[0];
+ const DexFile& d2 = *dex_files[0];
+
+ // The reference profile info will contain the methods with indices 0-100.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(&d1,
+ &d2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationSmallDelta.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfilePassByFilename) {
+ ScratchFile reference_profile;
+
+ // Use a real dex file to generate profile test data.
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+ const DexFile& d1 = *dex_files[0];
+ const DexFile& d2 = *dex_files[0];
+
+ // The reference profile info will contain the methods with indices 0-100.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(&d1,
+ &d2,
+ /*number_of_methods=*/100,
+ /*number_of_classes=*/0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file=" + reference_profile.GetFilename());
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationSmallDelta.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfile) {
+ ScratchFile reference_profile;
+
+ // The reference profile info will only contain the header.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(/*dex_file1=*/ nullptr,
+ /*dex_file2=*/ nullptr,
+ /*number_of_methods=*/ 0,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationEmptyProfiles.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering) {
+ ScratchFile reference_profile;
+
+ // Use fake dex files to generate profile test data.
+ // All the methods will be filtered out during the profman invocation.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(dex1,
+ dex2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the real dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationEmptyProfiles.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKey) {
ScratchFile profile1;
ScratchFile reference_profile;
@@ -1864,7 +2015,8 @@
argv_str.push_back("--copy-and-update-profile-key");
std::string error;
- ASSERT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
+ // Must return kCopyAndUpdateSuccess.
+ ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateSuccess) << error;
// Verify that we can load the result.
ProfileCompilationInfo result;
@@ -1880,6 +2032,45 @@
}
}
+TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) {
+ ScratchFile profile1;
+ ScratchFile reference_profile;
+
+ // Use fake dex files to generate profile test data.
+ ProfileCompilationInfo info1;
+ SetupProfile(dex1,
+ dex2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ profile1,
+ &info1);
+
+ std::string input_content;
+ ASSERT_TRUE(android::base::ReadFileToString(profile1.GetFilename(), &input_content));
+
+ // Run profman and pass the real dex file with --apk-fd. It won't match any entry in the profile.
+ android::base::unique_fd apk_fd(
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+ argv_str.push_back("--copy-and-update-profile-key");
+ std::string error;
+
+ // Must return kCopyAndUpdateNoUpdate.
+ ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateNoUpdate) << error;
+
+ // Verify that the content is the same.
+ std::string output_content;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &output_content));
+ EXPECT_EQ(input_content, output_content);
+}
+
TEST_F(ProfileAssistantTest, BootImageMerge) {
ScratchFile profile;
ScratchFile reference_profile;
@@ -1906,7 +2097,7 @@
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
- ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
// Verify the result: it should be equal to info2 since info1 is a regular profile
// and should be ignored.
@@ -1948,7 +2139,7 @@
std::vector<const std::string> extra_args({"--force-merge"});
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
- ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
// Check that the result is the aggregation.
ProfileCompilationInfo result;
@@ -1993,7 +2184,7 @@
argv_str.push_back("--boot-image-merge");
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kSuccess) << error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSuccess) << error;
// Verify that we can load the result and that it equals to what we saved.
ProfileCompilationInfo result(/*for_boot_image=*/ true);
@@ -2015,17 +2206,16 @@
int reference_profile_fd = GetFd(profile2);
std::vector<const std::string> boot_image_args({"--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
- ProfileAssistant::kErrorDifferentVersions);
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorDifferentVersions);
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd), ProfmanResult::kErrorBadProfiles);
// Reverse the order of the profiles to verify we get the same behaviour.
profile_fds[0] = GetFd(profile2);
reference_profile_fd = GetFd(profile1);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorBadProfiles);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
- ProfileAssistant::kErrorDifferentVersions);
+ ProfmanResult::kErrorDifferentVersions);
}
// Under default behaviour we will abort if we cannot load a profile during a merge
@@ -2048,7 +2238,7 @@
// 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),
- ProfileAssistant::kSuccess);
+ ProfmanResult::kSuccess);
ProfileCompilationInfo result(/*for_boot_image=*/ true);
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -2057,7 +2247,7 @@
// 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),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorBadProfiles);
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index 1968468..8e2f3b1 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -36,7 +36,6 @@
#include "android-base/parsebool.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
#include "base/array_ref.h"
#include "base/dumpable.h"
#include "base/logging.h" // For InitLogging.
@@ -64,6 +63,7 @@
#include "profile/profile_boot_info.h"
#include "profile/profile_compilation_info.h"
#include "profile_assistant.h"
+#include "profman/profman_result.h"
namespace art {
@@ -118,7 +118,10 @@
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
- UsageError(" profiles will be aggregated.");
+ UsageError(" profiles will be aggregated. Can also be specified zero times, in which case");
+ UsageError(" profman will still analyze the reference profile against the given --apk and");
+ UsageError(" return exit code based on whether the reference profile is empty and whether");
+ UsageError(" an error occurs, but no merge will happen.");
UsageError("");
UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
UsageError(" Cannot be used together with --profile-file.");
@@ -126,8 +129,8 @@
UsageError(" --reference-profile-file=<filename>: specify a reference profile.");
UsageError(" The data in this file will be compared with the data obtained by merging");
UsageError(" all the files specified with --profile-file or --profile-file-fd.");
- UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into");
- UsageError(" --reference-profile-file. ");
+ UsageError(" If the exit code is ProfmanResult::kCompile then all --profile-file will be");
+ UsageError(" merged into --reference-profile-file. ");
UsageError("");
UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
UsageError(" accepts a file descriptor. Cannot be used together with");
@@ -194,7 +197,7 @@
UsageError(" the min percent of new classes to trigger a compilation.");
UsageError("");
- exit(EXIT_FAILURE);
+ exit(ProfmanResult::kErrorUsage);
}
// Note: make sure you update the Usage if you change these values.
@@ -501,11 +504,10 @@
}
};
- ProfileAssistant::ProcessingResult ProcessProfiles() {
- // Validate that at least one profile file was passed, as well as a reference profile.
- if (profile_files_.empty() && profile_files_fd_.empty()) {
- Usage("No profile files specified.");
- }
+ ProfmanResult::ProcessingResult ProcessProfiles() {
+ // Validate that a reference profile was passed, at the very least. It's okay that profiles are
+ // missing, in which case profman will still analyze the reference profile (to check whether
+ // it's empty), but no merge will happen.
if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
Usage("No reference profile file specified.");
}
@@ -518,7 +520,7 @@
// Check if we have any apks which we should use to filter the profile data.
std::set<ProfileFilterKey> profile_filter_keys;
if (!GetProfileFilterKeyFromApks(&profile_filter_keys)) {
- return ProfileAssistant::kErrorIO;
+ return ProfmanResult::kErrorIO;
}
// Build the profile filter function. If the set of keys is empty it means we
@@ -536,9 +538,9 @@
}
};
- ProfileAssistant::ProcessingResult result;
+ ProfmanResult::ProcessingResult result;
- if (profile_files_.empty()) {
+ if (reference_profile_file_.empty()) {
// The file doesn't need to be flushed here (ProcessProfiles will do it)
// so don't check the usage.
File file(reference_profile_file_fd_, false);
@@ -1854,7 +1856,7 @@
return copy_and_update_profile_key_;
}
- int32_t CopyAndUpdateProfileKey() {
+ ProfmanResult::CopyAndUpdateResult CopyAndUpdateProfileKey() {
// Validate that at least one profile file was passed, as well as a reference profile.
if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
Usage("Only one profile file should be specified.");
@@ -1867,10 +1869,6 @@
Usage("No apk files specified");
}
- static constexpr int32_t kErrorFailedToUpdateProfile = -1;
- static constexpr int32_t kErrorFailedToSaveProfile = -2;
- static constexpr int32_t kErrorFailedToLoadProfile = -3;
-
bool use_fds = profile_files_fd_.size() == 1;
ProfileCompilationInfo profile;
@@ -1882,15 +1880,19 @@
// Open the dex files to look up classes and methods.
std::vector<std::unique_ptr<const DexFile>> dex_files;
OpenApkFilesFromLocations(&dex_files);
- if (!profile.UpdateProfileKeys(dex_files)) {
- return kErrorFailedToUpdateProfile;
+ bool updated = false;
+ if (!profile.UpdateProfileKeys(dex_files, &updated)) {
+ return ProfmanResult::kCopyAndUpdateErrorFailedToUpdateProfile;
}
bool result = use_fds
? profile.Save(reference_profile_file_fd_)
: profile.Save(reference_profile_file_, /*bytes_written=*/ nullptr);
- return result ? 0 : kErrorFailedToSaveProfile;
+ if (!result) {
+ return ProfmanResult::kCopyAndUpdateErrorFailedToSaveProfile;
+ }
+ return updated ? ProfmanResult::kCopyAndUpdateSuccess : ProfmanResult::kCopyAndUpdateNoUpdate;
} else {
- return kErrorFailedToLoadProfile;
+ return ProfmanResult::kCopyAndUpdateErrorFailedToLoadProfile;
}
}
@@ -1950,7 +1952,7 @@
return ics.Dump(os);
}
-// See ProfileAssistant::ProcessingResult for return codes.
+// See ProfmanResult for return codes.
static int profman(int argc, char** argv) {
ProfMan profman;