Index: base/profiler/stack_sampling_profiler.cc |
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc |
index 57b7b355a080923b2acc4b2201d4e58d7bb666ab..9da662859fd9535cb4037d1d1302401b2566f5b3 100644 |
--- a/base/profiler/stack_sampling_profiler.cc |
+++ b/base/profiler/stack_sampling_profiler.cc |
@@ -9,114 +9,144 @@ |
#include "base/bind.h" |
#include "base/callback.h" |
#include "base/memory/singleton.h" |
+#include "base/profiler/native_stack_sampler.h" |
#include "base/synchronization/lock.h" |
-#include "base/synchronization/waitable_event.h" |
#include "base/timer/elapsed_timer.h" |
-template <typename T> struct DefaultSingletonTraits; |
- |
namespace base { |
+// DefaultProfileProcessor ---------------------------------------------------- |
+ |
namespace { |
-// Thread-safe singleton class that stores collected profiles waiting to be |
-// processed. |
-class PendingProfiles { |
+// Singleton class responsible for providing the default processing for profiles |
+// (i.e. for profiles generated by profilers without their own completed |
+// callback). |
+class DefaultProfileProcessor { |
public: |
- PendingProfiles(); |
- ~PendingProfiles(); |
+ using CompletedCallback = StackSamplingProfiler::CompletedCallback; |
+ |
+ ~DefaultProfileProcessor(); |
+ |
+ static DefaultProfileProcessor* GetInstance(); |
- static PendingProfiles* GetInstance(); |
+ // Sets the callback to use for processing profiles captured without a |
+ // per-profiler completed callback. Pending completed profiles are stored in |
+ // this object until a non-null callback is provided here. This function is |
+ // thread-safe. |
+ void SetCompletedCallback(CompletedCallback callback); |
- // Appends |profiles|. This function is thread safe. |
- void PutProfiles(const std::vector<StackSamplingProfiler::Profile>& profiles); |
- // Gets the pending profiles into *|profiles|. This function is thread safe. |
- void GetProfiles(std::vector<StackSamplingProfiler::Profile>* profiles); |
+ // Processes |profiles|. This function is thread safe. |
+ void ProcessProfiles( |
+ const StackSamplingProfiler::CallStackProfiles& profiles); |
private: |
+ friend struct DefaultSingletonTraits<DefaultProfileProcessor>; |
+ |
+ DefaultProfileProcessor(); |
+ |
+ // Copies the pending profiles from |profiles_| into |profiles|, and clears |
+ // |profiles_|. This function may be called on any thread. |
+ void GetAndClearPendingProfiles( |
+ StackSamplingProfiler::CallStackProfiles* profiles); |
+ |
+ // Gets the current completed callback, with proper locking. |
+ CompletedCallback GetCompletedCallback() const; |
+ |
+ mutable Lock callback_lock_; |
+ CompletedCallback default_completed_callback_; |
+ |
Lock profiles_lock_; |
- std::vector<StackSamplingProfiler::Profile> profiles_; |
+ StackSamplingProfiler::CallStackProfiles profiles_; |
- DISALLOW_COPY_AND_ASSIGN(PendingProfiles); |
+ DISALLOW_COPY_AND_ASSIGN(DefaultProfileProcessor); |
}; |
-PendingProfiles::PendingProfiles() {} |
- |
-PendingProfiles::~PendingProfiles() {} |
+DefaultProfileProcessor::~DefaultProfileProcessor() {} |
// static |
-PendingProfiles* PendingProfiles::GetInstance() { |
- return Singleton<PendingProfiles>::get(); |
+DefaultProfileProcessor* DefaultProfileProcessor::GetInstance() { |
+ return Singleton<DefaultProfileProcessor>::get(); |
} |
-void PendingProfiles::PutProfiles( |
- const std::vector<StackSamplingProfiler::Profile>& profiles) { |
- AutoLock scoped_lock(profiles_lock_); |
- profiles_.insert(profiles_.end(), profiles.begin(), profiles.end()); |
+void DefaultProfileProcessor::SetCompletedCallback(CompletedCallback callback) { |
+ { |
+ AutoLock scoped_lock(callback_lock_); |
+ default_completed_callback_ = callback; |
+ } |
+ |
+ if (!callback.is_null()) { |
+ // Provide any pending profiles to the callback immediately. |
+ StackSamplingProfiler::CallStackProfiles profiles; |
+ GetAndClearPendingProfiles(&profiles); |
+ if (!profiles.empty()) |
+ callback.Run(profiles); |
+ } |
} |
-void PendingProfiles::GetProfiles( |
- std::vector<StackSamplingProfiler::Profile>* profiles) { |
- profiles->clear(); |
+void DefaultProfileProcessor::ProcessProfiles( |
+ const StackSamplingProfiler::CallStackProfiles& profiles) { |
+ CompletedCallback callback = GetCompletedCallback(); |
- AutoLock scoped_lock(profiles_lock_); |
- profiles_.swap(*profiles); |
+ // Store pending profiles if we don't have a valid callback. |
+ if (!callback.is_null()) { |
+ callback.Run(profiles); |
+ } else { |
+ AutoLock scoped_lock(profiles_lock_); |
+ profiles_.insert(profiles_.end(), profiles.begin(), profiles.end()); |
+ } |
} |
-} // namespace |
-StackSamplingProfiler::Module::Module() : base_address(nullptr) {} |
+DefaultProfileProcessor::DefaultProfileProcessor() {} |
-StackSamplingProfiler::Module::~Module() {} |
+void DefaultProfileProcessor::GetAndClearPendingProfiles( |
+ StackSamplingProfiler::CallStackProfiles* profiles) { |
+ profiles->clear(); |
-StackSamplingProfiler::Frame::Frame() |
- : instruction_pointer(nullptr), |
- module_index(-1) {} |
+ AutoLock scoped_lock(profiles_lock_); |
+ profiles_.swap(*profiles); |
+} |
-StackSamplingProfiler::Frame::~Frame() {} |
+DefaultProfileProcessor::CompletedCallback |
+DefaultProfileProcessor::GetCompletedCallback() const { |
+ AutoLock scoped_lock(callback_lock_); |
+ return default_completed_callback_; |
+} |
-StackSamplingProfiler::Profile::Profile() : preserve_sample_ordering(false) {} |
+} // namespace |
-StackSamplingProfiler::Profile::~Profile() {} |
+// StackSamplingProfiler::Module ---------------------------------------------- |
-class StackSamplingProfiler::SamplingThread : public PlatformThread::Delegate { |
- public: |
- // Samples stacks using |native_sampler|. When complete, invokes |
- // |profiles_callback| with the collected profiles. |profiles_callback| must |
- // be thread-safe and may consume the contents of the vector. |
- SamplingThread( |
- scoped_ptr<NativeStackSampler> native_sampler, |
- const SamplingParams& params, |
- Callback<void(const std::vector<Profile>&)> completed_callback); |
- ~SamplingThread() override; |
+StackSamplingProfiler::Module::Module() : base_address(nullptr) {} |
+StackSamplingProfiler::Module::Module(const void* base_address, |
+ const std::string& id, |
+ const FilePath& filename) |
+ : base_address(base_address), id(id), filename(filename) {} |
- // Implementation of PlatformThread::Delegate: |
- void ThreadMain() override; |
+StackSamplingProfiler::Module::~Module() {} |
- void Stop(); |
+// StackSamplingProfiler::Frame ----------------------------------------------- |
- private: |
- // Collects a profile from a single burst. Returns true if the profile was |
- // collected, or false if collection was stopped before it completed. |
- bool CollectProfile(Profile* profile, TimeDelta* elapsed_time); |
- // Collects profiles from all bursts, or until the sampling is stopped. If |
- // stopped before complete, |profiles| will contains only full bursts. |
- void CollectProfiles(std::vector<Profile>* profiles); |
+StackSamplingProfiler::Frame::Frame(const void* instruction_pointer, |
+ size_t module_index) |
+ : instruction_pointer(instruction_pointer), |
+ module_index(module_index) {} |
- scoped_ptr<NativeStackSampler> native_sampler_; |
+StackSamplingProfiler::Frame::~Frame() {} |
- const SamplingParams params_; |
+// StackSamplingProfiler::CallStackProfile ------------------------------------ |
- WaitableEvent stop_event_; |
+StackSamplingProfiler::CallStackProfile::CallStackProfile() |
+ : preserve_sample_ordering(false), user_data(0) {} |
- Callback<void(const std::vector<Profile>&)> completed_callback_; |
+StackSamplingProfiler::CallStackProfile::~CallStackProfile() {} |
- DISALLOW_COPY_AND_ASSIGN(SamplingThread); |
-}; |
+// StackSamplingProfiler::SamplingThread -------------------------------------- |
StackSamplingProfiler::SamplingThread::SamplingThread( |
scoped_ptr<NativeStackSampler> native_sampler, |
const SamplingParams& params, |
- Callback<void(const std::vector<Profile>&)> completed_callback) |
+ CompletedCallback completed_callback) |
: native_sampler_(native_sampler.Pass()), |
params_(params), |
stop_event_(false, false), |
@@ -128,61 +158,79 @@ StackSamplingProfiler::SamplingThread::~SamplingThread() {} |
void StackSamplingProfiler::SamplingThread::ThreadMain() { |
PlatformThread::SetName("Chrome_SamplingProfilerThread"); |
- std::vector<Profile> profiles; |
+ CallStackProfiles profiles; |
CollectProfiles(&profiles); |
completed_callback_.Run(profiles); |
} |
+// Depending on how long the sampling takes and the length of the sampling |
+// interval, a burst of samples could take arbitrarily longer than |
+// samples_per_burst * sampling_interval. In this case, we (somewhat |
+// arbitrarily) honor the number of samples requested rather than strictly |
+// adhering to the sampling intervals. Once we have established users for the |
+// StackSamplingProfiler and the collected data to judge, we may go the other |
+// way or make this behavior configurable. |
bool StackSamplingProfiler::SamplingThread::CollectProfile( |
- Profile* profile, |
+ CallStackProfile* profile, |
TimeDelta* elapsed_time) { |
ElapsedTimer profile_timer; |
- Profile current_profile; |
- native_sampler_->ProfileRecordingStarting(¤t_profile); |
+ CallStackProfile current_profile; |
+ native_sampler_->ProfileRecordingStarting(¤t_profile.modules); |
current_profile.sampling_period = params_.sampling_interval; |
- bool stopped_early = false; |
+ bool burst_completed = true; |
+ TimeDelta previous_elapsed_sample_time; |
for (int i = 0; i < params_.samples_per_burst; ++i) { |
- ElapsedTimer sample_timer; |
- current_profile.samples.push_back(Sample()); |
- native_sampler_->RecordStackSample(¤t_profile.samples.back()); |
- TimeDelta elapsed_sample_time = sample_timer.Elapsed(); |
- if (i != params_.samples_per_burst - 1) { |
+ if (i != 0) { |
+ // Always wait, even if for 0 seconds, so we can observe a signal on |
+ // stop_event_. |
if (stop_event_.TimedWait( |
- std::max(params_.sampling_interval - elapsed_sample_time, |
+ std::max(params_.sampling_interval - previous_elapsed_sample_time, |
TimeDelta()))) { |
- stopped_early = true; |
+ burst_completed = false; |
break; |
} |
} |
+ ElapsedTimer sample_timer; |
+ current_profile.samples.push_back(Sample()); |
+ native_sampler_->RecordStackSample(¤t_profile.samples.back()); |
+ previous_elapsed_sample_time = sample_timer.Elapsed(); |
} |
*elapsed_time = profile_timer.Elapsed(); |
current_profile.profile_duration = *elapsed_time; |
+ current_profile.preserve_sample_ordering = params_.preserve_sample_ordering; |
+ current_profile.user_data = params_.user_data; |
native_sampler_->ProfileRecordingStopped(); |
- if (!stopped_early) |
+ if (burst_completed) |
*profile = current_profile; |
- return !stopped_early; |
+ return burst_completed; |
} |
+// In an analogous manner to CollectProfile() and samples exceeding the expected |
+// total sampling time, bursts may also exceed the burst_interval. We adopt the |
+// same wait-and-see approach here. |
void StackSamplingProfiler::SamplingThread::CollectProfiles( |
- std::vector<Profile>* profiles) { |
+ CallStackProfiles* profiles) { |
if (stop_event_.TimedWait(params_.initial_delay)) |
return; |
+ TimeDelta previous_elapsed_profile_time; |
for (int i = 0; i < params_.bursts; ++i) { |
- Profile profile; |
- TimeDelta elapsed_profile_time; |
- if (CollectProfile(&profile, &elapsed_profile_time)) |
- profiles->push_back(profile); |
- else |
- return; |
+ if (i != 0) { |
+ // Always wait, even if for 0 seconds, so we can observe a signal on |
+ // stop_event_. |
+ if (stop_event_.TimedWait( |
+ std::max(params_.burst_interval - previous_elapsed_profile_time, |
+ TimeDelta()))) |
+ return; |
+ } |
- if (stop_event_.TimedWait( |
- std::max(params_.burst_interval - elapsed_profile_time, |
- TimeDelta()))) |
+ CallStackProfile profile; |
+ if (!CollectProfile(&profile, &previous_elapsed_profile_time)) |
return; |
+ profiles->push_back(profile); |
} |
} |
@@ -190,14 +238,7 @@ void StackSamplingProfiler::SamplingThread::Stop() { |
stop_event_.Signal(); |
} |
-void StackSamplingProfiler::SamplingThreadDeleter::operator()( |
- SamplingThread* thread) const { |
- delete thread; |
-} |
- |
-StackSamplingProfiler::NativeStackSampler::NativeStackSampler() {} |
- |
-StackSamplingProfiler::NativeStackSampler::~NativeStackSampler() {} |
+// StackSamplingProfiler ------------------------------------------------------ |
StackSamplingProfiler::SamplingParams::SamplingParams() |
: initial_delay(TimeDelta::FromMilliseconds(0)), |
@@ -205,29 +246,40 @@ StackSamplingProfiler::SamplingParams::SamplingParams() |
burst_interval(TimeDelta::FromMilliseconds(10000)), |
samples_per_burst(300), |
sampling_interval(TimeDelta::FromMilliseconds(100)), |
- preserve_sample_ordering(false) { |
+ preserve_sample_ordering(false), |
+ user_data(0) { |
} |
StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id, |
const SamplingParams& params) |
: thread_id_(thread_id), params_(params) {} |
-StackSamplingProfiler::~StackSamplingProfiler() {} |
+StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id, |
+ const SamplingParams& params, |
+ CompletedCallback callback) |
+ : thread_id_(thread_id), params_(params), completed_callback_(callback) {} |
+ |
+StackSamplingProfiler::~StackSamplingProfiler() { |
+ Stop(); |
+ if (!sampling_thread_handle_.is_null()) |
+ PlatformThread::Join(sampling_thread_handle_); |
+} |
void StackSamplingProfiler::Start() { |
- native_sampler_ = NativeStackSampler::Create(thread_id_); |
- if (!native_sampler_) |
+ scoped_ptr<NativeStackSampler> native_sampler = |
+ NativeStackSampler::Create(thread_id_); |
+ if (!native_sampler) |
return; |
+ CompletedCallback callback = |
+ !completed_callback_.is_null() ? completed_callback_ : |
+ Bind(&DefaultProfileProcessor::ProcessProfiles, |
+ Unretained(DefaultProfileProcessor::GetInstance())); |
sampling_thread_.reset( |
- new SamplingThread( |
- native_sampler_.Pass(), params_, |
- (custom_completed_callback_.is_null() ? |
- Bind(&PendingProfiles::PutProfiles, |
- Unretained(PendingProfiles::GetInstance())) : |
- custom_completed_callback_))); |
- if (!PlatformThread::CreateNonJoinable(0, sampling_thread_.get())) |
- LOG(ERROR) << "failed to create thread"; |
+ new SamplingThread(native_sampler.Pass(), params_, callback)); |
+ if (!PlatformThread::Create(0, sampling_thread_.get(), |
+ &sampling_thread_handle_)) |
+ sampling_thread_.reset(); |
} |
void StackSamplingProfiler::Stop() { |
@@ -236,14 +288,12 @@ void StackSamplingProfiler::Stop() { |
} |
// static |
-void StackSamplingProfiler::GetPendingProfiles(std::vector<Profile>* profiles) { |
- PendingProfiles::GetInstance()->GetProfiles(profiles); |
+void StackSamplingProfiler::SetDefaultCompletedCallback( |
+ CompletedCallback callback) { |
+ DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback); |
} |
-void StackSamplingProfiler::SetCustomCompletedCallback( |
- Callback<void(const std::vector<Profile>&)> callback) { |
- custom_completed_callback_ = callback; |
-} |
+// StackSamplingProfiler::Frame global functions ------------------------------ |
bool operator==(const StackSamplingProfiler::Frame &a, |
const StackSamplingProfiler::Frame &b) { |