| // 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 <map> | 
 | #include <utility> | 
 |  | 
 | #include "base/atomic_sequence_num.h" | 
 | #include "base/atomicops.h" | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/callback.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/location.h" | 
 | #include "base/macros.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/memory/singleton.h" | 
 | #include "base/profiler/native_stack_sampler.h" | 
 | #include "base/synchronization/lock.h" | 
 | #include "base/threading/thread.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "base/timer/elapsed_timer.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | namespace { | 
 |  | 
 | // This value is used to initialize the WaitableEvent object. This MUST BE set | 
 | // to MANUAL for correct operation of the IsSignaled() call in Start(). See the | 
 | // comment there for why. | 
 | constexpr WaitableEvent::ResetPolicy kResetPolicy = | 
 |     WaitableEvent::ResetPolicy::MANUAL; | 
 |  | 
 | // This value is used when there is no collection in progress and thus no ID | 
 | // for referencing the active collection to the SamplingThread. | 
 | const int NULL_PROFILER_ID = -1; | 
 |  | 
 | void ChangeAtomicFlags(subtle::Atomic32* flags, | 
 |                        subtle::Atomic32 set, | 
 |                        subtle::Atomic32 clear) { | 
 |   DCHECK(set != 0 || clear != 0); | 
 |   DCHECK_EQ(0, set & clear); | 
 |  | 
 |   subtle::Atomic32 bits = subtle::NoBarrier_Load(flags); | 
 |   while (true) { | 
 |     subtle::Atomic32 existing = | 
 |         subtle::NoBarrier_CompareAndSwap(flags, bits, (bits | set) & ~clear); | 
 |     if (existing == bits) | 
 |       break; | 
 |     bits = existing; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // StackSamplingProfiler::Module ---------------------------------------------- | 
 |  | 
 | StackSamplingProfiler::Module::Module() : base_address(0u) {} | 
 | StackSamplingProfiler::Module::Module(uintptr_t base_address, | 
 |                                       const std::string& id, | 
 |                                       const FilePath& filename) | 
 |     : base_address(base_address), id(id), filename(filename) {} | 
 |  | 
 | StackSamplingProfiler::Module::~Module() = default; | 
 |  | 
 | // StackSamplingProfiler::Frame ----------------------------------------------- | 
 |  | 
 | StackSamplingProfiler::Frame::Frame(uintptr_t instruction_pointer, | 
 |                                     size_t module_index) | 
 |     : instruction_pointer(instruction_pointer), module_index(module_index) {} | 
 |  | 
 | StackSamplingProfiler::Frame::~Frame() = default; | 
 |  | 
 | StackSamplingProfiler::Frame::Frame() | 
 |     : instruction_pointer(0), module_index(kUnknownModuleIndex) { | 
 | } | 
 |  | 
 | // StackSamplingProfiler::Sample ---------------------------------------------- | 
 |  | 
 | StackSamplingProfiler::Sample::Sample() = default; | 
 |  | 
 | StackSamplingProfiler::Sample::Sample(const Sample& sample) = default; | 
 |  | 
 | StackSamplingProfiler::Sample::~Sample() = default; | 
 |  | 
 | StackSamplingProfiler::Sample::Sample(const Frame& frame) { | 
 |   frames.push_back(std::move(frame)); | 
 | } | 
 |  | 
 | StackSamplingProfiler::Sample::Sample(const std::vector<Frame>& frames) | 
 |     : frames(frames) {} | 
 |  | 
 | // StackSamplingProfiler::CallStackProfile ------------------------------------ | 
 |  | 
 | StackSamplingProfiler::CallStackProfile::CallStackProfile() = default; | 
 |  | 
 | StackSamplingProfiler::CallStackProfile::CallStackProfile( | 
 |     CallStackProfile&& other) = default; | 
 |  | 
 | StackSamplingProfiler::CallStackProfile::~CallStackProfile() = default; | 
 |  | 
 | StackSamplingProfiler::CallStackProfile& | 
 | StackSamplingProfiler::CallStackProfile::operator=(CallStackProfile&& other) = | 
 |     default; | 
 |  | 
 | StackSamplingProfiler::CallStackProfile | 
 | StackSamplingProfiler::CallStackProfile::CopyForTesting() const { | 
 |   return CallStackProfile(*this); | 
 | } | 
 |  | 
 | StackSamplingProfiler::CallStackProfile::CallStackProfile( | 
 |     const CallStackProfile& other) = default; | 
 |  | 
 | // StackSamplingProfiler::SamplingThread -------------------------------------- | 
 |  | 
 | class StackSamplingProfiler::SamplingThread : public Thread { | 
 |  public: | 
 |   class TestAPI { | 
 |    public: | 
 |     // Reset the existing sampler. This will unfortunately create the object | 
 |     // unnecessarily if it doesn't already exist but there's no way around that. | 
 |     static void Reset(); | 
 |  | 
 |     // Disables inherent idle-shutdown behavior. | 
 |     static void DisableIdleShutdown(); | 
 |  | 
 |     // Begins an idle shutdown as if the idle-timer had expired and wait for | 
 |     // it to execute. Since the timer would have only been started at a time | 
 |     // when the sampling thread actually was idle, this must be called only | 
 |     // when it is known that there are no active sampling threads. If | 
 |     // |simulate_intervening_add| is true then, when executed, the shutdown | 
 |     // task will believe that a new collection has been added since it was | 
 |     // posted. | 
 |     static void ShutdownAssumingIdle(bool simulate_intervening_add); | 
 |  | 
 |    private: | 
 |     // Calls the sampling threads ShutdownTask and then signals an event. | 
 |     static void ShutdownTaskAndSignalEvent(SamplingThread* sampler, | 
 |                                            int add_events, | 
 |                                            WaitableEvent* event); | 
 |   }; | 
 |  | 
 |   struct CollectionContext { | 
 |     CollectionContext(int profiler_id, | 
 |                       PlatformThreadId target, | 
 |                       const SamplingParams& params, | 
 |                       const CompletedCallback& callback, | 
 |                       WaitableEvent* finished, | 
 |                       std::unique_ptr<NativeStackSampler> sampler) | 
 |         : profiler_id(profiler_id), | 
 |           target(target), | 
 |           params(params), | 
 |           callback(callback), | 
 |           finished(finished), | 
 |           native_sampler(std::move(sampler)) {} | 
 |     ~CollectionContext() = default; | 
 |  | 
 |     // An identifier for the profiler associated with this collection, used to | 
 |     // uniquely identify the collection to outside interests. | 
 |     const int profiler_id; | 
 |  | 
 |     const PlatformThreadId target;     // ID of The thread being sampled. | 
 |     const SamplingParams params;       // Information about how to sample. | 
 |     const CompletedCallback callback;  // Callback made when sampling complete. | 
 |     WaitableEvent* const finished;     // Signaled when all sampling complete. | 
 |  | 
 |     // Platform-specific module that does the actual sampling. | 
 |     std::unique_ptr<NativeStackSampler> native_sampler; | 
 |  | 
 |     // The absolute time for the next sample. | 
 |     Time next_sample_time; | 
 |  | 
 |     // The time that a profile was started, for calculating the total duration. | 
 |     Time profile_start_time; | 
 |  | 
 |     // Counters that indicate the current position along the acquisition. | 
 |     int burst = 0; | 
 |     int sample = 0; | 
 |  | 
 |     // The collected stack samples. The active profile is always at the back(). | 
 |     CallStackProfiles profiles; | 
 |  | 
 |     // Sequence number for generating new profiler ids. | 
 |     static AtomicSequenceNumber next_profiler_id; | 
 |   }; | 
 |  | 
 |   // Gets the single instance of this class. | 
 |   static SamplingThread* GetInstance(); | 
 |  | 
 |   // Adds a new CollectionContext to the thread. This can be called externally | 
 |   // from any thread. This returns an ID that can later be used to stop | 
 |   // the sampling. | 
 |   int Add(std::unique_ptr<CollectionContext> collection); | 
 |  | 
 |   // Removes an active collection based on its ID, forcing it to run its | 
 |   // callback if any data has been collected. This can be called externally | 
 |   // from any thread. | 
 |   void Remove(int id); | 
 |  | 
 |  private: | 
 |   friend class TestAPI; | 
 |   friend struct DefaultSingletonTraits<SamplingThread>; | 
 |  | 
 |   // The different states in which the sampling-thread can be. | 
 |   enum ThreadExecutionState { | 
 |     // The thread is not running because it has never been started. It will be | 
 |     // started when a sampling request is received. | 
 |     NOT_STARTED, | 
 |  | 
 |     // The thread is running and processing tasks. This is the state when any | 
 |     // sampling requests are active and during the "idle" period afterward | 
 |     // before the thread is stopped. | 
 |     RUNNING, | 
 |  | 
 |     // Once all sampling requests have finished and the "idle" period has | 
 |     // expired, the thread will be set to this state and its shutdown | 
 |     // initiated. A call to Stop() must be made to ensure the previous thread | 
 |     // has completely exited before calling Start() and moving back to the | 
 |     // RUNNING state. | 
 |     EXITING, | 
 |   }; | 
 |  | 
 |   SamplingThread(); | 
 |   ~SamplingThread() override; | 
 |  | 
 |   // Get task runner that is usable from the outside. | 
 |   scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd(); | 
 |   scoped_refptr<SingleThreadTaskRunner> GetTaskRunner( | 
 |       ThreadExecutionState* out_state); | 
 |  | 
 |   // Get task runner that is usable from the sampling thread itself. | 
 |   scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread(); | 
 |  | 
 |   // Finishes a collection and reports collected data via callback. The | 
 |   // collection's |finished| waitable event will be signalled. The |collection| | 
 |   // should already have been removed from |active_collections_| by the caller, | 
 |   // as this is needed to avoid flakyness in unit tests. | 
 |   void FinishCollection(CollectionContext* collection); | 
 |  | 
 |   // Records a single sample of a collection. | 
 |   void RecordSample(CollectionContext* collection); | 
 |  | 
 |   // Check if the sampling thread is idle and begin a shutdown if it is. | 
 |   void ScheduleShutdownIfIdle(); | 
 |  | 
 |   // These methods are tasks that get posted to the internal message queue. | 
 |   void AddCollectionTask(std::unique_ptr<CollectionContext> collection); | 
 |   void RemoveCollectionTask(int id); | 
 |   void PerformCollectionTask(int id); | 
 |   void ShutdownTask(int add_events); | 
 |  | 
 |   // Updates the |next_sample_time| time based on configured parameters. | 
 |   // Returns true if there is a next sample or false if sampling is complete. | 
 |   bool UpdateNextSampleTime(CollectionContext* collection); | 
 |  | 
 |   // Thread: | 
 |   void CleanUp() override; | 
 |  | 
 |   // A stack-buffer used by the native sampler for its work. This buffer can | 
 |   // be re-used for multiple native sampler objects so long as the API calls | 
 |   // that take it are not called concurrently. | 
 |   std::unique_ptr<NativeStackSampler::StackBuffer> stack_buffer_; | 
 |  | 
 |   // A map of IDs to collection contexts. Because this class is a singleton | 
 |   // that is never destroyed, context objects will never be destructed except | 
 |   // by explicit action. Thus, it's acceptable to pass unretained pointers | 
 |   // to these objects when posting tasks. | 
 |   std::map<int, std::unique_ptr<CollectionContext>> active_collections_; | 
 |  | 
 |   // State maintained about the current execution (or non-execution) of | 
 |   // the thread. This state must always be accessed while holding the | 
 |   // lock. A copy of the task-runner is maintained here for use by any | 
 |   // calling thread; this is necessary because Thread's accessor for it is | 
 |   // not itself thread-safe. The lock is also used to order calls to the | 
 |   // Thread API (Start, Stop, StopSoon, & DetachFromSequence) so that | 
 |   // multiple threads may make those calls. | 
 |   Lock thread_execution_state_lock_;  // Protects all thread_execution_state_* | 
 |   ThreadExecutionState thread_execution_state_ = NOT_STARTED; | 
 |   scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_; | 
 |   bool thread_execution_state_disable_idle_shutdown_for_testing_ = false; | 
 |  | 
 |   // A counter that notes adds of new collection requests. It is incremented | 
 |   // when changes occur so that delayed shutdown tasks are able to detect if | 
 |   // samething new has happened while it was waiting. Like all "execution_state" | 
 |   // vars, this must be accessed while holding |thread_execution_state_lock_|. | 
 |   int thread_execution_state_add_events_ = 0; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(SamplingThread); | 
 | }; | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::SamplingThread::TestAPI::Reset() { | 
 |   SamplingThread* sampler = SamplingThread::GetInstance(); | 
 |  | 
 |   ThreadExecutionState state; | 
 |   { | 
 |     AutoLock lock(sampler->thread_execution_state_lock_); | 
 |     state = sampler->thread_execution_state_; | 
 |     DCHECK(sampler->active_collections_.empty()); | 
 |   } | 
 |  | 
 |   // Stop the thread and wait for it to exit. This has to be done through by | 
 |   // the thread itself because it has taken ownership of its own lifetime. | 
 |   if (state == RUNNING) { | 
 |     ShutdownAssumingIdle(false); | 
 |     state = EXITING; | 
 |   } | 
 |   // Make sure thread is cleaned up since state will be reset to NOT_STARTED. | 
 |   if (state == EXITING) | 
 |     sampler->Stop(); | 
 |  | 
 |   // Reset internal variables to the just-initialized state. | 
 |   { | 
 |     AutoLock lock(sampler->thread_execution_state_lock_); | 
 |     sampler->thread_execution_state_ = NOT_STARTED; | 
 |     sampler->thread_execution_state_task_runner_ = nullptr; | 
 |     sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = false; | 
 |     sampler->thread_execution_state_add_events_ = 0; | 
 |   } | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::SamplingThread::TestAPI::DisableIdleShutdown() { | 
 |   SamplingThread* sampler = SamplingThread::GetInstance(); | 
 |  | 
 |   { | 
 |     AutoLock lock(sampler->thread_execution_state_lock_); | 
 |     sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = true; | 
 |   } | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownAssumingIdle( | 
 |     bool simulate_intervening_add) { | 
 |   SamplingThread* sampler = SamplingThread::GetInstance(); | 
 |  | 
 |   ThreadExecutionState state; | 
 |   scoped_refptr<SingleThreadTaskRunner> task_runner = | 
 |       sampler->GetTaskRunner(&state); | 
 |   DCHECK_EQ(RUNNING, state); | 
 |   DCHECK(task_runner); | 
 |  | 
 |   int add_events; | 
 |   { | 
 |     AutoLock lock(sampler->thread_execution_state_lock_); | 
 |     add_events = sampler->thread_execution_state_add_events_; | 
 |     if (simulate_intervening_add) | 
 |       ++sampler->thread_execution_state_add_events_; | 
 |   } | 
 |  | 
 |   WaitableEvent executed(WaitableEvent::ResetPolicy::MANUAL, | 
 |                          WaitableEvent::InitialState::NOT_SIGNALED); | 
 |   // PostTaskAndReply won't work because thread and associated message-loop may | 
 |   // be shut down. | 
 |   task_runner->PostTask( | 
 |       FROM_HERE, BindOnce(&ShutdownTaskAndSignalEvent, Unretained(sampler), | 
 |                           add_events, Unretained(&executed))); | 
 |   executed.Wait(); | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownTaskAndSignalEvent( | 
 |     SamplingThread* sampler, | 
 |     int add_events, | 
 |     WaitableEvent* event) { | 
 |   sampler->ShutdownTask(add_events); | 
 |   event->Signal(); | 
 | } | 
 |  | 
 | AtomicSequenceNumber | 
 |     StackSamplingProfiler::SamplingThread::CollectionContext::next_profiler_id; | 
 |  | 
 | StackSamplingProfiler::SamplingThread::SamplingThread() | 
 |     : Thread("StackSamplingProfiler") {} | 
 |  | 
 | StackSamplingProfiler::SamplingThread::~SamplingThread() = default; | 
 |  | 
 | StackSamplingProfiler::SamplingThread* | 
 | StackSamplingProfiler::SamplingThread::GetInstance() { | 
 |   return Singleton<SamplingThread, LeakySingletonTraits<SamplingThread>>::get(); | 
 | } | 
 |  | 
 | int StackSamplingProfiler::SamplingThread::Add( | 
 |     std::unique_ptr<CollectionContext> collection) { | 
 |   // This is not to be run on the sampling thread. | 
 |  | 
 |   int id = collection->profiler_id; | 
 |   scoped_refptr<SingleThreadTaskRunner> task_runner = | 
 |       GetOrCreateTaskRunnerForAdd(); | 
 |  | 
 |   task_runner->PostTask( | 
 |       FROM_HERE, BindOnce(&SamplingThread::AddCollectionTask, Unretained(this), | 
 |                           std::move(collection))); | 
 |  | 
 |   return id; | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::Remove(int id) { | 
 |   // This is not to be run on the sampling thread. | 
 |  | 
 |   ThreadExecutionState state; | 
 |   scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state); | 
 |   if (state != RUNNING) | 
 |     return; | 
 |   DCHECK(task_runner); | 
 |  | 
 |   // This can fail if the thread were to exit between acquisition of the task | 
 |   // runner above and the call below. In that case, however, everything has | 
 |   // stopped so there's no need to try to stop it. | 
 |   task_runner->PostTask( | 
 |       FROM_HERE, | 
 |       BindOnce(&SamplingThread::RemoveCollectionTask, Unretained(this), id)); | 
 | } | 
 |  | 
 | scoped_refptr<SingleThreadTaskRunner> | 
 | StackSamplingProfiler::SamplingThread::GetOrCreateTaskRunnerForAdd() { | 
 |   AutoLock lock(thread_execution_state_lock_); | 
 |  | 
 |   // The increment of the "add events" count is why this method is to be only | 
 |   // called from "add". | 
 |   ++thread_execution_state_add_events_; | 
 |  | 
 |   if (thread_execution_state_ == RUNNING) { | 
 |     DCHECK(thread_execution_state_task_runner_); | 
 |     // This shouldn't be called from the sampling thread as it's inefficient. | 
 |     // Use GetTaskRunnerOnSamplingThread() instead. | 
 |     DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); | 
 |     return thread_execution_state_task_runner_; | 
 |   } | 
 |  | 
 |   if (thread_execution_state_ == EXITING) { | 
 |     // StopSoon() was previously called to shut down the thread | 
 |     // asynchonously. Stop() must now be called before calling Start() again to | 
 |     // reset the thread state. | 
 |     // | 
 |     // We must allow blocking here to satisfy the Thread implementation, but in | 
 |     // practice the Stop() call is unlikely to actually block. For this to | 
 |     // happen a new profiling request would have to be made within the narrow | 
 |     // window between StopSoon() and thread exit following the end of the 60 | 
 |     // second idle period. | 
 |     ScopedAllowBlocking allow_blocking; | 
 |     Stop(); | 
 |   } | 
 |  | 
 |   DCHECK(!stack_buffer_); | 
 |   stack_buffer_ = NativeStackSampler::CreateStackBuffer(); | 
 |  | 
 |   // The thread is not running. Start it and get associated runner. The task- | 
 |   // runner has to be saved for future use because though it can be used from | 
 |   // any thread, it can be acquired via task_runner() only on the created | 
 |   // thread and the thread that creates it (i.e. this thread) for thread-safety | 
 |   // reasons which are alleviated in SamplingThread by gating access to it with | 
 |   // the |thread_execution_state_lock_|. | 
 |   Start(); | 
 |   thread_execution_state_ = RUNNING; | 
 |   thread_execution_state_task_runner_ = Thread::task_runner(); | 
 |  | 
 |   // Detach the sampling thread from the "sequence" (i.e. thread) that | 
 |   // started it so that it can be self-managed or stopped by another thread. | 
 |   DetachFromSequence(); | 
 |  | 
 |   return thread_execution_state_task_runner_; | 
 | } | 
 |  | 
 | scoped_refptr<SingleThreadTaskRunner> | 
 | StackSamplingProfiler::SamplingThread::GetTaskRunner( | 
 |     ThreadExecutionState* out_state) { | 
 |   AutoLock lock(thread_execution_state_lock_); | 
 |   if (out_state) | 
 |     *out_state = thread_execution_state_; | 
 |   if (thread_execution_state_ == RUNNING) { | 
 |     // This shouldn't be called from the sampling thread as it's inefficient. | 
 |     // Use GetTaskRunnerOnSamplingThread() instead. | 
 |     DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); | 
 |     DCHECK(thread_execution_state_task_runner_); | 
 |   } else { | 
 |     DCHECK(!thread_execution_state_task_runner_); | 
 |   } | 
 |  | 
 |   return thread_execution_state_task_runner_; | 
 | } | 
 |  | 
 | scoped_refptr<SingleThreadTaskRunner> | 
 | StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() { | 
 |   // This should be called only from the sampling thread as it has limited | 
 |   // accessibility. | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   return Thread::task_runner(); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::FinishCollection( | 
 |     CollectionContext* collection) { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |   DCHECK_EQ(0u, active_collections_.count(collection->profiler_id)); | 
 |  | 
 |   // If there is no duration for the final profile (because it was stopped), | 
 |   // calculate it now. | 
 |   if (!collection->profiles.empty() && | 
 |       collection->profiles.back().profile_duration == TimeDelta()) { | 
 |     collection->profiles.back().profile_duration = | 
 |         Time::Now() - collection->profile_start_time + | 
 |         collection->params.sampling_interval; | 
 |   } | 
 |  | 
 |   // Extract some information so callback and event-signalling can still be | 
 |   // done after the collection has been removed from the list of "active" ones. | 
 |   // This allows the the controlling object (and tests using it) to be confident | 
 |   // that collection is fully finished when those things occur. | 
 |   const CompletedCallback callback = collection->callback; | 
 |   CallStackProfiles profiles = std::move(collection->profiles); | 
 |   WaitableEvent* finished = collection->finished; | 
 |  | 
 |   // Run the associated callback, passing the collected profiles. | 
 |   callback.Run(std::move(profiles)); | 
 |  | 
 |   // Signal that this collection is finished. | 
 |   finished->Signal(); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::RecordSample( | 
 |     CollectionContext* collection) { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |   DCHECK(collection->native_sampler); | 
 |  | 
 |   // If this is the first sample of a burst, a new Profile needs to be created | 
 |   // and filled. | 
 |   if (collection->sample == 0) { | 
 |     collection->profiles.push_back(CallStackProfile()); | 
 |     CallStackProfile& profile = collection->profiles.back(); | 
 |     profile.sampling_period = collection->params.sampling_interval; | 
 |     collection->profile_start_time = Time::Now(); | 
 |     collection->native_sampler->ProfileRecordingStarting(&profile.modules); | 
 |   } | 
 |  | 
 |   // The currently active profile being captured. | 
 |   CallStackProfile& profile = collection->profiles.back(); | 
 |  | 
 |   // Record a single sample. | 
 |   profile.samples.push_back(Sample()); | 
 |   collection->native_sampler->RecordStackSample(stack_buffer_.get(), | 
 |                                                 &profile.samples.back()); | 
 |  | 
 |   // If this is the last sample of a burst, record the total time. | 
 |   if (collection->sample == collection->params.samples_per_burst - 1) { | 
 |     profile.profile_duration = Time::Now() - collection->profile_start_time + | 
 |                                collection->params.sampling_interval; | 
 |     collection->native_sampler->ProfileRecordingStopped(stack_buffer_.get()); | 
 |   } | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle() { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   if (!active_collections_.empty()) | 
 |     return; | 
 |  | 
 |   int add_events; | 
 |   { | 
 |     AutoLock lock(thread_execution_state_lock_); | 
 |     if (thread_execution_state_disable_idle_shutdown_for_testing_) | 
 |       return; | 
 |     add_events = thread_execution_state_add_events_; | 
 |   } | 
 |  | 
 |   GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       BindOnce(&SamplingThread::ShutdownTask, Unretained(this), add_events), | 
 |       TimeDelta::FromSeconds(60)); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::AddCollectionTask( | 
 |     std::unique_ptr<CollectionContext> collection) { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   const int profiler_id = collection->profiler_id; | 
 |   const TimeDelta initial_delay = collection->params.initial_delay; | 
 |  | 
 |   active_collections_.insert( | 
 |       std::make_pair(profiler_id, std::move(collection))); | 
 |  | 
 |   GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       BindOnce(&SamplingThread::PerformCollectionTask, Unretained(this), | 
 |                profiler_id), | 
 |       initial_delay); | 
 |  | 
 |   // Another increment of "add events" serves to invalidate any pending | 
 |   // shutdown tasks that may have been initiated between the Add() and this | 
 |   // task running. | 
 |   { | 
 |     AutoLock lock(thread_execution_state_lock_); | 
 |     ++thread_execution_state_add_events_; | 
 |   } | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   auto found = active_collections_.find(id); | 
 |   if (found == active_collections_.end()) | 
 |     return; | 
 |  | 
 |   // Remove |collection| from |active_collections_|. | 
 |   std::unique_ptr<CollectionContext> collection = std::move(found->second); | 
 |   size_t count = active_collections_.erase(id); | 
 |   DCHECK_EQ(1U, count); | 
 |  | 
 |   FinishCollection(collection.get()); | 
 |   ScheduleShutdownIfIdle(); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   auto found = active_collections_.find(id); | 
 |  | 
 |   // The task won't be found if it has been stopped. | 
 |   if (found == active_collections_.end()) | 
 |     return; | 
 |  | 
 |   CollectionContext* collection = found->second.get(); | 
 |  | 
 |   // Handle first-run with no "next time". | 
 |   if (collection->next_sample_time == Time()) | 
 |     collection->next_sample_time = Time::Now(); | 
 |  | 
 |   // Do the collection of a single sample. | 
 |   RecordSample(collection); | 
 |  | 
 |   // Update the time of the next sample recording. | 
 |   const bool collection_finished = !UpdateNextSampleTime(collection); | 
 |   if (!collection_finished) { | 
 |     bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 
 |         FROM_HERE, | 
 |         BindOnce(&SamplingThread::PerformCollectionTask, Unretained(this), id), | 
 |         std::max(collection->next_sample_time - Time::Now(), TimeDelta())); | 
 |     DCHECK(success); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Take ownership of |collection| and remove it from the map. If collection is | 
 |   // to be restarted, a new collection task will be added below. | 
 |   std::unique_ptr<CollectionContext> owned_collection = | 
 |       std::move(found->second); | 
 |   size_t count = active_collections_.erase(id); | 
 |   DCHECK_EQ(1U, count); | 
 |  | 
 |   // All capturing has completed so finish the collection. | 
 |   FinishCollection(collection); | 
 |   ScheduleShutdownIfIdle(); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   // Holding this lock ensures that any attempt to start another job will | 
 |   // get postponed until |thread_execution_state_| is updated, thus eliminating | 
 |   // the race in starting a new thread while the previous one is exiting. | 
 |   AutoLock lock(thread_execution_state_lock_); | 
 |  | 
 |   // If the current count of creation requests doesn't match the passed count | 
 |   // then other tasks have been created since this was posted. Abort shutdown. | 
 |   if (thread_execution_state_add_events_ != add_events) | 
 |     return; | 
 |  | 
 |   // There can be no new AddCollectionTasks at this point because creating | 
 |   // those always increments "add events". There may be other requests, like | 
 |   // Remove, but it's okay to schedule the thread to stop once they've been | 
 |   // executed (i.e. "soon"). | 
 |   DCHECK(active_collections_.empty()); | 
 |   StopSoon(); | 
 |  | 
 |   // StopSoon will have set the owning sequence (again) so it must be detached | 
 |   // (again) in order for Stop/Start to be called (again) should more work | 
 |   // come in. Holding the |thread_execution_state_lock_| ensures the necessary | 
 |   // happens-after with regard to this detach and future Thread API calls. | 
 |   DetachFromSequence(); | 
 |  | 
 |   // Set the thread_state variable so the thread will be restarted when new | 
 |   // work comes in. Remove the |thread_execution_state_task_runner_| to avoid | 
 |   // confusion. | 
 |   thread_execution_state_ = EXITING; | 
 |   thread_execution_state_task_runner_ = nullptr; | 
 |   stack_buffer_.reset(); | 
 | } | 
 |  | 
 | bool StackSamplingProfiler::SamplingThread::UpdateNextSampleTime( | 
 |     CollectionContext* collection) { | 
 |   // This will keep a consistent average interval between samples but will | 
 |   // result in constant series of acquisitions, thus nearly locking out the | 
 |   // target thread, if the interval is smaller than the time it takes to | 
 |   // actually acquire the sample. Anything sampling that quickly is going | 
 |   // to be a problem anyway so don't worry about it. | 
 |   if (++collection->sample < collection->params.samples_per_burst) { | 
 |     collection->next_sample_time += collection->params.sampling_interval; | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (++collection->burst < collection->params.bursts) { | 
 |     collection->sample = 0; | 
 |     collection->next_sample_time += collection->params.burst_interval; | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | void StackSamplingProfiler::SamplingThread::CleanUp() { | 
 |   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
 |  | 
 |   // There should be no collections remaining when the thread stops. | 
 |   DCHECK(active_collections_.empty()); | 
 |  | 
 |   // Let the parent clean up. | 
 |   Thread::CleanUp(); | 
 | } | 
 |  | 
 | // StackSamplingProfiler ------------------------------------------------------ | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::TestAPI::Reset() { | 
 |   SamplingThread::TestAPI::Reset(); | 
 |   ResetAnnotations(); | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::TestAPI::ResetAnnotations() { | 
 |   subtle::NoBarrier_Store(&process_milestones_, 0u); | 
 | } | 
 |  | 
 | // static | 
 | bool StackSamplingProfiler::TestAPI::IsSamplingThreadRunning() { | 
 |   return SamplingThread::GetInstance()->IsRunning(); | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::TestAPI::DisableIdleShutdown() { | 
 |   SamplingThread::TestAPI::DisableIdleShutdown(); | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown( | 
 |     bool simulate_intervening_start) { | 
 |   SamplingThread::TestAPI::ShutdownAssumingIdle(simulate_intervening_start); | 
 | } | 
 |  | 
 | subtle::Atomic32 StackSamplingProfiler::process_milestones_ = 0; | 
 |  | 
 | StackSamplingProfiler::StackSamplingProfiler( | 
 |     const SamplingParams& params, | 
 |     const CompletedCallback& callback, | 
 |     NativeStackSamplerTestDelegate* test_delegate) | 
 |     : StackSamplingProfiler(base::PlatformThread::CurrentId(), | 
 |                             params, | 
 |                             callback, | 
 |                             test_delegate) {} | 
 |  | 
 | StackSamplingProfiler::StackSamplingProfiler( | 
 |     PlatformThreadId thread_id, | 
 |     const SamplingParams& params, | 
 |     const CompletedCallback& callback, | 
 |     NativeStackSamplerTestDelegate* test_delegate) | 
 |     : thread_id_(thread_id), | 
 |       params_(params), | 
 |       completed_callback_(callback), | 
 |       // The event starts "signaled" so code knows it's safe to start thread | 
 |       // and "manual" so that it can be waited in multiple places. | 
 |       profiling_inactive_(kResetPolicy, WaitableEvent::InitialState::SIGNALED), | 
 |       profiler_id_(NULL_PROFILER_ID), | 
 |       test_delegate_(test_delegate) {} | 
 |  | 
 | StackSamplingProfiler::~StackSamplingProfiler() { | 
 |   // Stop returns immediately but the shutdown runs asynchronously. There is a | 
 |   // non-zero probability that one more sample will be taken after this call | 
 |   // returns. | 
 |   Stop(); | 
 |  | 
 |   // The behavior of sampling a thread that has exited is undefined and could | 
 |   // cause Bad Things(tm) to occur. The safety model provided by this class is | 
 |   // that an instance of this object is expected to live at least as long as | 
 |   // the thread it is sampling. However, because the sampling is performed | 
 |   // asynchronously by the SamplingThread, there is no way to guarantee this | 
 |   // is true without waiting for it to signal that it has finished. | 
 |   // | 
 |   // The wait time should, at most, be only as long as it takes to collect one | 
 |   // sample (~200us) or none at all if sampling has already completed. | 
 |   ThreadRestrictions::ScopedAllowWait allow_wait; | 
 |   profiling_inactive_.Wait(); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::Start() { | 
 |   if (completed_callback_.is_null()) | 
 |     return; | 
 |  | 
 |   std::unique_ptr<NativeStackSampler> native_sampler = | 
 |       NativeStackSampler::Create(thread_id_, &RecordAnnotations, | 
 |                                  test_delegate_); | 
 |  | 
 |   if (!native_sampler) | 
 |     return; | 
 |  | 
 |   // The IsSignaled() check below requires that the WaitableEvent be manually | 
 |   // reset, to avoid signaling the event in IsSignaled() itself. | 
 |   static_assert(kResetPolicy == WaitableEvent::ResetPolicy::MANUAL, | 
 |                 "The reset policy must be set to MANUAL"); | 
 |  | 
 |   // If a previous profiling phase is still winding down, wait for it to | 
 |   // complete. We can't use task posting for this coordination because the | 
 |   // thread owning the profiler may not have a message loop. | 
 |   if (!profiling_inactive_.IsSignaled()) | 
 |     profiling_inactive_.Wait(); | 
 |   profiling_inactive_.Reset(); | 
 |  | 
 |   DCHECK_EQ(NULL_PROFILER_ID, profiler_id_); | 
 |   profiler_id_ = SamplingThread::GetInstance()->Add( | 
 |       std::make_unique<SamplingThread::CollectionContext>( | 
 |           SamplingThread::CollectionContext::next_profiler_id.GetNext(), | 
 |           thread_id_, params_, completed_callback_, &profiling_inactive_, | 
 |           std::move(native_sampler))); | 
 |   DCHECK_NE(NULL_PROFILER_ID, profiler_id_); | 
 | } | 
 |  | 
 | void StackSamplingProfiler::Stop() { | 
 |   SamplingThread::GetInstance()->Remove(profiler_id_); | 
 |   profiler_id_ = NULL_PROFILER_ID; | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::SetProcessMilestone(int milestone) { | 
 |   DCHECK_LE(0, milestone); | 
 |   DCHECK_GT(static_cast<int>(sizeof(process_milestones_) * 8), milestone); | 
 |   DCHECK_EQ(0, subtle::NoBarrier_Load(&process_milestones_) & (1 << milestone)); | 
 |   ChangeAtomicFlags(&process_milestones_, 1 << milestone, 0); | 
 | } | 
 |  | 
 | // static | 
 | void StackSamplingProfiler::RecordAnnotations(Sample* sample) { | 
 |   // The code inside this method must not do anything that could acquire a | 
 |   // mutex, including allocating memory (which includes LOG messages) because | 
 |   // that mutex could be held by a stopped thread, thus resulting in deadlock. | 
 |   sample->process_milestones = subtle::NoBarrier_Load(&process_milestones_); | 
 | } | 
 |  | 
 | // StackSamplingProfiler::Frame global functions ------------------------------ | 
 |  | 
 | bool operator==(const StackSamplingProfiler::Module& a, | 
 |                 const StackSamplingProfiler::Module& b) { | 
 |   return a.base_address == b.base_address && a.id == b.id && | 
 |       a.filename == b.filename; | 
 | } | 
 |  | 
 | bool operator==(const StackSamplingProfiler::Sample& a, | 
 |                 const StackSamplingProfiler::Sample& b) { | 
 |   return a.process_milestones == b.process_milestones && a.frames == b.frames; | 
 | } | 
 |  | 
 | bool operator!=(const StackSamplingProfiler::Sample& a, | 
 |                 const StackSamplingProfiler::Sample& b) { | 
 |   return !(a == b); | 
 | } | 
 |  | 
 | bool operator<(const StackSamplingProfiler::Sample& a, | 
 |                const StackSamplingProfiler::Sample& b) { | 
 |   if (a.process_milestones < b.process_milestones) | 
 |     return true; | 
 |   if (a.process_milestones > b.process_milestones) | 
 |     return false; | 
 |  | 
 |   return a.frames < b.frames; | 
 | } | 
 |  | 
 | 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 |