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/compiler/Android.mk b/compiler/Android.mk
index f0bf499..4589736 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -108,7 +108,8 @@
 	elf_writer_debug.cc \
 	elf_writer_quick.cc \
 	image_writer.cc \
-	oat_writer.cc
+	oat_writer.cc \
+	profile_assistant.cc
 
 LIBART_COMPILER_SRC_FILES_arm := \
 	dex/quick/arm/assemble_arm.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 278c490..b5fd1e0 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -208,8 +208,8 @@
                                             false,
                                             timer_.get(),
                                             -1,
-                                            /* profile_file */ "",
-                                            /* dex_to_oat_map */ nullptr));
+                                            /* dex_to_oat_map */ nullptr,
+                                            /* profile_compilation_info */ nullptr));
   // We typically don't generate an image in unit tests, disable this optimization by default.
   compiler_driver_->SetSupportBootImageFixup(false);
 }
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index bcf20c7..12568a4 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -92,7 +92,7 @@
                           false,
                           0,
                           -1,
-                          "",
+                          nullptr,
                           nullptr);
     ClassLinker* linker = nullptr;
     CompilationUnit cu(&pool, isa, &driver, linker);
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index 9deabc0..b39fe4d 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -73,7 +73,7 @@
         false,
         0,
         -1,
-        "",
+        nullptr,
         nullptr));
     cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr));
     DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 84fb432..f18fa67 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -45,7 +45,7 @@
                         false,
                         nullptr,
                         -1,
-                        "",
+                        nullptr,
                         nullptr);
   CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index afb4b71..043bd93 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -347,8 +347,8 @@
     size_t thread_count, bool dump_stats, bool dump_passes,
     const std::string& dump_cfg_file_name, bool dump_cfg_append,
     CumulativeLogger* timer, int swap_fd,
-    const std::string& profile_file,
-    const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map)
+    const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map,
+    const ProfileCompilationInfo* profile_compilation_info)
     : compiler_options_(compiler_options),
       verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
@@ -377,7 +377,8 @@
       support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
       dex_files_for_oat_file_(nullptr),
       dex_file_oat_filename_map_(dex_to_oat_map),
-      compiled_method_storage_(swap_fd) {
+      compiled_method_storage_(swap_fd),
+      profile_compilation_info_(profile_compilation_info) {
   DCHECK(compiler_options_ != nullptr);
   DCHECK(verification_results_ != nullptr);
   DCHECK(method_inliner_map_ != nullptr);
@@ -385,12 +386,6 @@
   compiler_->Init();
 
   CHECK_EQ(boot_image_, image_classes_.get() != nullptr);
-
-  // Read the profile file if one is provided.
-  if (!profile_file.empty()) {
-    profile_compilation_info_.reset(new ProfileCompilationInfo(profile_file));
-    LOG(INFO) << "Using profile data from file " << profile_file;
-  }
 }
 
 CompilerDriver::~CompilerDriver() {
@@ -2306,15 +2301,11 @@
 
 void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                              ThreadPool* thread_pool, TimingLogger* timings) {
-  if (profile_compilation_info_ != nullptr) {
-    if (!profile_compilation_info_->Load(dex_files)) {
-      LOG(WARNING) << "Failed to load offline profile info from "
-          << profile_compilation_info_->GetFilename()
-          << ". No methods will be compiled";
-    } else if (kDebugProfileGuidedCompilation) {
-      LOG(INFO) << "[ProfileGuidedCompilation] "
-          << profile_compilation_info_->DumpInfo();
-    }
+  if (kDebugProfileGuidedCompilation) {
+    LOG(INFO) << "[ProfileGuidedCompilation] " <<
+        ((profile_compilation_info_ == nullptr)
+            ? "null"
+            : profile_compilation_info_->DumpInfo(&dex_files));
   }
   for (size_t i = 0; i != dex_files.size(); ++i) {
     const DexFile* dex_file = dex_files[i];
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index fa0cb9a..3847c81 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -97,8 +97,8 @@
                  size_t thread_count, bool dump_stats, bool dump_passes,
                  const std::string& dump_cfg_file_name, bool dump_cfg_append,
                  CumulativeLogger* timer, int swap_fd,
-                 const std::string& profile_file,
-                 const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map);
+                 const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map,
+                 const ProfileCompilationInfo* profile_compilation_info);
 
   ~CompilerDriver();
 
@@ -657,9 +657,6 @@
   // This option may be restricted to the boot image, depending on a flag in the implementation.
   std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
 
-  // Info for profile guided compilation.
-  std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
-
   bool had_hard_verifier_failure_;
 
   size_t thread_count_;
@@ -689,6 +686,9 @@
 
   CompiledMethodStorage compiled_method_storage_;
 
+  // Info for profile guided compilation.
+  const ProfileCompilationInfo* const profile_compilation_info_;
+
   friend class CompileClassVisitor;
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index b323d24..85216b7 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -155,8 +155,8 @@
       /* dump_cfg_append */ false,
       cumulative_logger_.get(),
       /* swap_fd */ -1,
-      /* profile_file */ "",
-      /* dex to oat map */ nullptr));
+      /* dex to oat map */ nullptr,
+      /* profile_compilation_info */ nullptr));
   // Disable dedupe so we can remove compiled methods.
   compiler_driver_->SetDedupeEnabled(false);
   compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 877a674..b10cc35 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -47,7 +47,7 @@
         driver_(&compiler_options_, &verification_results_, &inliner_map_,
                 Compiler::kQuick, instruction_set, nullptr,
                 false, nullptr, nullptr, nullptr, 1u,
-                false, false, "", false, nullptr, -1, "", nullptr),
+                false, false, "", false, nullptr, -1, nullptr, nullptr),
         error_msg_(),
         instruction_set_(instruction_set),
         features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 58f46d6..9f7ffa5 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -121,7 +121,7 @@
                                               false,
                                               timer_.get(),
                                               -1,
-                                              "",
+                                              nullptr,
                                               nullptr));
   }
 
diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc
new file mode 100644
index 0000000..81f2a56
--- /dev/null
+++ b/compiler/profile_assistant.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "profile_assistant.h"
+
+namespace art {
+
+// Minimum number of new methods that profiles must contain to enable recompilation.
+static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
+
+bool ProfileAssistant::ProcessProfiles(
+      const std::vector<std::string>& profile_files,
+      const std::vector<std::string>& reference_profile_files,
+      /*out*/ ProfileCompilationInfo** profile_compilation_info) {
+  DCHECK(!profile_files.empty());
+  DCHECK(reference_profile_files.empty() ||
+      (profile_files.size() == reference_profile_files.size()));
+
+  std::vector<ProfileCompilationInfo> new_info(profile_files.size());
+  bool should_compile = false;
+  // Read the main profile files.
+  for (size_t i = 0; i < profile_files.size(); i++) {
+    if (!new_info[i].Load(profile_files[i])) {
+      LOG(WARNING) << "Could not load profile file: " << profile_files[i];
+      return false;
+    }
+    // Do we have enough new profiled methods that will make the compilation worthwhile?
+    should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation);
+  }
+  if (!should_compile) {
+    *profile_compilation_info = nullptr;
+    return true;
+  }
+
+  std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo());
+  for (size_t i = 0; i < new_info.size(); i++) {
+    // Merge all data into a single object.
+    result->Load(new_info[i]);
+    // If we have any reference profile information merge their information with
+    // the current profiles and save them back to disk.
+    if (!reference_profile_files.empty()) {
+      if (!new_info[i].Load(reference_profile_files[i])) {
+        LOG(WARNING) << "Could not load reference profile file: " << reference_profile_files[i];
+        return false;
+      }
+      if (!new_info[i].Save(reference_profile_files[i])) {
+        LOG(WARNING) << "Could not save reference profile file: " << reference_profile_files[i];
+        return false;
+      }
+    }
+  }
+  *profile_compilation_info = result.release();
+  return true;
+}
+
+}  // namespace art
diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h
new file mode 100644
index 0000000..088c8bd
--- /dev/null
+++ b/compiler/profile_assistant.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_PROFILE_ASSISTANT_H_
+#define ART_COMPILER_PROFILE_ASSISTANT_H_
+
+#include <string>
+#include <vector>
+
+#include "jit/offline_profiling_info.cc"
+
+namespace art {
+
+class ProfileAssistant {
+ public:
+  // Process the profile information present in the given files. Returns true
+  // if the analysis ended up successfully (i.e. no errors during reading,
+  // merging or writing of profile files).
+  //
+  // If the returned value is true and there is a significant difference between
+  // profile_files and reference_profile_files:
+  //   - profile_compilation_info is set to a not null object that
+  //     can be used to drive compilation. It will be the merge of all the data
+  //     found in profile_files and reference_profile_files.
+  //   - the data from profile_files[i] is merged into
+  //     reference_profile_files[i] and the corresponding backing file is
+  //     updated.
+  //
+  // If the returned value is false or the difference is insignificant,
+  // profile_compilation_info will be set to null.
+  //
+  // Additional notes:
+  //   - as mentioned above, this function may update the content of the files
+  //     passed with the reference_profile_files.
+  //   - if reference_profile_files is not empty it must be the same size as
+  //     profile_files.
+  static bool ProcessProfiles(
+      const std::vector<std::string>& profile_files,
+      const std::vector<std::string>& reference_profile_files,
+      /*out*/ ProfileCompilationInfo** profile_compilation_info);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProfileAssistant);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_PROFILE_ASSISTANT_H_