summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Calin Juravle <calin@google.com> 2015-12-01 18:38:09 +0000
committer Calin Juravle <calin@google.com> 2015-12-24 12:02:12 +0200
commit4d77b6a511659f26fdc711e23825ffa6e7feed7a (patch)
tree7ac013467a20fcdf64cb6cf4c79a8ff67dc7690a
parent66f55237679db90cb0a0a265043a787932b466f8 (diff)
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
-rw-r--r--runtime/Android.mk1
-rw-r--r--runtime/atomic.h5
-rw-r--r--runtime/jit/jit.cc44
-rw-r--r--runtime/jit/jit.h7
-rw-r--r--runtime/jit/jit_code_cache.cc11
-rw-r--r--runtime/jit/jit_code_cache.h7
-rw-r--r--runtime/jit/offline_profiling_info.cc36
-rw-r--r--runtime/jit/offline_profiling_info.h14
-rw-r--r--runtime/jit/profile_saver.cc204
-rw-r--r--runtime/jit/profile_saver.h82
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc1
-rw-r--r--runtime/runtime.cc31
-rw-r--r--runtime/runtime.h2
-rw-r--r--runtime/utils.cc6
-rw-r--r--runtime/utils.h3
15 files changed, 344 insertions, 110 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 40961179d3..ee8e69099f 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -107,6 +107,7 @@ LIBART_COMMON_SRC_FILES := \
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 87de506a85..0faa3c69c6 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -199,6 +199,11 @@ class PACKED(sizeof(T)) Atomic : public std::atomic<T> {
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 4ee1446e30..f241308101 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 @@ void Jit::AddTimingLogger(const TimingLogger& logger) {
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 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
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::DeleteThreadPool() {
}
}
-void Jit::SaveProfilingInfo(const std::string& filename) {
- if (offline_profile_info_ == nullptr) {
- return;
- }
- 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::StartProfileSaver(const std::string& filename,
+ const std::vector<std::string>& code_paths) {
+ if (save_profiling_info_) {
+ ProfileSaver::Start(filename, code_cache_.get(), code_paths);
+ }
+}
+
+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 @@ void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_thr
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 8a0778ceae..e600a97bb0 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -72,8 +72,8 @@ class Jit {
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 @@ class Jit {
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 033a8f05d8..08eac0ec20 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -317,7 +317,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
// 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::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S
}
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 fa43766f4d..905b277113 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -148,11 +148,11 @@ class JitCodeCache {
// 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 @@ class JitCodeCache {
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 511b53d5db..5dc0e45234 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 @@ void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
{
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 @@ void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
// 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 8c5ffbe635..32d4c5bedc 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 ArtMethod;
*/
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 @@ class OfflineProfilingInfo {
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 0000000000..0278138d6e
--- /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 0000000000..88efd41156
--- /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 424cc11da9..4b24f821cb 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -224,7 +224,6 @@ static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
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 32e83883a2..b18d415e84 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -190,7 +190,6 @@ Runtime::Runtime()
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 @@ Runtime::~Runtime() {
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 @@ Runtime::~Runtime() {
// 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 @@ bool Runtime::Start() {
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::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) {
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 @@ void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::strin
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 b45408eab7..c2ee5778ca 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -469,7 +469,6 @@ class Runtime {
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 @@ class Runtime {
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 eddc3a417a..ff6b4c0d20 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1860,4 +1860,10 @@ void ParseDouble(const std::string& option,
*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 5b9e963919..a07e74c62b 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -367,6 +367,9 @@ T GetRandomNumber(T min, T max) {
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_