Perform profile file analysis in dex2oat

Dex2oat can accept now multiple profile files to drive a profile based
compilation. --profile-file and --reference-profile-file speficy a pair
of profile files which will be evaluated for significant differences
before starting the compilation. If the difference is insignificant
(according to some internal metric) the compilation is skipped and a
message is logged.

Multiple pairs of --profile-file and --reference-profile-file can be
specified. This effectively enables multi user support since profiles
for different users will be kept separately.

--reference-profile-file can be left out, case in which the decision is
solely based on --profile-file. If both flags are present, then their
repetition should form unique pairs.

If the compilation is performed and --reference-profile-file is given
then its data is merged with the data from the corresponding --profile-
file and saved back to the file.

If no profile flags are given, dex2oat proceeds as before and compiles
the dex files unconditionally.

As part of this change
- merge ProfileCompilationInfo and OfflineProfilingInfo under the same
object. There was no use to keep them separate anymore.
- SaveProfilingInfo now merges the data with what was in
the file before instead of overwriting it.

Bug: 26080105

Change-Id: Ia8c8b55587d468bca5179f78941854285426234d
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 50480d9..c4f68ea 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -63,6 +63,7 @@
 #include "gc/space/space-inl.h"
 #include "image_writer.h"
 #include "interpreter/unstarted_runtime.h"
+#include "jit/offline_profiling_info.h"
 #include "leb128.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -70,6 +71,7 @@
 #include "mirror/object_array-inl.h"
 #include "oat_writer.h"
 #include "os.h"
+#include "profile_assistant.h"
 #include "runtime.h"
 #include "runtime_options.h"
 #include "ScopedLocalRef.h"
@@ -328,6 +330,16 @@
   UsageError("      Example: --runtime-arg -Xms256m");
   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("");
+  UsageError("  --reference-profile-file=<filename>: specify a reference profile file to use when");
+  UsageError("      compiling. The data in this file will be compared with the data in the");
+  UsageError("      associated --profile-file and the compilation will proceed only if there is");
+  UsageError("      a significant difference (--reference-profile-file is paired with");
+  UsageError("      --profile-file in the natural order). If the compilation was attempted then");
+  UsageError("      --profile-file will be merged into --reference-profile-file. Valid only when");
+  UsageError("      specified together with --profile-file.");
   UsageError("");
   UsageError("  --print-pass-names: print a list of pass names");
   UsageError("");
@@ -767,6 +779,13 @@
       }
     }
 
+    if (!profile_files_.empty()) {
+      if (!reference_profile_files_.empty() &&
+          (reference_profile_files_.size() != profile_files_.size())) {
+        Usage("If specified, --reference-profile-file should match the number of --profile-file.");
+      }
+    }
+
     if (!parser_options->oat_symbols.empty()) {
       oat_unstripped_ = std::move(parser_options->oat_symbols);
     }
@@ -1057,8 +1076,10 @@
       } else if (option.starts_with("--compiler-backend=")) {
         ParseCompilerBackend(option, parser_options.get());
       } else if (option.starts_with("--profile-file=")) {
-        profile_file_ = option.substr(strlen("--profile-file=")).data();
-        VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
+        profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
+      } else if (option.starts_with("--reference-profile-file=")) {
+        reference_profile_files_.push_back(
+            option.substr(strlen("--reference-profile-file=")).ToString());
       } else if (option == "--no-profile-file") {
         // No profile
       } else if (option == "--host") {
@@ -1479,9 +1500,8 @@
                                      dump_cfg_append_,
                                      compiler_phases_timings_.get(),
                                      swap_fd_,
-                                     profile_file_,
-                                     &dex_file_oat_filename_map_));
-
+                                     &dex_file_oat_filename_map_,
+                                     profile_compilation_info_.get()));
     driver_->SetDexFilesForOatFile(dex_files_);
     driver_->CompileAll(class_loader, dex_files_, timings_);
   }
@@ -1790,6 +1810,26 @@
     return is_host_;
   }
 
+  bool UseProfileGuidedCompilation() const {
+    return !profile_files_.empty();
+  }
+
+  bool ProcessProfiles() {
+    DCHECK(UseProfileGuidedCompilation());
+    ProfileCompilationInfo* info = nullptr;
+    if (ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_files_, &info)) {
+      profile_compilation_info_.reset(info);
+      return true;
+    }
+    return false;
+  }
+
+  bool ShouldCompileBasedOnProfiles() const {
+    DCHECK(UseProfileGuidedCompilation());
+    // If we are given profiles, compile only if we have new information.
+    return profile_compilation_info_ != nullptr;
+  }
+
  private:
   template <typename T>
   static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
@@ -2263,7 +2303,9 @@
   int swap_fd_;
   std::string app_image_file_name_;
   int app_image_fd_;
-  std::string profile_file_;  // Profile file to use
+  std::vector<std::string> profile_files_;
+  std::vector<std::string> reference_profile_files_;
+  std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
   TimingLogger* timings_;
   std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
   std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_;
@@ -2380,6 +2422,20 @@
   // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
   dex2oat.ParseArgs(argc, argv);
 
+  // Process profile information and assess if we need to do a profile guided compilation.
+  // This operation involves I/O.
+  if (dex2oat.UseProfileGuidedCompilation()) {
+    if (dex2oat.ProcessProfiles()) {
+      if (!dex2oat.ShouldCompileBasedOnProfiles()) {
+        LOG(INFO) << "Skipped compilation because of insignificant profile delta";
+        return EXIT_SUCCESS;
+      }
+    } else {
+      LOG(WARNING) << "Failed to process profile files";
+      return EXIT_FAILURE;
+    }
+  }
+
   // Check early that the result of compilation can be written
   if (!dex2oat.OpenFile()) {
     return EXIT_FAILURE;