Enable profile data filtering in profman

Update profile merging to accept a set of apks (passes with --apk) which
will dictate what data should be processed.

When profman is invoked with a list of --apk files, only profile data
belonging to that apks will be in the output reference profile.

If no --dex-location is specified then the locations is inferred from
reding /proc/self/fd/apk_fd link.

Test: profile_assistant_test
Bug: 30934496
Change-Id: I44698c6db545ecf91454db1387c3d0e47fe5b9b3
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index c75f3e9..79310ac 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include "android-base/strings.h"
 #include "art_method-inl.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
@@ -51,6 +52,28 @@
     uint32_t dex_location_checksum1 = checksum;
     std::string dex_location2 = "location2" + id;
     uint32_t dex_location_checksum2 = 10 * checksum;
+    SetupProfile(dex_location1,
+                 dex_location_checksum1,
+                 dex_location2,
+                 dex_location_checksum2,
+                 number_of_methods,
+                 number_of_classes,
+                 profile,
+                 info,
+                 start_method_index,
+                 reverse_dex_write_order);
+  }
+
+  void SetupProfile(const std::string& dex_location1,
+                    uint32_t dex_location_checksum1,
+                    const std::string& dex_location2,
+                    uint32_t dex_location_checksum2,
+                    uint16_t number_of_methods,
+                    uint16_t number_of_classes,
+                    const ScratchFile& profile,
+                    ProfileCompilationInfo* info,
+                    uint16_t start_method_index = 0,
+                    bool reverse_dex_write_order = false) {
     for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
       // reverse_dex_write_order controls the order in which the dex files will be added to
       // the profile and thus written to disk.
@@ -1128,4 +1151,89 @@
   }
 }
 
+TEST_F(ProfileAssistantTest, MergeProfilesWithFilter) {
+  ScratchFile profile1;
+  ScratchFile profile2;
+  ScratchFile reference_profile;
+
+  std::vector<int> profile_fds({
+      GetFd(profile1),
+      GetFd(profile2)});
+  int reference_profile_fd = GetFd(reference_profile);
+
+  // Use a real dex file to generate profile test data.
+  // The file will be used during merging to filter unwanted data.
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+  const DexFile& d1 = *dex_files[0];
+  const DexFile& d2 = *dex_files[1];
+  // The new profile info will contain the methods with indices 0-100.
+  const uint16_t kNumberOfMethodsToEnableCompilation = 100;
+  ProfileCompilationInfo info1;
+  SetupProfile(d1.GetLocation(), d1.GetLocationChecksum(), "p1", 1,
+      kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
+  ProfileCompilationInfo info2;
+  SetupProfile(d2.GetLocation(), d2.GetLocationChecksum(), "p2", 2,
+      kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
+
+
+  // The reference profile info will contain the methods with indices 50-150.
+  const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
+  ProfileCompilationInfo reference_info;
+  SetupProfile(d1.GetLocation(), d1.GetLocationChecksum(), "p1", 1,
+      kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
+      &reference_info, kNumberOfMethodsToEnableCompilation / 2);
+
+  // 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("--profile-file-fd=" + std::to_string(profile1.GetFd()));
+  argv_str.push_back("--profile-file-fd=" + std::to_string(profile2.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()));
+  std::string error;
+
+  EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
+
+  // Verify that we can load the result.
+
+  ProfileCompilationInfo result;
+  ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+  ASSERT_TRUE(result.Load(reference_profile_fd));
+
+
+  ASSERT_TRUE(profile1.GetFile()->ResetOffset());
+  ASSERT_TRUE(profile2.GetFile()->ResetOffset());
+  ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+
+  // Verify that the result filtered out data not belonging to the dex file.
+  // This is equivalent to checking that the result is equal to the merging of
+  // all profiles while filtering out data not belonging to the dex file.
+
+  ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
+      [&d1, &d2](const std::string& dex_location, uint32_t checksum) -> bool {
+          return (dex_location == ProfileCompilationInfo::GetProfileDexFileKey(d1.GetLocation())
+              && checksum == d1.GetLocationChecksum())
+              || (dex_location == ProfileCompilationInfo::GetProfileDexFileKey(d2.GetLocation())
+              && checksum == d2.GetLocationChecksum());
+        };
+
+  ProfileCompilationInfo info1_filter;
+  ProfileCompilationInfo info2_filter;
+  ProfileCompilationInfo expected;
+
+  info2_filter.Load(profile1.GetFd(), /*merge_classes*/ true, filter_fn);
+  info2_filter.Load(profile2.GetFd(), /*merge_classes*/ true, filter_fn);
+  expected.Load(reference_profile.GetFd(), /*merge_classes*/ true, filter_fn);
+
+  ASSERT_TRUE(expected.MergeWith(info1_filter));
+  ASSERT_TRUE(expected.MergeWith(info2_filter));
+
+  ASSERT_TRUE(expected.Equals(result));
+}
+
 }  // namespace art