|  | // 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 |