Update dex2oat to accept multiple profiles.

`ProfileCompilationInfo` already supports loading multiple profiles, we
just need to update the commandline flags.

Bug: 203492478
Test: atest art_standalone_dex2oat_tests
Change-Id: Ifd1e614807e4d637529efe86673a691cc8a441be
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8cf4f6f..3d956e2 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -40,21 +40,21 @@
 #endif
 
 #include "android-base/parseint.h"
+#include "android-base/scopeguard.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 #include "android-base/unique_fd.h"
-
 #include "aot_class_linker.h"
 #include "arch/instruction_set_features.h"
 #include "art_method-inl.h"
 #include "base/callee_save_type.h"
 #include "base/dumpable.h"
+#include "base/fast_exit.h"
 #include "base/file_utils.h"
 #include "base/leb128.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/os.h"
-#include "base/fast_exit.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
 #include "base/time_utils.h"
@@ -505,49 +505,47 @@
 
 class Dex2Oat final {
  public:
-  explicit Dex2Oat(TimingLogger* timings) :
-      compiler_kind_(Compiler::kOptimizing),
-      // Take the default set of instruction features from the build.
-      key_value_store_(nullptr),
-      verification_results_(nullptr),
-      runtime_(nullptr),
-      thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
-      start_ns_(NanoTime()),
-      start_cputime_ns_(ProcessCpuNanoTime()),
-      strip_(false),
-      oat_fd_(-1),
-      input_vdex_fd_(-1),
-      output_vdex_fd_(-1),
-      input_vdex_file_(nullptr),
-      dm_fd_(-1),
-      zip_fd_(-1),
-      image_fd_(-1),
-      have_multi_image_arg_(false),
-      multi_image_(false),
-      image_base_(0U),
-      image_storage_mode_(ImageHeader::kStorageModeUncompressed),
-      passes_to_run_filename_(nullptr),
-      dirty_image_objects_filename_(nullptr),
-      dirty_image_objects_fd_(-1),
-      is_host_(false),
-      elf_writers_(),
-      oat_writers_(),
-      rodata_(),
-      image_writer_(nullptr),
-      driver_(nullptr),
-      opened_dex_files_maps_(),
-      opened_dex_files_(),
-      avoid_storing_invocation_(false),
-      swap_fd_(File::kInvalidFd),
-      app_image_fd_(File::kInvalidFd),
-      profile_file_fd_(File::kInvalidFd),
-      timings_(timings),
-      force_determinism_(false),
-      check_linkage_conditions_(false),
-      crash_on_linkage_violation_(false),
-      compile_individually_(false),
-      profile_load_attempted_(false)
-      {}
+  explicit Dex2Oat(TimingLogger* timings)
+      : compiler_kind_(Compiler::kOptimizing),
+        // Take the default set of instruction features from the build.
+        key_value_store_(nullptr),
+        verification_results_(nullptr),
+        runtime_(nullptr),
+        thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
+        start_ns_(NanoTime()),
+        start_cputime_ns_(ProcessCpuNanoTime()),
+        strip_(false),
+        oat_fd_(-1),
+        input_vdex_fd_(-1),
+        output_vdex_fd_(-1),
+        input_vdex_file_(nullptr),
+        dm_fd_(-1),
+        zip_fd_(-1),
+        image_fd_(-1),
+        have_multi_image_arg_(false),
+        multi_image_(false),
+        image_base_(0U),
+        image_storage_mode_(ImageHeader::kStorageModeUncompressed),
+        passes_to_run_filename_(nullptr),
+        dirty_image_objects_filename_(nullptr),
+        dirty_image_objects_fd_(-1),
+        is_host_(false),
+        elf_writers_(),
+        oat_writers_(),
+        rodata_(),
+        image_writer_(nullptr),
+        driver_(nullptr),
+        opened_dex_files_maps_(),
+        opened_dex_files_(),
+        avoid_storing_invocation_(false),
+        swap_fd_(File::kInvalidFd),
+        app_image_fd_(File::kInvalidFd),
+        timings_(timings),
+        force_determinism_(false),
+        check_linkage_conditions_(false),
+        crash_on_linkage_violation_(false),
+        compile_individually_(false),
+        profile_load_attempted_(false) {}
 
   ~Dex2Oat() {
     // Log completion time before deleting the runtime_, because this accesses
@@ -788,10 +786,10 @@
       Usage("--single-image not specified for --image-fd");
     }
 
-    const bool have_profile_file = !profile_file_.empty();
-    const bool have_profile_fd = profile_file_fd_ != File::kInvalidFd;
+    const bool have_profile_file = !profile_files_.empty();
+    const bool have_profile_fd = !profile_file_fds_.empty();
     if (have_profile_file && have_profile_fd) {
-      Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
+      Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
     }
 
     if (!parser_options->oat_symbols.empty()) {
@@ -1069,8 +1067,8 @@
     AssignIfExists(args, M::Passes, &passes_to_run_filename_);
     AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename);
     AssignIfExists(args, M::AndroidRoot, &android_root_);
-    AssignIfExists(args, M::Profile, &profile_file_);
-    AssignIfExists(args, M::ProfileFd, &profile_file_fd_);
+    AssignIfExists(args, M::Profile, &profile_files_);
+    AssignIfExists(args, M::ProfileFd, &profile_file_fds_);
     AssignIfExists(args, M::RuntimeOptions, &runtime_args_);
     AssignIfExists(args, M::SwapFile, &swap_file_name_);
     AssignIfExists(args, M::SwapFileFd, &swap_fd_);
@@ -2301,9 +2299,7 @@
     return is_host_;
   }
 
-  bool HasProfileInput() const {
-    return profile_file_fd_ != -1 || !profile_file_.empty();
-  }
+  bool HasProfileInput() const { return !profile_file_fds_.empty() || !profile_files_.empty(); }
 
   // Must be called after the profile is loaded.
   bool DoProfileGuidedOptimizations() const {
@@ -2344,29 +2340,37 @@
     // runtime).
     bool for_boot_image = IsBootImage() || IsBootImageExtension();
     profile_compilation_info_.reset(new ProfileCompilationInfo(for_boot_image));
+
+    // Cleanup profile compilation info if we encounter any error when reading profiles.
+    auto cleanup = android::base::ScopeGuard([&]() { profile_compilation_info_.reset(nullptr); });
+
     // 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::unique_ptr<File> profile_file;
-    if (profile_file_fd_ != -1) {
-      profile_file.reset(new File(DupCloexec(profile_file_fd_),
-                                  "profile",
-                                  /* check_usage= */ false,
-                                  /* read_only_mode= */ true));
-    } else if (profile_file_ != "") {
-      profile_file.reset(OS::OpenFileForReading(profile_file_.c_str()));
+    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;
+        }
+      }
+    } else {
+      for (const std::string& file : profile_files_) {
+        std::unique_ptr<File> profile_file(OS::OpenFileForReading(file.c_str()));
+        if (profile_file.get() == nullptr) {
+          PLOG(ERROR) << "Cannot open profiles";
+          return false;
+        }
+        if (!profile_compilation_info_->Load(profile_file->Fd())) {
+          return false;
+        }
+      }
     }
 
-    if (profile_file.get() == nullptr) {
-      PLOG(ERROR) << "Cannot lock profiles";
-      return false;
-    }
-
-    if (!profile_compilation_info_->Load(profile_file->Fd())) {
-      profile_compilation_info_.reset(nullptr);
-      return false;
-    }
-
+    cleanup.Disable();
     return true;
   }
 
@@ -2900,8 +2904,8 @@
   size_t very_large_threshold_ = std::numeric_limits<size_t>::max();
   std::string app_image_file_name_;
   int app_image_fd_;
-  std::string profile_file_;
-  int profile_file_fd_;
+  std::vector<std::string> profile_files_;
+  std::vector<int> profile_file_fds_;
   std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
   TimingLogger* timings_;
   std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 0125f9d..8d5296b 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -242,11 +242,11 @@
           .WithType<std::string>()
           .IntoKey(M::Passes)
       .Define("--profile-file=_")
-          .WithType<std::string>()
+          .WithType<std::vector<std::string>>().AppendValues()
           .WithHelp("Specify profiler output file to use for compilation using a filename.")
           .IntoKey(M::Profile)
       .Define("--profile-file-fd=_")
-          .WithType<int>()
+          .WithType<std::vector<int>>().AppendValues()
           .WithHelp("Specify profiler output file to use for compilation using a file-descriptor.")
           .IntoKey(M::ProfileFd)
       .Define("--no-inline-from=_")
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 804231c..f153ddf 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -66,8 +66,8 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    TargetInstructionSetVariant)
 DEX2OAT_OPTIONS_KEY (std::string,                    TargetInstructionSetFeatures)
 DEX2OAT_OPTIONS_KEY (Compiler::Kind,                 Backend)
-DEX2OAT_OPTIONS_KEY (std::string,                    Profile)
-DEX2OAT_OPTIONS_KEY (int,                            ProfileFd)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       Profile)
+DEX2OAT_OPTIONS_KEY (std::vector<int>,               ProfileFd)
 DEX2OAT_OPTIONS_KEY (Unit,                           Host)
 DEX2OAT_OPTIONS_KEY (Unit,                           DumpTiming)
 DEX2OAT_OPTIONS_KEY (Unit,                           DumpPasses)
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 06b3626..ab5f8c6 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -14,27 +14,26 @@
  * limitations under the License.
  */
 
+#include <sys/wait.h>
+#include <unistd.h>
+
 #include <algorithm>
+#include <iterator>
 #include <regex>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/macros.h>
-#include <android-base/stringprintf.h>
-
-#include "common_runtime_test.h"
-
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
 #include "arch/instruction_set_features.h"
 #include "base/macros.h"
 #include "base/mutex-inl.h"
 #include "base/string_view_cpp20.h"
 #include "base/utils.h"
 #include "base/zip_archive.h"
+#include "common_runtime_test.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/base64_test_util.h"
 #include "dex/bytecode_utils.h"
@@ -611,10 +610,20 @@
   }
 
   // Emits a profile with a single dex file with the given location and classes ranging
-  // from 0 to num_classes.
+  // from `class_offset` to `class_offset + num_classes`.
   void GenerateProfile(const std::string& test_profile,
-                       const DexFile* dex,
-                       size_t num_classes) {
+                       const std::string& dex_location,
+                       size_t num_classes,
+                       size_t class_offset = 0) {
+    const char* location = dex_location.c_str();
+    std::string error_msg;
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    const ArtDexFileLoader dex_file_loader;
+    ASSERT_TRUE(dex_file_loader.Open(
+        location, location, /*verify=*/true, /*verify_checksum=*/true, &error_msg, &dex_files));
+    EXPECT_EQ(dex_files.size(), 1U);
+    std::unique_ptr<const DexFile>& dex_file = dex_files[0];
+
     int profile_test_fd = open(test_profile.c_str(),
                                O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
                                0644);
@@ -623,33 +632,26 @@
     ProfileCompilationInfo info;
     std::vector<dex::TypeIndex> classes;;
     for (size_t i = 0; i < num_classes; ++i) {
-      classes.push_back(dex::TypeIndex(1 + i));
+      classes.push_back(dex::TypeIndex(class_offset + 1 + i));
     }
-    info.AddClassesForDex(dex, classes.begin(), classes.end());
+    info.AddClassesForDex(dex_file.get(), classes.begin(), classes.end());
     bool result = info.Save(profile_test_fd);
     close(profile_test_fd);
     ASSERT_TRUE(result);
   }
 
+  // Compiles a dex file with profiles.
   void CompileProfileOdex(const std::string& dex_location,
                           const std::string& odex_location,
                           const std::string& app_image_file_name,
                           bool use_fd,
-                          size_t num_profile_classes,
+                          const std::vector<std::string>& profile_locations,
                           const std::vector<std::string>& extra_args = {},
                           bool expect_success = true) {
-    const std::string profile_location = GetScratchDir() + "/primary.prof";
-    const char* location = dex_location.c_str();
-    std::string error_msg;
-    std::vector<std::unique_ptr<const DexFile>> dex_files;
-    const ArtDexFileLoader dex_file_loader;
-    ASSERT_TRUE(dex_file_loader.Open(
-        location, location, /*verify=*/ true, /*verify_checksum=*/ true, &error_msg, &dex_files));
-    EXPECT_EQ(dex_files.size(), 1U);
-    std::unique_ptr<const DexFile>& dex_file = dex_files[0];
-    GenerateProfile(profile_location, dex_file.get(), num_profile_classes);
     std::vector<std::string> copy(extra_args);
-    copy.push_back("--profile-file=" + profile_location);
+    for (const std::string& profile_location : profile_locations) {
+      copy.push_back("--profile-file=" + profile_location);
+    }
     std::unique_ptr<File> app_image_file;
     if (!app_image_file_name.empty()) {
       if (use_fd) {
@@ -670,6 +672,26 @@
     }
   }
 
+  // Same as above, but generates the profile internally with classes ranging from 0 to
+  // `num_profile_classes`.
+  void CompileProfileOdex(const std::string& dex_location,
+                          const std::string& odex_location,
+                          const std::string& app_image_file_name,
+                          bool use_fd,
+                          size_t num_profile_classes,
+                          const std::vector<std::string>& extra_args = {},
+                          bool expect_success = true) {
+    const std::string profile_location = GetScratchDir() + "/primary.prof";
+    GenerateProfile(profile_location, dex_location, num_profile_classes);
+    CompileProfileOdex(dex_location,
+                       odex_location,
+                       app_image_file_name,
+                       use_fd,
+                       {profile_location},
+                       extra_args,
+                       expect_success);
+  }
+
   uint64_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()));
@@ -850,6 +872,45 @@
   RunTest(/*app_image=*/ true);
 }
 
+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";
+  Copy(GetDexSrc2(), dex_location);
+
+  const std::string profile1_location = GetScratchDir() + "/primary.prof";
+  GenerateProfile(profile1_location, dex_location, /*num_classes=*/1, /*class_offset=*/0);
+  CompileProfileOdex(dex_location,
+                     odex_location,
+                     app_image_file,
+                     /*use_fd=*/false,
+                     {profile1_location});
+  uint64_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);
+  CompileProfileOdex(dex_location,
+                     odex_location,
+                     app_image_file,
+                     /*use_fd=*/false,
+                     {profile2_location});
+  uint64_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);
+
+  CheckCompilerFilter(dex_location, odex_location, CompilerFilter::Filter::kSpeedProfile);
+
+  // The image file generated with multiple profiles should be larger than any image file generated
+  // with each profile.
+  ASSERT_GT(image_file_size_multiple_profiles, image_file_size_profile1);
+  ASSERT_GT(image_file_size_multiple_profiles, image_file_size_profile2);
+}
+
 TEST_F(Dex2oatLayoutTest, TestVdexLayout) {
   RunTestVDex();
 }