| 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) {
|
|
|