| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/profiler/stack_sampling_profiler.h" |
| |
| #include <algorithm> |
| |
| #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/timer/elapsed_timer.h" |
| |
| namespace base { |
| |
| // DefaultProfileProcessor ---------------------------------------------------- |
| |
| namespace { |
| |
| // 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: |
| using CompletedCallback = StackSamplingProfiler::CompletedCallback; |
| |
| ~DefaultProfileProcessor(); |
| |
| static DefaultProfileProcessor* 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); |
| |
| // 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_; |
| StackSamplingProfiler::CallStackProfiles profiles_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DefaultProfileProcessor); |
| }; |
| |
| DefaultProfileProcessor::~DefaultProfileProcessor() {} |
| |
| // static |
| DefaultProfileProcessor* DefaultProfileProcessor::GetInstance() { |
| return Singleton<DefaultProfileProcessor>::get(); |
| } |
| |
| 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 DefaultProfileProcessor::ProcessProfiles( |
| const StackSamplingProfiler::CallStackProfiles& profiles) { |
| CompletedCallback callback = GetCompletedCallback(); |
| |
| // 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()); |
| } |
| } |
| |
| DefaultProfileProcessor::DefaultProfileProcessor() {} |
| |
| void DefaultProfileProcessor::GetAndClearPendingProfiles( |
| StackSamplingProfiler::CallStackProfiles* profiles) { |
| profiles->clear(); |
| |
| AutoLock scoped_lock(profiles_lock_); |
| profiles_.swap(*profiles); |
| } |
| |
| DefaultProfileProcessor::CompletedCallback |
| DefaultProfileProcessor::GetCompletedCallback() const { |
| AutoLock scoped_lock(callback_lock_); |
| return default_completed_callback_; |
| } |
| |
| } // namespace |
| |
| // StackSamplingProfiler::Module ---------------------------------------------- |
| |
| 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) {} |
| |
| StackSamplingProfiler::Module::~Module() {} |
| |
| // StackSamplingProfiler::Frame ----------------------------------------------- |
| |
| StackSamplingProfiler::Frame::Frame(const void* instruction_pointer, |
| size_t module_index) |
| : instruction_pointer(instruction_pointer), |
| module_index(module_index) {} |
| |
| StackSamplingProfiler::Frame::~Frame() {} |
| |
| // StackSamplingProfiler::CallStackProfile ------------------------------------ |
| |
| StackSamplingProfiler::CallStackProfile::CallStackProfile() |
| : preserve_sample_ordering(false), user_data(0) {} |
| |
| StackSamplingProfiler::CallStackProfile::~CallStackProfile() {} |
| |
| // StackSamplingProfiler::SamplingThread -------------------------------------- |
| |
| StackSamplingProfiler::SamplingThread::SamplingThread( |
| scoped_ptr<NativeStackSampler> native_sampler, |
| const SamplingParams& params, |
| CompletedCallback completed_callback) |
| : native_sampler_(native_sampler.Pass()), |
| params_(params), |
| stop_event_(false, false), |
| completed_callback_(completed_callback) { |
| } |
| |
| StackSamplingProfiler::SamplingThread::~SamplingThread() {} |
| |
| void StackSamplingProfiler::SamplingThread::ThreadMain() { |
| PlatformThread::SetName("Chrome_SamplingProfilerThread"); |
| |
| 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( |
| CallStackProfile* profile, |
| TimeDelta* elapsed_time) { |
| ElapsedTimer profile_timer; |
| CallStackProfile current_profile; |
| native_sampler_->ProfileRecordingStarting(¤t_profile.modules); |
| current_profile.sampling_period = params_.sampling_interval; |
| bool burst_completed = true; |
| TimeDelta previous_elapsed_sample_time; |
| for (int i = 0; i < params_.samples_per_burst; ++i) { |
| 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 - previous_elapsed_sample_time, |
| TimeDelta()))) { |
| 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 (burst_completed) |
| *profile = current_profile; |
| |
| 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( |
| CallStackProfiles* profiles) { |
| if (stop_event_.TimedWait(params_.initial_delay)) |
| return; |
| |
| TimeDelta previous_elapsed_profile_time; |
| for (int i = 0; i < params_.bursts; ++i) { |
| 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; |
| } |
| |
| CallStackProfile profile; |
| if (!CollectProfile(&profile, &previous_elapsed_profile_time)) |
| return; |
| profiles->push_back(profile); |
| } |
| } |
| |
| void StackSamplingProfiler::SamplingThread::Stop() { |
| stop_event_.Signal(); |
| } |
| |
| // StackSamplingProfiler ------------------------------------------------------ |
| |
| StackSamplingProfiler::SamplingParams::SamplingParams() |
| : initial_delay(TimeDelta::FromMilliseconds(0)), |
| bursts(1), |
| burst_interval(TimeDelta::FromMilliseconds(10000)), |
| samples_per_burst(300), |
| sampling_interval(TimeDelta::FromMilliseconds(100)), |
| preserve_sample_ordering(false), |
| user_data(0) { |
| } |
| |
| StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id, |
| const SamplingParams& params) |
| : thread_id_(thread_id), params_(params) {} |
| |
| 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() { |
| 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_, callback)); |
| if (!PlatformThread::Create(0, sampling_thread_.get(), |
| &sampling_thread_handle_)) |
| sampling_thread_.reset(); |
| } |
| |
| void StackSamplingProfiler::Stop() { |
| if (sampling_thread_) |
| sampling_thread_->Stop(); |
| } |
| |
| // static |
| void StackSamplingProfiler::SetDefaultCompletedCallback( |
| CompletedCallback callback) { |
| DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback); |
| } |
| |
| // StackSamplingProfiler::Frame global functions ------------------------------ |
| |
| bool operator==(const StackSamplingProfiler::Frame &a, |
| const StackSamplingProfiler::Frame &b) { |
| return a.instruction_pointer == b.instruction_pointer && |
| a.module_index == b.module_index; |
| } |
| |
| bool operator<(const StackSamplingProfiler::Frame &a, |
| const StackSamplingProfiler::Frame &b) { |
| return (a.module_index < b.module_index) || |
| (a.module_index == b.module_index && |
| a.instruction_pointer < b.instruction_pointer); |
| } |
| |
| } // namespace base |