OSDN Git Service

Perform profile file analysis in dex2oat
authorCalin Juravle <calin@google.com>
Mon, 21 Dec 2015 13:39:33 +0000 (15:39 +0200)
committerCalin Juravle <calin@google.com>
Thu, 7 Jan 2016 16:23:18 +0000 (16:23 +0000)
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

17 files changed:
compiler/Android.mk
compiler/common_compiler_test.cc
compiler/dex/quick/quick_cfi_test.cc
compiler/dex/quick/x86/quick_assemble_x86_test.cc
compiler/driver/compiled_method_storage_test.cc
compiler/driver/compiler_driver.cc
compiler/driver/compiler_driver.h
compiler/jit/jit_compiler.cc
compiler/linker/relative_patcher_test.h
compiler/oat_test.cc
compiler/profile_assistant.cc [new file with mode: 0644]
compiler/profile_assistant.h [new file with mode: 0644]
dex2oat/dex2oat.cc
runtime/jit/offline_profiling_info.cc
runtime/jit/offline_profiling_info.h
runtime/jit/profile_saver.cc
runtime/jit/profile_saver.h

index f0bf499..4589736 100644 (file)
@@ -108,7 +108,8 @@ LIBART_COMPILER_SRC_FILES := \
        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 \
index 278c490..b5fd1e0 100644 (file)
@@ -208,8 +208,8 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe
                                             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);
 }
index bcf20c7..12568a4 100644 (file)
@@ -92,7 +92,7 @@ class QuickCFITest : public CFITest {
                           false,
                           0,
                           -1,
-                          "",
+                          nullptr,
                           nullptr);
     ClassLinker* linker = nullptr;
     CompilationUnit cu(&pool, isa, &driver, linker);
index 9deabc0..b39fe4d 100644 (file)
@@ -73,7 +73,7 @@ class QuickAssembleX86TestBase : public testing::Test {
         false,
         0,
         -1,
-        "",
+        nullptr,
         nullptr));
     cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr));
     DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
index 84fb432..f18fa67 100644 (file)
@@ -45,7 +45,7 @@ TEST(CompiledMethodStorage, Deduplicate) {
                         false,
                         nullptr,
                         -1,
-                        "",
+                        nullptr,
                         nullptr);
   CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
 
index afb4b71..043bd93 100644 (file)
@@ -347,8 +347,8 @@ CompilerDriver::CompilerDriver(
     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 @@ CompilerDriver::CompilerDriver(
       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 @@ CompilerDriver::CompilerDriver(
   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::InitializeClasses(jobject class_loader,
 
 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];
index fa0cb9a..3847c81 100644 (file)
@@ -97,8 +97,8 @@ class CompilerDriver {
                  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 @@ class CompilerDriver {
   // 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 @@ class CompilerDriver {
 
   CompiledMethodStorage compiled_method_storage_;
 
+  // Info for profile guided compilation.
+  const ProfileCompilationInfo* const profile_compilation_info_;
+
   friend class CompileClassVisitor;
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
index b323d24..85216b7 100644 (file)
@@ -155,8 +155,8 @@ JitCompiler::JitCompiler() : total_time_(0) {
       /* 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);
index 877a674..b10cc35 100644 (file)
@@ -47,7 +47,7 @@ class RelativePatcherTest : public testing::Test {
         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_)),
index 58f46d6..9f7ffa5 100644 (file)
@@ -121,7 +121,7 @@ class OatTest : public CommonCompilerTest {
                                               false,
                                               timer_.get(),
                                               -1,
-                                              "",
+                                              nullptr,
                                               nullptr));
   }
 
diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc
new file mode 100644 (file)
index 0000000..81f2a56
--- /dev/null
@@ -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 (file)
index 0000000..088c8bd
--- /dev/null
@@ -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_
index 50480d9..c4f68ea 100644 (file)
@@ -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 @@ NO_RETURN static void Usage(const char* fmt, ...) {
   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 @@ class Dex2Oat FINAL {
       }
     }
 
+    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 @@ class Dex2Oat FINAL {
       } 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 @@ class Dex2Oat FINAL {
                                      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 @@ class Dex2Oat FINAL {
     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 @@ class Dex2Oat FINAL {
   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 @@ static int dex2oat(int argc, char** argv) {
   // 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;
index 3942b0b..a132701 100644 (file)
 
 namespace art {
 
-void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
-                                             const std::vector<ArtMethod*>& methods) {
+bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename,
+                                               const std::vector<ArtMethod*>& methods) {
   if (methods.empty()) {
     VLOG(profiler) << "No info to save to " << filename;
-    return;
+    return true;
   }
 
-  DexFileToMethodsMap info;
+  ProfileCompilationInfo info;
+  if (!info.Load(filename)) {
+    LOG(WARNING) << "Could not load previous profile data from file " << filename;
+    return false;
+  }
   {
     ScopedObjectAccess soa(Thread::Current());
     for (auto it = methods.begin(); it != methods.end(); it++) {
-      AddMethodInfo(*it, &info);
+      const DexFile* dex_file = (*it)->GetDexFile();
+      if (!info.AddData(dex_file->GetLocation(),
+                        dex_file->GetLocationChecksum(),
+                        (*it)->GetDexMethodIndex())) {
+        return false;
+      }
     }
   }
 
   // This doesn't need locking because we are trying to lock the file for exclusive
   // access and fail immediately if we can't.
-  if (Serialize(filename, info)) {
+  bool result = info.Save(filename);
+  if (result) {
     VLOG(profiler) << "Successfully saved profile info to " << filename
         << " Size: " << GetFileSizeBytes(filename);
+  } else {
+    VLOG(profiler) << "Failed to save profile info to " << filename;
   }
-}
-
-void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) {
-  DCHECK(method != nullptr);
-  const DexFile* dex_file = method->GetDexFile();
-
-  auto info_it = info->find(dex_file);
-  if (info_it == info->end()) {
-    info_it = info->Put(dex_file, std::set<uint32_t>());
-  }
-  info_it->second.insert(method->GetDexMethodIndex());
+  return result;
 }
 
 enum OpenMode {
@@ -135,8 +137,7 @@ static constexpr const char kLineSeparator = '\n';
  *    /system/priv-app/app/app.apk,131232145,11,23,454,54
  *    /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1
  **/
-bool OfflineProfilingInfo::Serialize(const std::string& filename,
-                                     const DexFileToMethodsMap& info) const {
+bool ProfileCompilationInfo::Save(const std::string& filename) {
   int fd = OpenFile(filename, READ_WRITE);
   if (fd == -1) {
     return false;
@@ -146,14 +147,12 @@ bool OfflineProfilingInfo::Serialize(const std::string& filename,
   // TODO(calin): Profile this and see how much memory it takes. If too much,
   // write to file directly.
   std::ostringstream os;
-  for (auto it : info) {
-    const DexFile* dex_file = it.first;
-    const std::set<uint32_t>& method_dex_ids = it.second;
-
-    os << dex_file->GetLocation()
-        << kFieldSeparator
-        << dex_file->GetLocationChecksum();
-    for (auto method_it : method_dex_ids) {
+  for (const auto& it : info_) {
+    const std::string& dex_location = it.first;
+    const DexFileData& dex_data = it.second;
+
+    os << dex_location << kFieldSeparator << dex_data.checksum;
+    for (auto method_it : dex_data.method_set) {
       os << kFieldSeparator << method_it;
     }
     os << kLineSeparator;
@@ -188,8 +187,22 @@ static void SplitString(const std::string& s, char separator, std::vector<std::s
   }
 }
 
-bool ProfileCompilationInfo::ProcessLine(const std::string& line,
-                                         const std::vector<const DexFile*>& dex_files) {
+bool ProfileCompilationInfo::AddData(const std::string& dex_location,
+                                     uint32_t checksum,
+                                     uint16_t method_idx) {
+  auto info_it = info_.find(dex_location);
+  if (info_it == info_.end()) {
+    info_it = info_.Put(dex_location, DexFileData(checksum));
+  }
+  if (info_it->second.checksum != checksum) {
+    LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
+    return false;
+  }
+  info_it->second.method_set.insert(method_idx);
+  return true;
+}
+
+bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
   std::vector<std::string> parts;
   SplitString(line, kFieldSeparator, &parts);
   if (parts.size() < 3) {
@@ -203,39 +216,13 @@ bool ProfileCompilationInfo::ProcessLine(const std::string& line,
     return false;
   }
 
-  const DexFile* current_dex_file = nullptr;
-  for (auto dex_file : dex_files) {
-    if (dex_file->GetLocation() == dex_location) {
-      if (checksum != dex_file->GetLocationChecksum()) {
-        LOG(WARNING) << "Checksum mismatch for "
-            << dex_file->GetLocation() << " when parsing " << filename_;
-        return false;
-      }
-      current_dex_file = dex_file;
-      break;
-    }
-  }
-  if (current_dex_file == nullptr) {
-    return true;
-  }
-
   for (size_t i = 2; i < parts.size(); i++) {
     uint32_t method_idx;
     if (!ParseInt(parts[i].c_str(), &method_idx)) {
       LOG(WARNING) << "Cannot parse method_idx " << parts[i];
       return false;
     }
-    uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_;
-    auto info_it = info_.find(current_dex_file);
-    if (info_it == info_.end()) {
-      info_it = info_.Put(current_dex_file, ClassToMethodsMap());
-    }
-    ClassToMethodsMap& class_map = info_it->second;
-    auto class_it = class_map.find(class_idx);
-    if (class_it == class_map.end()) {
-      class_it = class_map.Put(class_idx, std::set<uint32_t>());
-    }
-    class_it->second.insert(method_idx);
+    AddData(dex_location, checksum, method_idx);
   }
   return true;
 }
@@ -262,25 +249,8 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l
   return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
 }
 
-bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) {
-  if (dex_files.empty()) {
-    return true;
-  }
-  if (kIsDebugBuild) {
-    // In debug builds verify that the locations are unique.
-    std::set<std::string> locations;
-    for (auto dex_file : dex_files) {
-      const std::string& location = dex_file->GetLocation();
-      DCHECK(locations.find(location) == locations.end())
-          << "DexFiles appear to belong to different apks."
-          << " There are multiple dex files with the same location: "
-          << location;
-      locations.insert(location);
-    }
-  }
-  info_.clear();
-
-  int fd = OpenFile(filename_, READ);
+bool ProfileCompilationInfo::Load(const std::string& filename) {
+  int fd = OpenFile(filename, READ);
   if (fd == -1) {
     return false;
   }
@@ -293,7 +263,7 @@ bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files)
   while (success) {
     int n = read(fd, buffer, kBufferSize);
     if (n < 0) {
-      PLOG(WARNING) << "Error when reading profile file " << filename_;
+      PLOG(WARNING) << "Error when reading profile file " << filename;
       success = false;
       break;
     } else if (n == 0) {
@@ -307,7 +277,7 @@ bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files)
       if (current_start_pos == -1) {
         break;
       }
-      if (!ProcessLine(current_line, dex_files)) {
+      if (!ProcessLine(current_line)) {
         success = false;
         break;
       }
@@ -318,25 +288,50 @@ bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files)
   if (!success) {
     info_.clear();
   }
-  return CloseDescriptorForFile(fd, filename_) && success;
+  return CloseDescriptorForFile(fd, filename) && success;
+}
+
+bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+  for (const auto& other_it : other.info_) {
+    const std::string& other_dex_location = other_it.first;
+    const DexFileData& other_dex_data = other_it.second;
+
+    auto info_it = info_.find(other_dex_location);
+    if (info_it == info_.end()) {
+      info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
+    }
+    if (info_it->second.checksum != other_dex_data.checksum) {
+      LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
+      return false;
+    }
+    info_it->second.method_set.insert(other_dex_data.method_set.begin(),
+                                      other_dex_data.method_set.end());
+  }
+  return true;
 }
 
 bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
-  auto info_it = info_.find(method_ref.dex_file);
+  auto info_it = info_.find(method_ref.dex_file->GetLocation());
   if (info_it != info_.end()) {
-    uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_;
-    const ClassToMethodsMap& class_map = info_it->second;
-    auto class_it = class_map.find(class_idx);
-    if (class_it != class_map.end()) {
-      const std::set<uint32_t>& methods = class_it->second;
-      return methods.find(method_ref.dex_method_index) != methods.end();
+    if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
+      return false;
     }
-    return false;
+    const std::set<uint16_t>& methods = info_it->second.method_set;
+    return methods.find(method_ref.dex_method_index) != methods.end();
   }
   return false;
 }
 
-std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const {
+uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
+  uint32_t total = 0;
+  for (const auto& it : info_) {
+    total += it.second.method_set.size();
+  }
+  return total;
+}
+
+std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
+                                             bool print_full_dex_location) const {
   std::ostringstream os;
   if (info_.empty()) {
     return "ProfileInfo: empty";
@@ -344,17 +339,11 @@ std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const
 
   os << "ProfileInfo:";
 
-  // Use an additional map to achieve a predefined order based on the dex locations.
-  SafeMap<const std::string, const DexFile*> dex_locations_map;
-  for (auto info_it : info_) {
-    dex_locations_map.Put(info_it.first->GetLocation(), info_it.first);
-  }
-
   const std::string kFirstDexFileKeySubstitute = ":classes.dex";
-  for (auto dex_file_it : dex_locations_map) {
+  for (const auto& it : info_) {
     os << "\n";
-    const std::string& location = dex_file_it.first;
-    const DexFile* dex_file = dex_file_it.second;
+    const std::string& location = it.first;
+    const DexFileData& dex_data = it.second;
     if (print_full_dex_location) {
       os << location;
     } else {
@@ -362,10 +351,19 @@ std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const
       std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
       os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
     }
-    for (auto class_it : info_.find(dex_file)->second) {
-      for (auto method_it : class_it.second) {
-        os << "\n  " << PrettyMethod(method_it, *dex_file, true);
+    for (const auto method_it : dex_data.method_set) {
+      if (dex_files != nullptr) {
+        const DexFile* dex_file = nullptr;
+        for (size_t i = 0; i < dex_files->size(); i++) {
+          if (location == (*dex_files)[i]->GetLocation()) {
+            dex_file = (*dex_files)[i];
+          }
+        }
+        if (dex_file != nullptr) {
+          os << "\n  " << PrettyMethod(method_it, *dex_file, true);
+        }
       }
+      os << "\n  " << method_it;
     }
   }
   return os.str();
index 32d4c5b..26e1ac3 100644 (file)
@@ -29,60 +29,50 @@ namespace art {
 
 class ArtMethod;
 
+// TODO: rename file.
 /**
- * Profiling information in a format that can be serialized to disk.
- * It is a serialize-friendly format based on information collected
- * by the interpreter (ProfileInfo).
+ * Profile information in a format suitable to be queried by the compiler and
+ * performing profile guided compilation.
+ * It is a serialize-friendly format based on information collected by the
+ * interpreter (ProfileInfo).
  * Currently it stores only the hot compiled methods.
  */
-class OfflineProfilingInfo {
- public:
-  void SaveProfilingInfo(const std::string& filename, const std::vector<ArtMethod*>& methods);
-
- private:
-  // Map identifying the location of the profiled methods.
-  // dex_file_ -> [dex_method_index]+
-  using DexFileToMethodsMap = SafeMap<const DexFile*, std::set<uint32_t>>;
-
-  void AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info)
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const;
-};
-
-/**
- * Profile information in a format suitable to be queried by the compiler and performing
- * profile guided compilation.
- */
 class ProfileCompilationInfo {
  public:
-  // Constructs a ProfileCompilationInfo backed by the provided file.
-  explicit ProfileCompilationInfo(const std::string& filename) : filename_(filename) {}
-
-  // Loads profile information corresponding to the provided dex files.
-  // The dex files' multidex suffixes must be unique.
-  // This resets the state of the profiling information
-  // (i.e. all previously loaded info are cleared).
-  bool Load(const std::vector<const DexFile*>& dex_files);
+  static bool SaveProfilingInfo(const std::string& filename,
+                                const std::vector<ArtMethod*>& methods);
+
+  // Loads profile information from the given file.
+  bool Load(const std::string& profile_filename);
+  // Loads the data from another ProfileCompilationInfo object.
+  bool Load(const ProfileCompilationInfo& info);
+  // Saves the profile data to the given file.
+  bool Save(const std::string& profile_filename);
+  // Returns the number of methods that were profiled.
+  uint32_t GetNumberOfMethods() const;
 
   // Returns true if the method reference is present in the profiling info.
   bool ContainsMethod(const MethodReference& method_ref) const;
 
-  const std::string& GetFilename() const { return filename_; }
-
   // Dumps all the loaded profile info into a string and returns it.
+  // If dex_files is not null then the method indices will be resolved to their
+  // names.
   // This is intended for testing and debugging.
-  std::string DumpInfo(bool print_full_dex_location = true) const;
+  std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
+                       bool print_full_dex_location = true) const;
 
  private:
-  bool ProcessLine(const std::string& line,
-                   const std::vector<const DexFile*>& dex_files);
+  bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+  bool ProcessLine(const std::string& line);
+
+  struct DexFileData {
+    explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
+    uint32_t checksum;
+    std::set<uint16_t> method_set;
+  };
 
-  using ClassToMethodsMap = SafeMap<uint32_t, std::set<uint32_t>>;
-  // Map identifying the location of the profiled methods.
-  // dex_file -> class_index -> [dex_method_index]+
-  using DexFileToProfileInfoMap = SafeMap<const DexFile*, ClassToMethodsMap>;
+  using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>;
 
-  const std::string filename_;
   DexFileToProfileInfoMap info_;
 };
 
index 0278138..ec289ea 100644 (file)
@@ -106,10 +106,9 @@ bool ProfileSaver::ProcessProfilingInfo() {
     VLOG(profiler) << "Not enough information to save. Nr of methods: " << methods.size();
     return false;
   }
-  offline_profiling_info_.SaveProfilingInfo(output_filename_, methods);
-
-  VLOG(profiler) << "Saved profile time: " << PrettyDuration(NanoTime() - start);
 
+  ProfileCompilationInfo::SaveProfilingInfo(output_filename_, methods);
+  VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
   return true;
 }
 
index 88efd41..d60142b 100644 (file)
@@ -66,7 +66,6 @@ class ProfileSaver {
   const std::string output_filename_;
   jit::JitCodeCache* jit_code_cache_;
   const std::set<const std::string> tracked_dex_base_locations_;
-  OfflineProfilingInfo offline_profiling_info_;
   uint64_t code_cache_last_update_time_ns_;
   bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);