Merge "Save profile information in a separate thread."
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 07fa2fd..a854650 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -108,6 +108,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 b2fc74d..ab70f4c 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 7a2db31..0edce2f 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();
 
   void DumpForSigQuit(std::ostream& os) {
     DumpInfo(os);
@@ -98,7 +98,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 0ceb17a..1c842e4 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;
 
   size_t GetCurrentCapacity() REQUIRES(!lock_) {
     MutexLock lock(Thread::Current(), lock_);
@@ -249,7 +249,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 f3197c7..d241112 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.";
     }
   }
 
@@ -1642,11 +1637,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.
@@ -1792,18 +1789,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_