Tweak ProfileSaver saving strategy

To minimize the I/O, we used to store the number of methods/classes we
last saved globally in the profiler saver. This is no longer viable once
we want to track secondary dex files for profiling because each file
might save a different set of methods.

To make sure we do not miss data for secondary dex profiles,
store the number of last saved methods separetely for each profile file.

Test: test-art-host
Bug: 26719109
Change-Id: I3a657f63d26f68c7ca83a754f6e4aa2c9d946176
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 00487c6..2724b00 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -42,8 +42,6 @@
                            const std::vector<std::string>& code_paths)
     : jit_code_cache_(jit_code_cache),
       shutting_down_(false),
-      last_save_number_of_methods_(0),
-      last_save_number_of_classes_(0),
       last_time_ns_saver_woke_up_(0),
       jit_activity_notifications_(0),
       wait_lock_("ProfileSaver wait lock"),
@@ -171,10 +169,10 @@
   }
 }
 
-ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
+ProfileSaver::ProfileInfoCache* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
   auto info_it = profile_cache_.find(filename);
   if (info_it == profile_cache_.end()) {
-    info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
+    info_it = profile_cache_.Put(filename, ProfileInfoCache());
   }
   return &info_it->second;
 }
@@ -248,8 +246,9 @@
                        << " (" << classes.GetDexLocation() << ")";
       }
     }
-    ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
-    info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location);
+    ProfileInfoCache* cached_info = GetCachedProfiledInfo(filename);
+    cached_info->profile.AddMethodsAndClasses(profile_methods_for_location,
+                                              resolved_classes_for_location);
     total_number_of_profile_entries_cached += resolved_classes_for_location.size();
   }
   max_number_of_profile_entries_cached_ = std::max(
@@ -283,14 +282,15 @@
       total_number_of_code_cache_queries_++;
     }
 
-    ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
-    cached_info->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
+    ProfileInfoCache* cached_info = GetCachedProfiledInfo(filename);
+    ProfileCompilationInfo* cached_profile = &cached_info->profile;
+    cached_profile->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
     int64_t delta_number_of_methods =
-        cached_info->GetNumberOfMethods() -
-        static_cast<int64_t>(last_save_number_of_methods_);
+        cached_profile->GetNumberOfMethods() -
+        static_cast<int64_t>(cached_info->last_save_number_of_methods);
     int64_t delta_number_of_classes =
-        cached_info->GetNumberOfResolvedClasses() -
-        static_cast<int64_t>(last_save_number_of_classes_);
+        cached_profile->GetNumberOfResolvedClasses() -
+        static_cast<int64_t>(cached_info->last_save_number_of_classes);
 
     if (delta_number_of_methods < options_.GetMinMethodsToSave() &&
         delta_number_of_classes < options_.GetMinClassesToSave()) {
@@ -304,12 +304,12 @@
     uint64_t bytes_written;
     // Force the save. In case the profile data is corrupted or the the profile
     // has the wrong version this will "fix" the file to the correct format.
-    if (cached_info->MergeAndSave(filename, &bytes_written, /*force*/ true)) {
-      last_save_number_of_methods_ = cached_info->GetNumberOfMethods();
-      last_save_number_of_classes_ = cached_info->GetNumberOfResolvedClasses();
+    if (cached_profile->MergeAndSave(filename, &bytes_written, /*force*/ true)) {
+      cached_info->last_save_number_of_methods = cached_profile->GetNumberOfMethods();
+      cached_info->last_save_number_of_classes = cached_profile->GetNumberOfResolvedClasses();
       // Clear resolved classes. No need to store them around as
       // they don't change after the first write.
-      cached_info->ClearResolvedClasses();
+      cached_profile->ClearResolvedClasses();
       if (bytes_written > 0) {
         total_number_of_writes_++;
         total_bytes_written_ += bytes_written;
@@ -326,8 +326,8 @@
       total_number_of_failed_writes_++;
     }
     total_number_of_profile_entries_cached +=
-        cached_info->GetNumberOfMethods() +
-        cached_info->GetNumberOfResolvedClasses();
+        cached_profile->GetNumberOfMethods() +
+        cached_profile->GetNumberOfResolvedClasses();
   }
   max_number_of_profile_entries_cached_ = std::max(
       max_number_of_profile_entries_cached_,
@@ -526,10 +526,8 @@
                                  uint16_t method_idx) {
   MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
   if (instance_ != nullptr) {
-    ProfileCompilationInfo* info = instance_->GetCachedProfiledInfo(profile);
-    if (info != nullptr) {
-      return info->ContainsMethod(MethodReference(dex_file, method_idx));
-    }
+    const ProfileCompilationInfo& info = instance_->GetCachedProfiledInfo(profile)->profile;
+    return info.ContainsMethod(MethodReference(dex_file, method_idx));
   }
   return false;
 }
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index ec8342a..8e0682d 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -59,6 +59,14 @@
                             uint16_t method_idx);
 
  private:
+  // A cache structure which keeps track of the data saved to disk.
+  // It is used to reduce the number of disk read/writes.
+  struct ProfileInfoCache {
+    ProfileCompilationInfo profile;
+    uint32_t last_save_number_of_methods = 0;
+    uint32_t last_save_number_of_classes = 0;
+  };
+
   ProfileSaver(const ProfileSaverOptions& options,
                const std::string& output_filename,
                jit::JitCodeCache* jit_code_cache,
@@ -90,7 +98,7 @@
   // Retrieves the cached profile compilation info for the given profile file.
   // If no entry exists, a new empty one will be created, added to the cache and
   // then returned.
-  ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename);
+  ProfileInfoCache* GetCachedProfiledInfo(const std::string& filename);
   // Fetches the current resolved classes and methods from the ClassLinker and stores them in the
   // profile_cache_ for later save.
   void FetchAndCacheResolvedClassesAndMethods();
@@ -110,8 +118,6 @@
       GUARDED_BY(Locks::profiler_lock_);
 
   bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
-  uint32_t last_save_number_of_methods_;
-  uint32_t last_save_number_of_classes_;
   uint64_t last_time_ns_saver_woke_up_ GUARDED_BY(wait_lock_);
   uint32_t jit_activity_notifications_;
 
@@ -119,7 +125,7 @@
   // profile information. The size of this cache is usually very small and tops
   // to just a few hundreds entries in the ProfileCompilationInfo objects.
   // It helps avoiding unnecessary writes to disk.
-  SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
+  SafeMap<std::string, ProfileInfoCache> profile_cache_;
 
   // Save period condition support.
   Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;