// 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 #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(); // 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::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 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 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