diff options
author | wittman <wittman@chromium.org> | 2015-04-07 19:20:35 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-08 02:21:04 +0000 |
commit | ae2a08ac68bcb4000d1ddf391dbf19aa285cc48f (patch) | |
tree | 4b16d638ad7c12e54f025d78d7c544a8c7a0b705 /base/profiler | |
parent | c438c1843041746877c9dc83d9ce63a3c43bb6bb (diff) | |
download | chromium_src-ae2a08ac68bcb4000d1ddf391dbf19aa285cc48f.zip chromium_src-ae2a08ac68bcb4000d1ddf391dbf19aa285cc48f.tar.gz chromium_src-ae2a08ac68bcb4000d1ddf391dbf19aa285cc48f.tar.bz2 |
Enable startup profiling by Win x64 stack sampling profiler
Collects stack samples at 10 Hz for 30 seconds at startup. Uploading of
samples to UMA is controlled by a Finch experiment.
This change moves to a callback-based mechanism for communicating
completed profiles to the metrics provider to avoid profiles lingering
in memory when reporting is disabled.
BUG=464929
Review URL: https://codereview.chromium.org/1029653002
Cr-Commit-Position: refs/heads/master@{#324175}
Diffstat (limited to 'base/profiler')
-rw-r--r-- | base/profiler/stack_sampling_profiler.cc | 125 | ||||
-rw-r--r-- | base/profiler/stack_sampling_profiler.h | 51 | ||||
-rw-r--r-- | base/profiler/stack_sampling_profiler_unittest.cc | 184 |
3 files changed, 269 insertions, 91 deletions
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc index 317400b..1c08234 100644 --- a/base/profiler/stack_sampling_profiler.cc +++ b/base/profiler/stack_sampling_profiler.cc @@ -15,61 +15,102 @@ namespace base { -// PendingProfiles ------------------------------------------------------------ +// DefaultProfileProcessor ---------------------------------------------------- namespace { -// Thread-safe singleton class that stores collected call stack 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(); + using CompletedCallback = StackSamplingProfiler::CompletedCallback; - static PendingProfiles* GetInstance(); + ~DefaultProfileProcessor(); - // Appends |profiles| to |profiles_|. This function may be called on any - // thread. - void AppendProfiles( - const std::vector<StackSamplingProfiler::CallStackProfile>& profiles); + 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( - std::vector<StackSamplingProfiler::CallStackProfile>* profiles); + StackSamplingProfiler::CallStackProfiles* profiles); - private: - friend struct DefaultSingletonTraits<PendingProfiles>; + // Gets the current completed callback, with proper locking. + CompletedCallback GetCompletedCallback() const; - PendingProfiles(); + mutable Lock callback_lock_; + CompletedCallback default_completed_callback_; Lock profiles_lock_; - std::vector<StackSamplingProfiler::CallStackProfile> 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::AppendProfiles( - const std::vector<StackSamplingProfiler::CallStackProfile>& 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 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()); + } } -void PendingProfiles::GetAndClearPendingProfiles( - std::vector<StackSamplingProfiler::CallStackProfile>* profiles) { +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 @@ -209,7 +250,16 @@ 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() { scoped_ptr<NativeStackSampler> native_sampler = @@ -217,14 +267,14 @@ void StackSamplingProfiler::Start() { 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::AppendProfiles, - Unretained(PendingProfiles::GetInstance())) : - custom_completed_callback_))); - if (!PlatformThread::CreateNonJoinable(0, sampling_thread_.get())) + new SamplingThread(native_sampler.Pass(), params_, callback)); + if (!PlatformThread::Create(0, sampling_thread_.get(), + &sampling_thread_handle_)) sampling_thread_.reset(); } @@ -234,8 +284,9 @@ void StackSamplingProfiler::Stop() { } // static -void StackSamplingProfiler::GetPendingProfiles(CallStackProfiles* profiles) { - PendingProfiles::GetInstance()->GetAndClearPendingProfiles(profiles); +void StackSamplingProfiler::SetDefaultCompletedCallback( + CompletedCallback callback) { + DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback); } // StackSamplingProfiler::Frame global functions ------------------------------ diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h index bea266d..73dbec7 100644 --- a/base/profiler/stack_sampling_profiler.h +++ b/base/profiler/stack_sampling_profiler.h @@ -34,11 +34,12 @@ class NativeStackSampler; // base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), // params); // -// // To process the call stack profiles within Chrome rather than via UMA, -// // set a custom completed callback: +// // Or, to process the profiles within Chrome rather than via UMA, use a +// // custom completed callback: // base::StackStackSamplingProfiler::CompletedCallback // thread_safe_callback = ...; -// profiler.SetCustomCompletedCallback(thread_safe_callback); +// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), +// params, thread_safe_callback); // // profiler.Start(); // // ... work being done on the target thread here ... @@ -49,13 +50,13 @@ class NativeStackSampler; // altered as desired. // // When all call stack profiles are complete or the profiler is stopped, if the -// custom completed callback was set it is called from the profiler thread with -// the completed profiles. A profile is considered complete if all requested -// samples were recorded for the profile (i.e. it was not stopped -// prematurely). If no callback was set, the completed profiles are stored -// internally and retrieved for UMA through GetPendingProfiles(). -// GetPendingProfiles() should never be called by other code; to retrieve -// profiles for in-process processing, set a completed callback. +// custom completed callback was set it is called from a thread created by the +// profiler with the completed profiles. A profile is considered complete if all +// requested samples were recorded for the profile (i.e. it was not stopped +// prematurely). If no callback was set, the default completed callback will be +// called with the profiles. It is expected that the the default completed +// callback is set by the metrics system to allow profiles to be provided via +// UMA. // // The results of the profiling are passed to the completed callback and consist // of a vector of CallStackProfiles. Each CallStackProfile corresponds to a @@ -164,8 +165,14 @@ class BASE_EXPORT StackSamplingProfiler { // thread-safe callback implementation. using CompletedCallback = Callback<void(const CallStackProfiles&)>; + // Creates a profiler that sends completed profiles to the default completed + // callback. StackSamplingProfiler(PlatformThreadId thread_id, const SamplingParams& params); + // Creates a profiler that sends completed profiles to |completed_callback|. + StackSamplingProfiler(PlatformThreadId thread_id, + const SamplingParams& params, + CompletedCallback callback); ~StackSamplingProfiler(); // Initializes the profiler and starts sampling. @@ -176,19 +183,14 @@ class BASE_EXPORT StackSamplingProfiler { // specified in the SamplingParams are completed. void Stop(); - // Moves all pending call stack profiles from internal storage to - // |profiles|. This function is thread safe. + // Sets a callback to process profiles collected by profiler instances without + // a completed callback. Profiles are queued internally until a non-null + // callback is provided to this function, // - // ***This is intended for use only by UMA.*** Callers who want to process the - // collected profiles should use SetCustomCompletedCallback. - static void GetPendingProfiles(CallStackProfiles* call_stack_profiles); - - // By default, collected call stack profiles are stored internally and can be - // retrieved by GetPendingProfiles. If a callback is provided via this - // function, however, it is called with the collected profiles instead. - void set_custom_completed_callback(CompletedCallback callback) { - custom_completed_callback_ = callback; - } + // The callback is typically called on a thread created by the profiler. If + // completed profiles are queued when set, however, it will also be called + // immediately on the calling thread. + static void SetDefaultCompletedCallback(CompletedCallback callback); private: // SamplingThread is a separate thread used to suspend and sample stacks from @@ -226,7 +228,7 @@ class BASE_EXPORT StackSamplingProfiler { // terminate before all the samples specified in |params_| are collected. WaitableEvent stop_event_; - CompletedCallback completed_callback_; + const CompletedCallback completed_callback_; DISALLOW_COPY_AND_ASSIGN(SamplingThread); }; @@ -237,8 +239,9 @@ class BASE_EXPORT StackSamplingProfiler { const SamplingParams params_; scoped_ptr<SamplingThread> sampling_thread_; + PlatformThreadHandle sampling_thread_handle_; - CompletedCallback custom_completed_callback_; + const CompletedCallback completed_callback_; DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler); }; diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc index 5bfc2d4..e48c89b 100644 --- a/base/profiler/stack_sampling_profiler_unittest.cc +++ b/base/profiler/stack_sampling_profiler_unittest.cc @@ -14,10 +14,12 @@ namespace base { +using SamplingParams = StackSamplingProfiler::SamplingParams; using Frame = StackSamplingProfiler::Frame; using Module = StackSamplingProfiler::Module; using Sample = StackSamplingProfiler::Sample; using CallStackProfile = StackSamplingProfiler::CallStackProfile; +using CallStackProfiles = StackSamplingProfiler::CallStackProfiles; namespace { @@ -85,45 +87,99 @@ NOINLINE void TargetThread::SignalAndWaitUntilSignaled( ALLOW_UNUSED_LOCAL(x); } +// Called on the profiler thread when complete, to collect profiles. +void SaveProfiles(CallStackProfiles* profiles, + const CallStackProfiles& pending_profiles) { + *profiles = pending_profiles; +} + // Called on the profiler thread when complete. Collects profiles produced by // the profiler, and signals an event to allow the main thread to know that that // the profiler is done. -void SaveProfilesAndSignalEvent( - std::vector<CallStackProfile>* profiles, - WaitableEvent* event, - const std::vector<CallStackProfile>& pending_profiles) { +void SaveProfilesAndSignalEvent(CallStackProfiles* profiles, + WaitableEvent* event, + const CallStackProfiles& pending_profiles) { *profiles = pending_profiles; event->Signal(); } -// Captures call stack profiles as specified by |params| on the TargetThread, -// and returns them in |profiles|. Waits up to |profiler_wait_time| for the -// profiler to complete. -void CaptureProfiles(const StackSamplingProfiler::SamplingParams& params, - std::vector<CallStackProfile>* profiles, - TimeDelta profiler_wait_time) { +// Executes the function with the target thread running and executing within +// SignalAndWaitUntilSignaled(). Performs all necessary target thread startup +// and shutdown work before and afterward. +template <class Function> +void WithTargetThread(Function function) { TargetThread target_thread; PlatformThreadHandle target_thread_handle; EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle)); target_thread.WaitForThreadStart(); - WaitableEvent sampling_thread_completed(true, false); - profiles->clear(); - StackSamplingProfiler profiler(target_thread.id(), params); - profiler.set_custom_completed_callback( - Bind(&SaveProfilesAndSignalEvent, Unretained(profiles), - Unretained(&sampling_thread_completed))); - profiler.Start(); - sampling_thread_completed.TimedWait(profiler_wait_time); - profiler.Stop(); - sampling_thread_completed.Wait(); + function(target_thread.id()); target_thread.SignalThreadToFinish(); PlatformThread::Join(target_thread_handle); } +// Captures profiles as specified by |params| on the TargetThread, and returns +// them in |profiles|. Waits up to |profiler_wait_time| for the profiler to +// complete. +void CaptureProfilesWithObjectCallback(const SamplingParams& params, + CallStackProfiles* profiles, + TimeDelta profiler_wait_time) { + profiles->clear(); + + WithTargetThread([¶ms, profiles, profiler_wait_time]( + PlatformThreadId target_thread_id) { + WaitableEvent sampling_thread_completed(true, false); + const StackSamplingProfiler::CompletedCallback callback = + Bind(&SaveProfilesAndSignalEvent, Unretained(profiles), + Unretained(&sampling_thread_completed)); + StackSamplingProfiler profiler(target_thread_id, params, callback); + profiler.Start(); + sampling_thread_completed.TimedWait(profiler_wait_time); + profiler.Stop(); + sampling_thread_completed.Wait(); + }); +} + +// Captures profiles as specified by |params| on the TargetThread, and returns +// them in |profiles|. Uses the default callback rather than a per-object +// callback. +void CaptureProfilesWithDefaultCallback(const SamplingParams& params, + CallStackProfiles* profiles) { + profiles->clear(); + + WithTargetThread([¶ms, profiles](PlatformThreadId target_thread_id) { + WaitableEvent sampling_thread_completed(false, false); + StackSamplingProfiler::SetDefaultCompletedCallback( + Bind(&SaveProfilesAndSignalEvent, Unretained(profiles), + Unretained(&sampling_thread_completed))); + + StackSamplingProfiler profiler(target_thread_id, params); + profiler.Start(); + sampling_thread_completed.Wait(); + + StackSamplingProfiler::SetDefaultCompletedCallback( + StackSamplingProfiler::CompletedCallback()); + }); +} + +// Runs the profiler with |params| on the TargetThread, with no default or +// per-object callback. +void RunProfilerWithNoCallback(const SamplingParams& params, + TimeDelta profiler_wait_time) { + WithTargetThread( + [¶ms, profiler_wait_time](PlatformThreadId target_thread_id) { + StackSamplingProfiler profiler(target_thread_id, params); + profiler.Start(); + // Since we don't specify a callback, we don't have a synchronization + // mechanism with the sampling thread. Just sleep instead. + PlatformThread::Sleep(profiler_wait_time); + profiler.Stop(); + }); +} + // If this executable was linked with /INCREMENTAL (the default for non-official // debug and release builds on Windows), function addresses do not correspond to // function code itself, but instead to instructions in the Incremental Link @@ -195,12 +251,12 @@ TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); } #define MAYBE_Basic DISABLED_Basic #endif TEST(StackSamplingProfilerTest, MAYBE_Basic) { - StackSamplingProfiler::SamplingParams params; + SamplingParams params; params.sampling_interval = TimeDelta::FromMilliseconds(0); params.samples_per_burst = 1; std::vector<CallStackProfile> profiles; - CaptureProfiles(params, &profiles, AVeryLongTimeDelta()); + CaptureProfilesWithObjectCallback(params, &profiles, AVeryLongTimeDelta()); // Check that the profile and samples sizes are correct, and the module // indices are in range. @@ -244,14 +300,14 @@ TEST(StackSamplingProfilerTest, MAYBE_Basic) { #define MAYBE_MultipleProfilesAndSamples DISABLED_MultipleProfilesAndSamples #endif TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) { - StackSamplingProfiler::SamplingParams params; + SamplingParams params; params.burst_interval = params.sampling_interval = TimeDelta::FromMilliseconds(0); params.bursts = 2; params.samples_per_burst = 3; std::vector<CallStackProfile> profiles; - CaptureProfiles(params, &profiles, AVeryLongTimeDelta()); + CaptureProfilesWithObjectCallback(params, &profiles, AVeryLongTimeDelta()); ASSERT_EQ(2u, profiles.size()); EXPECT_EQ(3u, profiles[0].samples.size()); @@ -266,11 +322,12 @@ TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) { #define MAYBE_StopDuringInitialDelay DISABLED_StopDuringInitialDelay #endif TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) { - StackSamplingProfiler::SamplingParams params; + SamplingParams params; params.initial_delay = TimeDelta::FromSeconds(60); std::vector<CallStackProfile> profiles; - CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(0)); + CaptureProfilesWithObjectCallback(params, &profiles, + TimeDelta::FromMilliseconds(0)); EXPECT_TRUE(profiles.empty()); } @@ -283,14 +340,15 @@ TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) { #define MAYBE_StopDuringInterBurstInterval DISABLED_StopDuringInterBurstInterval #endif TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) { - StackSamplingProfiler::SamplingParams params; + SamplingParams params; params.sampling_interval = TimeDelta::FromMilliseconds(0); params.burst_interval = TimeDelta::FromSeconds(60); params.bursts = 2; params.samples_per_burst = 1; std::vector<CallStackProfile> profiles; - CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50)); + CaptureProfilesWithObjectCallback(params, &profiles, + TimeDelta::FromMilliseconds(50)); ASSERT_EQ(1u, profiles.size()); EXPECT_EQ(1u, profiles[0].samples.size()); @@ -304,14 +362,80 @@ TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) { DISABLED_StopDuringInterSampleInterval #endif TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterSampleInterval) { - StackSamplingProfiler::SamplingParams params; + SamplingParams params; params.sampling_interval = TimeDelta::FromSeconds(60); params.samples_per_burst = 2; std::vector<CallStackProfile> profiles; - CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50)); + CaptureProfilesWithObjectCallback(params, &profiles, + TimeDelta::FromMilliseconds(50)); EXPECT_TRUE(profiles.empty()); } +// Checks that profiles are captured via the default completed callback. +#if defined(_WIN64) +#define MAYBE_DefaultCallback DefaultCallback +#else +#define MAYBE_DefaultCallback DISABLED_DefaultCallback +#endif +TEST(StackSamplingProfilerTest, MAYBE_DefaultCallback) { + SamplingParams params; + params.samples_per_burst = 1; + + CallStackProfiles profiles; + CaptureProfilesWithDefaultCallback(params, &profiles); + + EXPECT_EQ(1u, profiles.size()); + EXPECT_EQ(1u, profiles[0].samples.size()); +} + +// Checks that profiles are queued until a default callback is set, then +// delivered. +#if defined(_WIN64) +#define MAYBE_ProfilesQueuedWithNoCallback ProfilesQueuedWithNoCallback +#else +#define MAYBE_ProfilesQueuedWithNoCallback DISABLED_ProfilesQueuedWithNoCallback +#endif +TEST(StackSamplingProfilerTest, MAYBE_ProfilesQueuedWithNoCallback) { + SamplingParams params; + params.samples_per_burst = 1; + + RunProfilerWithNoCallback(params, TimeDelta::FromMilliseconds(50)); + + CallStackProfiles profiles; + // This should immediately call SaveProfiles on this thread. + StackSamplingProfiler::SetDefaultCompletedCallback( + Bind(&SaveProfiles, Unretained(&profiles))); + EXPECT_EQ(1u, profiles.size()); + EXPECT_EQ(1u, profiles[0].samples.size()); + StackSamplingProfiler::SetDefaultCompletedCallback( + StackSamplingProfiler::CompletedCallback()); +} + +// Checks that we can destroy the profiler while profiling. +#if defined(_WIN64) +#define MAYBE_DestroyProfilerWhileProfiling DestroyProfilerWhileProfiling +#else +#define MAYBE_DestroyProfilerWhileProfiling \ + DISABLED_DestroyProfilerWhileProfiling +#endif +TEST(StackSamplingProfilerTest, MAYBE_DestroyProfilerWhileProfiling) { + SamplingParams params; + params.sampling_interval = TimeDelta::FromMilliseconds(10); + + CallStackProfiles profiles; + WithTargetThread([¶ms, &profiles](PlatformThreadId target_thread_id) { + scoped_ptr<StackSamplingProfiler> profiler; + profiler.reset(new StackSamplingProfiler( + target_thread_id, params, Bind(&SaveProfiles, Unretained(&profiles)))); + profiler->Start(); + profiler.reset(); + + // Wait longer than a sample interval to catch any use-after-free actions by + // the profiler thread. + PlatformThread::Sleep(TimeDelta::FromMilliseconds(50)); + }); +} + } // namespace base |