Save profile information in a separate thread.

Previously we would save the profiling information only when the app
was sent to background. This missed on an important number of updates
on the jit code cache and it didn't work for background processes.

Bug: 26080105

Change-Id: I84075629870e69b3ed372f00f4806af1e9391e0f
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 4096117..ee8e690 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -107,6 +107,7 @@
   jit/jit_instrumentation.cc \
   jit/offline_profiling_info.cc \
   jit/profiling_info.cc \
+  jit/profile_saver.cc  \
   lambda/art_lambda_method.cc \
   lambda/box_table.cc \
   lambda/closure.cc \
diff --git a/runtime/atomic.h b/runtime/atomic.h
index 87de506..0faa3c6 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -199,6 +199,11 @@
     return this->load(std::memory_order_relaxed);
   }
 
+  // Load from memory with acquire ordering.
+  T LoadAcquire() const {
+    return this->load(std::memory_order_acquire);
+  }
+
   // Word tearing allowed, but may race.
   // TODO: Optimize?
   // There has been some discussion of eventually disallowing word
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 4ee1446..f241308 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -26,6 +26,7 @@
 #include "jit_instrumentation.h"
 #include "oat_file_manager.h"
 #include "offline_profiling_info.h"
+#include "profile_saver.h"
 #include "runtime.h"
 #include "runtime_options.h"
 #include "utils.h"
@@ -66,7 +67,7 @@
 Jit::Jit()
     : jit_library_handle_(nullptr), jit_compiler_handle_(nullptr), jit_load_(nullptr),
       jit_compile_method_(nullptr), dump_info_on_shutdown_(false),
-      cumulative_timings_("JIT timings") {
+      cumulative_timings_("JIT timings"), save_profiling_info_(false) {
 }
 
 Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
@@ -80,14 +81,12 @@
   if (jit->GetCodeCache() == nullptr) {
     return nullptr;
   }
-  jit->offline_profile_info_.reset(nullptr);
-  if (options->GetSaveProfilingInfo()) {
-    jit->offline_profile_info_.reset(new OfflineProfilingInfo());
-  }
+  jit->save_profiling_info_ = options->GetSaveProfilingInfo();
   LOG(INFO) << "JIT created with initial_capacity="
       << PrettySize(options->GetCodeCacheInitialCapacity())
       << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
-      << ", compile_threshold=" << options->GetCompileThreshold();
+      << ", compile_threshold=" << options->GetCompileThreshold()
+      << ", save_profiling_info=" << options->GetSaveProfilingInfo();
   return jit.release();
 }
 
@@ -173,25 +172,21 @@
   }
 }
 
-void Jit::SaveProfilingInfo(const std::string& filename) {
-  if (offline_profile_info_ == nullptr) {
-    return;
+void Jit::StartProfileSaver(const std::string& filename,
+                            const std::vector<std::string>& code_paths) {
+  if (save_profiling_info_) {
+    ProfileSaver::Start(filename, code_cache_.get(), code_paths);
   }
-  uint64_t last_update_ns = code_cache_->GetLastUpdateTimeNs();
-  if (offline_profile_info_->NeedsSaving(last_update_ns)) {
-    VLOG(profiler) << "Initiate save profiling information to: " << filename;
-    std::set<ArtMethod*> methods;
-    {
-      ScopedObjectAccess soa(Thread::Current());
-      code_cache_->GetCompiledArtMethods(offline_profile_info_->GetTrackedDexLocations(), methods);
-    }
-    offline_profile_info_->SaveProfilingInfo(filename, last_update_ns, methods);
-  } else {
-    VLOG(profiler) << "No need to save profiling information to: " << filename;
+}
+
+void Jit::StopProfileSaver() {
+  if (save_profiling_info_ && ProfileSaver::IsStarted()) {
+    ProfileSaver::Stop();
   }
 }
 
 Jit::~Jit() {
+  DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted());
   if (dump_info_on_shutdown_) {
     DumpInfo(LOG(INFO));
   }
@@ -210,12 +205,5 @@
       new jit::JitInstrumentationCache(compile_threshold, warmup_threshold));
 }
 
-void Jit::SetDexLocationsForProfiling(const std::vector<std::string>& dex_base_locations) {
-  if (offline_profile_info_ == nullptr) {
-    return;
-  }
-  offline_profile_info_->SetTrackedDexLocations(dex_base_locations);
-}
-
 }  // namespace jit
 }  // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 8a0778c..e600a97 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -72,8 +72,8 @@
     return instrumentation_cache_.get();
   }
 
-  void SetDexLocationsForProfiling(const std::vector<std::string>& dex_locations);
-  void SaveProfilingInfo(const std::string& filename);
+  void StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths);
+  void StopProfileSaver();
 
  private:
   Jit();
@@ -94,7 +94,8 @@
   std::unique_ptr<jit::JitCodeCache> code_cache_;
   CompilerCallbacks* compiler_callbacks_;  // Owned by the jit compiler.
 
-  std::unique_ptr<OfflineProfilingInfo> offline_profile_info_;
+  bool save_profiling_info_;
+
   DISALLOW_COPY_AND_ASSIGN(Jit);
 };
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 033a8f0..08eac0e 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -317,7 +317,7 @@
       // code.
       GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
     }
-    last_update_time_ns_ = NanoTime();
+    last_update_time_ns_.StoreRelease(NanoTime());
     VLOG(jit)
         << "JIT added "
         << PrettyMethod(method) << "@" << method
@@ -689,18 +689,17 @@
 }
 
 void JitCodeCache::GetCompiledArtMethods(const std::set<const std::string>& dex_base_locations,
-                                         std::set<ArtMethod*>& methods) {
+                                         std::vector<ArtMethod*>& methods) {
   MutexLock mu(Thread::Current(), lock_);
   for (auto it : method_code_map_) {
     if (ContainsElement(dex_base_locations, it.second->GetDexFile()->GetBaseLocation())) {
-      methods.insert(it.second);
+      methods.push_back(it.second);
     }
   }
 }
 
-uint64_t JitCodeCache::GetLastUpdateTimeNs() {
-  MutexLock mu(Thread::Current(), lock_);
-  return last_update_time_ns_;
+uint64_t JitCodeCache::GetLastUpdateTimeNs() const {
+  return last_update_time_ns_.LoadAcquire();
 }
 
 bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self) {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index fa43766..905b277 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -148,11 +148,11 @@
 
   // Adds to `methods` all the compiled ArtMethods which are part of any of the given dex locations.
   void GetCompiledArtMethods(const std::set<const std::string>& dex_base_locations,
-                             std::set<ArtMethod*>& methods)
+                             std::vector<ArtMethod*>& methods)
       REQUIRES(!lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  uint64_t GetLastUpdateTimeNs() REQUIRES(!lock_);
+  uint64_t GetLastUpdateTimeNs() const;
 
  private:
   // Take ownership of maps.
@@ -244,7 +244,8 @@
   bool has_done_one_collection_ GUARDED_BY(lock_);
 
   // Last time the the code_cache was updated.
-  uint64_t last_update_time_ns_ GUARDED_BY(lock_);
+  // It is atomic to avoid locking when reading it.
+  Atomic<uint64_t> last_update_time_ns_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
 };
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 511b53d..5dc0e45 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -17,7 +17,7 @@
 #include "offline_profiling_info.h"
 
 #include <fstream>
-#include <set>
+#include <vector>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/uio.h>
@@ -30,34 +30,8 @@
 
 namespace art {
 
-// An arbitrary value to throttle save requests. Set to 500ms for now.
-static constexpr const uint64_t kMilisecondsToNano = 1000000;
-static constexpr const uint64_t kMinimumTimeBetweenSavesNs = 500 * kMilisecondsToNano;
-
-void OfflineProfilingInfo::SetTrackedDexLocations(
-      const std::vector<std::string>& dex_base_locations) {
-  tracked_dex_base_locations_.clear();
-  tracked_dex_base_locations_.insert(dex_base_locations.begin(), dex_base_locations.end());
-  VLOG(profiler) << "Tracking dex locations: " << Join(dex_base_locations, ':');
-}
-
-const std::set<const std::string>& OfflineProfilingInfo::GetTrackedDexLocations() const {
-  return tracked_dex_base_locations_;
-}
-
-bool OfflineProfilingInfo::NeedsSaving(uint64_t last_update_time_ns) const {
-  return !tracked_dex_base_locations_.empty() &&
-      (last_update_time_ns - last_update_time_ns_.LoadRelaxed() > kMinimumTimeBetweenSavesNs);
-}
-
 void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
-                                             uint64_t last_update_time_ns,
-                                             const std::set<ArtMethod*>& methods) {
-  if (!NeedsSaving(last_update_time_ns)) {
-    VLOG(profiler) << "No need to saved profile info to " << filename;
-    return;
-  }
-
+                                             const std::vector<ArtMethod*>& methods) {
   if (methods.empty()) {
     VLOG(profiler) << "No info to save to " << filename;
     return;
@@ -67,7 +41,6 @@
   {
     ScopedObjectAccess soa(Thread::Current());
     for (auto it = methods.begin(); it != methods.end(); it++) {
-      DCHECK(ContainsElement(tracked_dex_base_locations_, (*it)->GetDexFile()->GetBaseLocation()));
       AddMethodInfo(*it, &info);
     }
   }
@@ -75,9 +48,8 @@
   // 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)) {
-    last_update_time_ns_.StoreRelaxed(last_update_time_ns);
-    VLOG(profiler) << "Successfully saved profile info to "
-                   << filename << " with time stamp: " << last_update_time_ns;
+    VLOG(profiler) << "Successfully saved profile info to " << filename
+        << " Size: " << GetFileSizeBytes(filename);
   }
 }
 
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index 8c5ffbe..32d4c5b 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_
 
 #include <set>
+#include <vector>
 
 #include "atomic.h"
 #include "dex_file.h"
@@ -36,12 +37,7 @@
  */
 class OfflineProfilingInfo {
  public:
-  bool NeedsSaving(uint64_t last_update_time_ns) const;
-  void SaveProfilingInfo(const std::string& filename,
-                         uint64_t last_update_time_ns,
-                         const std::set<ArtMethod*>& methods);
-  void SetTrackedDexLocations(const std::vector<std::string>& dex_locations);
-  const std::set<const std::string>& GetTrackedDexLocations() const;
+  void SaveProfilingInfo(const std::string& filename, const std::vector<ArtMethod*>& methods);
 
  private:
   // Map identifying the location of the profiled methods.
@@ -51,12 +47,6 @@
   void AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info)
       SHARED_REQUIRES(Locks::mutator_lock_);
   bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const;
-
-  // TODO(calin): Verify if Atomic is really needed (are we sure to be called from a
-  // single thread?)
-  Atomic<uint64_t> last_update_time_ns_;
-
-  std::set<const std::string> tracked_dex_base_locations_;
 };
 
 /**
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
new file mode 100644
index 0000000..0278138
--- /dev/null
+++ b/runtime/jit/profile_saver.cc
@@ -0,0 +1,204 @@
+/*
+ * 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_saver.h"
+
+#include "art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "oat_file_manager.h"
+
+namespace art {
+
+// An arbitrary value to throttle save requests. Set to 500ms for now.
+static constexpr const uint64_t kMilisecondsToNano = 1000000;
+static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 500 * kMilisecondsToNano;
+
+// TODO: read the constants from ProfileOptions,
+// Add a random delay each time we go to sleep so that we don't hammer the CPU
+// with all profile savers running at the same time.
+static constexpr const uint64_t kRandomDelayMaxMs = 10 * 1000;  // 10 seconds
+static constexpr const uint64_t kMaxBackoffMs = 4 * 60 * 1000;  // 4 minutes
+static constexpr const uint64_t kSavePeriodMs = 4 * 1000;  // 4 seconds
+static constexpr const double kBackoffCoef = 1.5;
+
+static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10;
+
+ProfileSaver* ProfileSaver::instance_ = nullptr;
+pthread_t ProfileSaver::profiler_pthread_ = 0U;
+
+ProfileSaver::ProfileSaver(const std::string& output_filename,
+                           jit::JitCodeCache* jit_code_cache,
+                           const std::vector<std::string>& code_paths)
+    : output_filename_(output_filename),
+      jit_code_cache_(jit_code_cache),
+      tracked_dex_base_locations_(code_paths.begin(), code_paths.end()),
+      code_cache_last_update_time_ns_(0),
+      shutting_down_(false),
+      wait_lock_("ProfileSaver wait lock"),
+      period_condition_("ProfileSaver period condition", wait_lock_) {
+}
+
+void ProfileSaver::Run() {
+  srand(MicroTime() * getpid());
+  Thread* self = Thread::Current();
+
+  uint64_t save_period_ms = kSavePeriodMs;
+  VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
+  while (true) {
+    if (ShuttingDown(self)) {
+      break;
+    }
+
+    uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
+    uint64_t sleep_time_ms = save_period_ms + random_sleep_delay_ms;
+    {
+      MutexLock mu(self, wait_lock_);
+      period_condition_.TimedWait(self, sleep_time_ms, 0);
+    }
+
+    if (ShuttingDown(self)) {
+      break;
+    }
+
+    if (!ProcessProfilingInfo() && save_period_ms < kMaxBackoffMs) {
+      // If we don't need to save now it is less likely that we will need to do
+      // so in the future. Increase the time between saves according to the
+      // kBackoffCoef, but make it no larger than kMaxBackoffMs.
+      save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+    } else {
+      // Reset the period to the initial value as it's highly likely to JIT again.
+      save_period_ms = kSavePeriodMs;
+    }
+  }
+}
+
+bool ProfileSaver::ProcessProfilingInfo() {
+  VLOG(profiler) << "Initiating save profiling information to: " << output_filename_;
+
+  uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
+  if (last_update_time_ns - code_cache_last_update_time_ns_
+      > kMinimumTimeBetweenCodeCacheUpdatesNs) {
+    VLOG(profiler) << "Not enough time has passed since the last code cache update.";
+    return false;
+  }
+
+  uint64_t start = NanoTime();
+  code_cache_last_update_time_ns_ = last_update_time_ns;
+  std::vector<ArtMethod*> methods;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    jit_code_cache_->GetCompiledArtMethods(tracked_dex_base_locations_, methods);
+  }
+  if (methods.size() < kMinimumNrOrMethodsToSave) {
+    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);
+
+  return true;
+}
+
+void* ProfileSaver::RunProfileSaverThread(void* arg) {
+  Runtime* runtime = Runtime::Current();
+  ProfileSaver* profile_saver = reinterpret_cast<ProfileSaver*>(arg);
+
+  CHECK(runtime->AttachCurrentThread("Profile Saver",
+                                     /*as_daemon*/true,
+                                     runtime->GetSystemThreadGroup(),
+                                     /*create_peer*/true));
+  profile_saver->Run();
+
+  runtime->DetachCurrentThread();
+  VLOG(profiler) << "Profile saver shutdown";
+  return nullptr;
+}
+
+void ProfileSaver::Start(const std::string& output_filename,
+                         jit::JitCodeCache* jit_code_cache,
+                         const std::vector<std::string>& code_paths) {
+  DCHECK(Runtime::Current()->UseJit());
+  DCHECK(!output_filename.empty());
+  DCHECK(jit_code_cache != nullptr);
+
+  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+  // Don't start two profile saver threads.
+  if (instance_ != nullptr) {
+    DCHECK(false) << "Tried to start two profile savers";
+    return;
+  }
+
+  VLOG(profiler) << "Starting profile saver using output file: " << output_filename
+      << ". Tracking: " << Join(code_paths, ':');
+
+  instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths);
+
+  // Create a new thread which does the saving.
+  CHECK_PTHREAD_CALL(
+      pthread_create,
+      (&profiler_pthread_, nullptr, &RunProfileSaverThread, reinterpret_cast<void*>(instance_)),
+      "Profile saver thread");
+}
+
+void ProfileSaver::Stop() {
+  ProfileSaver* profile_saver = nullptr;
+  pthread_t profiler_pthread = 0U;
+
+  {
+    MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
+    VLOG(profiler) << "Stopping profile saver thread for file: " << instance_->output_filename_;
+    profile_saver = instance_;
+    profiler_pthread = profiler_pthread_;
+    if (instance_ == nullptr) {
+      DCHECK(false) << "Tried to stop a profile saver which was not started";
+      return;
+    }
+    if (instance_->shutting_down_) {
+      DCHECK(false) << "Tried to stop the profile saver twice";
+      return;
+    }
+    instance_->shutting_down_ = true;
+  }
+
+  {
+    // Wake up the saver thread if it is sleeping to allow for a clean exit.
+    MutexLock wait_mutex(Thread::Current(), profile_saver->wait_lock_);
+    profile_saver->period_condition_.Signal(Thread::Current());
+  }
+
+  // Wait for the saver thread to stop.
+  CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profile saver thread shutdown");
+
+  {
+    MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
+    instance_ = nullptr;
+    profiler_pthread_ = 0U;
+  }
+  delete profile_saver;
+}
+
+bool ProfileSaver::ShuttingDown(Thread* self) {
+  MutexLock mu(self, *Locks::profiler_lock_);
+  return shutting_down_;
+}
+
+bool ProfileSaver::IsStarted() {
+  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+  return instance_ != nullptr;
+}
+
+}   // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
new file mode 100644
index 0000000..88efd41
--- /dev/null
+++ b/runtime/jit/profile_saver.h
@@ -0,0 +1,82 @@
+/*
+ * 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_RUNTIME_JIT_PROFILE_SAVER_H_
+#define ART_RUNTIME_JIT_PROFILE_SAVER_H_
+
+#include "base/mutex.h"
+#include "jit_code_cache.h"
+#include "offline_profiling_info.h"
+
+namespace art {
+
+class ProfileSaver {
+ public:
+  // Starts the profile saver thread.
+  static void Start(const std::string& output_filename,
+                    jit::JitCodeCache* jit_code_cache,
+                    const std::vector<std::string>& code_paths)
+      REQUIRES(!Locks::profiler_lock_, !wait_lock_);
+
+  // Stops the profile saver thread.
+  // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
+  static void Stop()
+      REQUIRES(!Locks::profiler_lock_, !wait_lock_)
+      NO_THREAD_SAFETY_ANALYSIS;
+
+  // Returns true if the profile saver is started.
+  static bool IsStarted() REQUIRES(!Locks::profiler_lock_);
+
+ private:
+  ProfileSaver(const std::string& output_filename,
+               jit::JitCodeCache* jit_code_cache,
+               const std::vector<std::string>& code_paths);
+
+  // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
+  static void* RunProfileSaverThread(void* arg)
+      REQUIRES(!Locks::profiler_lock_, !wait_lock_)
+      NO_THREAD_SAFETY_ANALYSIS;
+
+  // The run loop for the saver.
+  void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_);
+  // Processes the existing profiling info from the jit code cache and returns
+  // true if it needed to be saved to disk.
+  bool ProcessProfilingInfo();
+  // Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
+  bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
+
+  // The only instance of the saver.
+  static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
+  // Profile saver thread.
+  static pthread_t profiler_pthread_ GUARDED_BY(Locks::profiler_lock_);
+
+  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_);
+
+  // Save period condition support.
+  Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
+
+  DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_JIT_PROFILE_SAVER_H_
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 424cc11..4b24f82 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -224,7 +224,6 @@
 static void VMRuntime_updateProcessState(JNIEnv*, jobject, jint process_state) {
   Runtime* runtime = Runtime::Current();
   runtime->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
-  runtime->UpdateProfilerState(process_state);
 }
 
 static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 32e8388..b18d415 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -190,7 +190,6 @@
       abort_(nullptr),
       stats_enabled_(false),
       is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL),
-      profiler_started_(false),
       instrumentation_(),
       main_thread_group_(nullptr),
       system_thread_group_(nullptr),
@@ -258,11 +257,6 @@
     self = nullptr;
   }
 
-  // Shut down background profiler before the runtime exits.
-  if (profiler_started_) {
-    BackgroundMethodSamplingProfiler::Shutdown();
-  }
-
   // Make sure to let the GC complete if it is running.
   heap_->WaitForGcToComplete(gc::kGcCauseBackground, self);
   heap_->DeleteThreadPool();
@@ -271,6 +265,8 @@
     // Delete thread pool before the thread list since we don't want to wait forever on the
     // JIT compiler threads.
     jit_->DeleteThreadPool();
+    // Similarly, stop the profile saver thread before deleting the thread list.
+    jit_->StopProfileSaver();
   }
 
   // Make sure our internal threads are dead before we start tearing down things they're using.
@@ -616,8 +612,7 @@
     if (fd >= 0) {
       close(fd);
     } else if (errno != EEXIST) {
-      LOG(INFO) << "Failed to access the profile file. Profiler disabled.";
-      return true;
+      LOG(WARNING) << "Failed to access the profile file. Profiler disabled.";
     }
   }
 
@@ -1635,11 +1630,13 @@
 
 void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
                               const std::string& profile_output_filename) {
+  VLOG(profiler) << "Register app with " << profile_output_filename_
+      << " " << Join(code_paths, ':');
   DCHECK(!profile_output_filename.empty());
-  if (jit_.get() != nullptr) {
-    jit_->SetDexLocationsForProfiling(code_paths);
-  }
   profile_output_filename_ = profile_output_filename;
+  if (jit_.get() != nullptr && !profile_output_filename.empty() && !code_paths.empty()) {
+    jit_->StartProfileSaver(profile_output_filename, code_paths);
+  }
 }
 
 // Transaction support.
@@ -1785,18 +1782,6 @@
   argv->push_back(feature_string);
 }
 
-void Runtime::MaybeSaveJitProfilingInfo() {
-  if (jit_.get() != nullptr && !profile_output_filename_.empty()) {
-    jit_->SaveProfilingInfo(profile_output_filename_);
-  }
-}
-
-void Runtime::UpdateProfilerState(int state) {
-  if (state == kProfileBackground) {
-    MaybeSaveJitProfilingInfo();
-  }
-}
-
 void Runtime::CreateJit() {
   CHECK(!IsAotCompiler());
   if (GetInstrumentation()->IsForcedInterpretOnly()) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index b45408e..c2ee577 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -469,7 +469,6 @@
 
   void RegisterAppInfo(const std::vector<std::string>& code_paths,
                        const std::string& profile_output_filename);
-  void UpdateProfilerState(int state);
 
   // Transaction support.
   bool IsActiveTransaction() const {
@@ -734,7 +733,6 @@
 
   std::string profile_output_filename_;
   ProfilerOptions profiler_options_;
-  bool profiler_started_;
 
   std::unique_ptr<TraceConfig> trace_config_;
 
diff --git a/runtime/utils.cc b/runtime/utils.cc
index eddc3a4..ff6b4c0 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1860,4 +1860,10 @@
   *parsed_value = value;
 }
 
+int64_t GetFileSizeBytes(const std::string& filename) {
+  struct stat stat_buf;
+  int rc = stat(filename.c_str(), &stat_buf);
+  return rc == 0 ? stat_buf.st_size : -1;
+}
+
 }  // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index 5b9e963..a07e74c 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -367,6 +367,9 @@
   return dist(rng);
 }
 
+// Return the file size in bytes or -1 if the file does not exists.
+int64_t GetFileSizeBytes(const std::string& filename);
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_