| OLD | NEW | 
|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "base/profiler/stack_sampling_profiler.h" | 5 #include "base/profiler/stack_sampling_profiler.h" | 
| 6 | 6 | 
| 7 #include <algorithm> | 7 #include <algorithm> | 
|  | 8 #include <map> | 
| 8 #include <utility> | 9 #include <utility> | 
| 9 | 10 | 
|  | 11 #include "base/atomic_sequence_num.h" | 
|  | 12 #include "base/atomicops.h" | 
| 10 #include "base/bind.h" | 13 #include "base/bind.h" | 
| 11 #include "base/bind_helpers.h" | 14 #include "base/bind_helpers.h" | 
| 12 #include "base/callback.h" | 15 #include "base/callback.h" | 
| 13 #include "base/lazy_instance.h" | 16 #include "base/lazy_instance.h" | 
| 14 #include "base/location.h" | 17 #include "base/location.h" | 
| 15 #include "base/macros.h" | 18 #include "base/macros.h" | 
|  | 19 #include "base/memory/ptr_util.h" | 
|  | 20 #include "base/memory/singleton.h" | 
| 16 #include "base/profiler/native_stack_sampler.h" | 21 #include "base/profiler/native_stack_sampler.h" | 
| 17 #include "base/synchronization/lock.h" | 22 #include "base/synchronization/lock.h" | 
|  | 23 #include "base/threading/thread.h" | 
|  | 24 #include "base/threading/thread_restrictions.h" | 
| 18 #include "base/threading/thread_task_runner_handle.h" | 25 #include "base/threading/thread_task_runner_handle.h" | 
| 19 #include "base/timer/elapsed_timer.h" | 26 #include "base/timer/elapsed_timer.h" | 
| 20 | 27 | 
| 21 namespace base { | 28 namespace base { | 
| 22 | 29 | 
| 23 namespace { | 30 namespace { | 
| 24 | 31 | 
| 25 // Used to ensure only one profiler is running at a time. | 32 // This value is used when there is no collection in progress and thus no ID | 
| 26 LazyInstance<Lock>::Leaky concurrent_profiling_lock = LAZY_INSTANCE_INITIALIZER; | 33 // for referencing the active collection to the SamplingThread. | 
| 27 | 34 const int NULL_COLLECTION_ID = -1; | 
| 28 // AsyncRunner ---------------------------------------------------------------- |  | 
| 29 |  | 
| 30 // Helper class to allow a profiler to be run completely asynchronously from the |  | 
| 31 // initiator, without being concerned with the profiler's lifetime. |  | 
| 32 class AsyncRunner { |  | 
| 33  public: |  | 
| 34   // Sets up a profiler and arranges for it to be deleted on its completed |  | 
| 35   // callback. |  | 
| 36   static void Run(PlatformThreadId thread_id, |  | 
| 37                   const StackSamplingProfiler::SamplingParams& params, |  | 
| 38                   const StackSamplingProfiler::CompletedCallback& callback); |  | 
| 39 |  | 
| 40  private: |  | 
| 41   AsyncRunner(); |  | 
| 42 |  | 
| 43   // Runs the callback and deletes the AsyncRunner instance. |profiles| is not |  | 
| 44   // const& because it must be passed with std::move. |  | 
| 45   static void RunCallbackAndDeleteInstance( |  | 
| 46       std::unique_ptr<AsyncRunner> object_to_be_deleted, |  | 
| 47       const StackSamplingProfiler::CompletedCallback& callback, |  | 
| 48       scoped_refptr<SingleThreadTaskRunner> task_runner, |  | 
| 49       StackSamplingProfiler::CallStackProfiles profiles); |  | 
| 50 |  | 
| 51   std::unique_ptr<StackSamplingProfiler> profiler_; |  | 
| 52 |  | 
| 53   DISALLOW_COPY_AND_ASSIGN(AsyncRunner); |  | 
| 54 }; |  | 
| 55 |  | 
| 56 // static |  | 
| 57 void AsyncRunner::Run( |  | 
| 58     PlatformThreadId thread_id, |  | 
| 59     const StackSamplingProfiler::SamplingParams& params, |  | 
| 60     const StackSamplingProfiler::CompletedCallback &callback) { |  | 
| 61   std::unique_ptr<AsyncRunner> runner(new AsyncRunner); |  | 
| 62   AsyncRunner* temp_ptr = runner.get(); |  | 
| 63   temp_ptr->profiler_.reset( |  | 
| 64       new StackSamplingProfiler(thread_id, params, |  | 
| 65                                 Bind(&AsyncRunner::RunCallbackAndDeleteInstance, |  | 
| 66                                      Passed(&runner), callback, |  | 
| 67                                      ThreadTaskRunnerHandle::Get()))); |  | 
| 68   // The callback won't be called until after Start(), so temp_ptr will still |  | 
| 69   // be valid here. |  | 
| 70   temp_ptr->profiler_->Start(); |  | 
| 71 } |  | 
| 72 |  | 
| 73 AsyncRunner::AsyncRunner() {} |  | 
| 74 |  | 
| 75 void AsyncRunner::RunCallbackAndDeleteInstance( |  | 
| 76     std::unique_ptr<AsyncRunner> object_to_be_deleted, |  | 
| 77     const StackSamplingProfiler::CompletedCallback& callback, |  | 
| 78     scoped_refptr<SingleThreadTaskRunner> task_runner, |  | 
| 79     StackSamplingProfiler::CallStackProfiles profiles) { |  | 
| 80   callback.Run(std::move(profiles)); |  | 
| 81   // Delete the instance on the original calling thread. |  | 
| 82   task_runner->DeleteSoon(FROM_HERE, object_to_be_deleted.release()); |  | 
| 83 } |  | 
| 84 | 35 | 
| 85 void ChangeAtomicFlags(subtle::Atomic32* flags, | 36 void ChangeAtomicFlags(subtle::Atomic32* flags, | 
| 86                        subtle::Atomic32 set, | 37                        subtle::Atomic32 set, | 
| 87                        subtle::Atomic32 clear) { | 38                        subtle::Atomic32 clear) { | 
| 88   DCHECK(set != 0 || clear != 0); | 39   DCHECK(set != 0 || clear != 0); | 
| 89   DCHECK_EQ(0, set & clear); | 40   DCHECK_EQ(0, set & clear); | 
| 90 | 41 | 
| 91   subtle::Atomic32 bits = subtle::NoBarrier_Load(flags); | 42   subtle::Atomic32 bits = subtle::NoBarrier_Load(flags); | 
| 92   while (true) { | 43   while (true) { | 
| 93     subtle::Atomic32 existing = | 44     subtle::Atomic32 existing = | 
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 153 StackSamplingProfiler::CallStackProfile | 104 StackSamplingProfiler::CallStackProfile | 
| 154 StackSamplingProfiler::CallStackProfile::CopyForTesting() const { | 105 StackSamplingProfiler::CallStackProfile::CopyForTesting() const { | 
| 155   return CallStackProfile(*this); | 106   return CallStackProfile(*this); | 
| 156 } | 107 } | 
| 157 | 108 | 
| 158 StackSamplingProfiler::CallStackProfile::CallStackProfile( | 109 StackSamplingProfiler::CallStackProfile::CallStackProfile( | 
| 159     const CallStackProfile& other) = default; | 110     const CallStackProfile& other) = default; | 
| 160 | 111 | 
| 161 // StackSamplingProfiler::SamplingThread -------------------------------------- | 112 // StackSamplingProfiler::SamplingThread -------------------------------------- | 
| 162 | 113 | 
| 163 StackSamplingProfiler::SamplingThread::SamplingThread( | 114 class StackSamplingProfiler::SamplingThread : public Thread { | 
| 164     std::unique_ptr<NativeStackSampler> native_sampler, | 115  public: | 
|  | 116   class TestAPI { | 
|  | 117    public: | 
|  | 118     // Reset the existing sampler. This will unfortunately create the object | 
|  | 119     // unnecessarily if it doesn't already exist but there's no way around that. | 
|  | 120     static void Reset(); | 
|  | 121 | 
|  | 122     // Disables inherent idle-shutdown behavior. | 
|  | 123     static void DisableIdleShutdown(); | 
|  | 124 | 
|  | 125     // Begins an idle shutdown as if the idle-timer had expired and wait for | 
|  | 126     // it to execute. Since the timer would have only been started at a time | 
|  | 127     // when the sampling thread actually was idle, this must be called only when | 
|  | 128     // it is known that there are no active sampling threads. If |simulate_add| | 
|  | 129     // is true then, when executed, the shutdown task will believe that a new | 
|  | 130     // collection has been added since it was posted. | 
|  | 131     static void ShutdownAssumingIdle(bool simulate_add); | 
|  | 132 | 
|  | 133    private: | 
|  | 134     // Calls the sampling threads ShutdownTask and then signals an event. | 
|  | 135     static void ShutdownTaskAndSignalEvent(SamplingThread* sampler, | 
|  | 136                                            int add_events, | 
|  | 137                                            WaitableEvent* event); | 
|  | 138   }; | 
|  | 139 | 
|  | 140   struct CollectionContext { | 
|  | 141     CollectionContext(PlatformThreadId target, | 
|  | 142                       const SamplingParams& params, | 
|  | 143                       const CompletedCallback& callback, | 
|  | 144                       WaitableEvent* finished, | 
|  | 145                       std::unique_ptr<NativeStackSampler> sampler) | 
|  | 146         : collection_id(next_collection_id_.GetNext()), | 
|  | 147           target(target), | 
|  | 148           params(params), | 
|  | 149           callback(callback), | 
|  | 150           finished(finished), | 
|  | 151           native_sampler(std::move(sampler)) {} | 
|  | 152     ~CollectionContext() {} | 
|  | 153 | 
|  | 154     // An identifier for this collection, used to uniquely identify it to | 
|  | 155     // outside interests. | 
|  | 156     const int collection_id; | 
|  | 157 | 
|  | 158     const PlatformThreadId target;     // ID of The thread being sampled. | 
|  | 159     const SamplingParams params;       // Information about how to sample. | 
|  | 160     const CompletedCallback callback;  // Callback made when sampling complete. | 
|  | 161     WaitableEvent* const finished;     // Signaled when all sampling complete. | 
|  | 162 | 
|  | 163     // Platform-specific module that does the actual sampling. | 
|  | 164     std::unique_ptr<NativeStackSampler> native_sampler; | 
|  | 165 | 
|  | 166     // The absolute time for the next sample. | 
|  | 167     Time next_sample_time; | 
|  | 168 | 
|  | 169     // The time that a profile was started, for calculating the total duration. | 
|  | 170     Time profile_start_time; | 
|  | 171 | 
|  | 172     // Counters that indicate the current position along the acquisition. | 
|  | 173     int burst = 0; | 
|  | 174     int sample = 0; | 
|  | 175 | 
|  | 176     // The collected stack samples. The active profile is always at the back(). | 
|  | 177     CallStackProfiles profiles; | 
|  | 178 | 
|  | 179    private: | 
|  | 180     static StaticAtomicSequenceNumber next_collection_id_; | 
|  | 181   }; | 
|  | 182 | 
|  | 183   // Gets the single instance of this class. | 
|  | 184   static SamplingThread* GetInstance(); | 
|  | 185 | 
|  | 186   // Starts the thread. | 
|  | 187   void Start(); | 
|  | 188 | 
|  | 189   // Adds a new CollectionContext to the thread. This can be called externally | 
|  | 190   // from any thread. This returns an ID that can later be used to stop | 
|  | 191   // the sampling. | 
|  | 192   int Add(std::unique_ptr<CollectionContext> collection); | 
|  | 193 | 
|  | 194   // Removes an active collection based on its ID, forcing it to run its | 
|  | 195   // callback if any data has been collected. This can be called externally | 
|  | 196   // from any thread. | 
|  | 197   void Remove(int id); | 
|  | 198 | 
|  | 199  private: | 
|  | 200   friend class TestAPI; | 
|  | 201   friend struct DefaultSingletonTraits<SamplingThread>; | 
|  | 202 | 
|  | 203   // The different states in which the sampling-thread can be. | 
|  | 204   enum ThreadExecutionState { | 
|  | 205     // The thread is not running because it has never been started. It will be | 
|  | 206     // started when a sampling request is received. | 
|  | 207     NOT_STARTED, | 
|  | 208 | 
|  | 209     // The thread is running and processing tasks. This is the state when any | 
|  | 210     // sampling requests are active and during the "idle" period afterward | 
|  | 211     // before the thread is stopped. | 
|  | 212     RUNNING, | 
|  | 213 | 
|  | 214     // Once all sampling requests have finished and the "idle" period has | 
|  | 215     // expired, the thread will be set to this state and its shutdown | 
|  | 216     // initiated. A call to Stop() must be made to ensure the previous thread | 
|  | 217     // has completely exited before calling Start() and moving back to the | 
|  | 218     // RUNNING state. | 
|  | 219     EXITING, | 
|  | 220   }; | 
|  | 221 | 
|  | 222   SamplingThread(); | 
|  | 223   ~SamplingThread() override; | 
|  | 224 | 
|  | 225   // Get task runner that is usable from the outside. | 
|  | 226   scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd(); | 
|  | 227   scoped_refptr<SingleThreadTaskRunner> GetTaskRunner( | 
|  | 228       ThreadExecutionState* out_state); | 
|  | 229 | 
|  | 230   // Get task runner that is usable from the sampling thread itself. | 
|  | 231   scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread(); | 
|  | 232 | 
|  | 233   // Finishes a collection and reports collected data via callback. | 
|  | 234   void FinishCollection(CollectionContext* collection); | 
|  | 235 | 
|  | 236   // Records a single sample of a collection. | 
|  | 237   void RecordSample(CollectionContext* collection); | 
|  | 238 | 
|  | 239   // Check if the sampling thread is idle and begin a shutdown if so. | 
|  | 240   void ScheduleShutdownIfIdle(); | 
|  | 241 | 
|  | 242   // These methods are tasks that get posted to the internal message queue. | 
|  | 243   void AddCollectionTask(std::unique_ptr<CollectionContext> collection); | 
|  | 244   void RemoveCollectionTask(int id); | 
|  | 245   void PerformCollectionTask(int id); | 
|  | 246   void ShutdownTask(int add_events); | 
|  | 247 | 
|  | 248   // Updates the |next_sample_time| time based on configured parameters. | 
|  | 249   bool UpdateNextSampleTime(CollectionContext* collection); | 
|  | 250 | 
|  | 251   // Thread: | 
|  | 252   void CleanUp() override; | 
|  | 253 | 
|  | 254   // The task-runner for the sampling thread and some information about it. | 
|  | 255   // This must always be accessed while holding the lock. The saved task-runner | 
|  | 256   // can be freely used by any calling thread. This lock is also used to | 
|  | 257   // order calls to the Thread API (Start, Stop, StopSoon, & DetachFromSequence) | 
|  | 258   // so that multiple threads may make those calls. | 
|  | 259   Lock thread_execution_state_lock_;  // Protects all thread_execution_state_* | 
|  | 260   ThreadExecutionState thread_execution_state_ = NOT_STARTED; | 
|  | 261   scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_; | 
|  | 262   bool thread_execution_state_disable_idle_shutdown_for_testing_ = false; | 
|  | 263 | 
|  | 264   // A counter that notes adds of new collection requests. It is incremented | 
|  | 265   // when changes occur so that delayed shutdown tasks are able to detect if | 
|  | 266   // samething new has happened while it was waiting. Like all "execution_state" | 
|  | 267   // vars, this must be accessed while holding |thread_execution_state_lock_|. | 
|  | 268   int thread_execution_state_add_events_ = 0; | 
|  | 269 | 
|  | 270   // A map of IDs to collection contexts. Because this class is a singleton | 
|  | 271   // that is never destroyed, context objects will never be destructed except | 
|  | 272   // by explicit action. Thus, it's acceptable to pass unretained pointers | 
|  | 273   // to these objects when posting tasks. | 
|  | 274   std::map<int, std::unique_ptr<CollectionContext>> active_collections_; | 
|  | 275 | 
|  | 276   DISALLOW_COPY_AND_ASSIGN(SamplingThread); | 
|  | 277 }; | 
|  | 278 | 
|  | 279 // static | 
|  | 280 void StackSamplingProfiler::SamplingThread::TestAPI::Reset() { | 
|  | 281   SamplingThread* sampler = SamplingThread::GetInstance(); | 
|  | 282 | 
|  | 283   ThreadExecutionState state; | 
|  | 284   { | 
|  | 285     AutoLock lock(sampler->thread_execution_state_lock_); | 
|  | 286     state = sampler->thread_execution_state_; | 
|  | 287     DCHECK(sampler->active_collections_.empty()); | 
|  | 288   } | 
|  | 289 | 
|  | 290   // Stop the thread and wait for it to exit. This has to be done through by | 
|  | 291   // the thread itself because it has taken ownership of its own lifetime. | 
|  | 292   if (state == RUNNING) { | 
|  | 293     ShutdownAssumingIdle(false); | 
|  | 294     state = EXITING; | 
|  | 295   } | 
|  | 296   // Make sure thread is cleaned up since state will be reset to NOT_STARTED. | 
|  | 297   if (state == EXITING) | 
|  | 298     sampler->Stop(); | 
|  | 299 | 
|  | 300   // Reset internal variables to the just-initialized state. | 
|  | 301   { | 
|  | 302     AutoLock lock(sampler->thread_execution_state_lock_); | 
|  | 303     sampler->thread_execution_state_ = NOT_STARTED; | 
|  | 304     sampler->thread_execution_state_task_runner_ = nullptr; | 
|  | 305     sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = false; | 
|  | 306     sampler->thread_execution_state_add_events_ = 0; | 
|  | 307   } | 
|  | 308 } | 
|  | 309 | 
|  | 310 // static | 
|  | 311 void StackSamplingProfiler::SamplingThread::TestAPI::DisableIdleShutdown() { | 
|  | 312   SamplingThread* sampler = SamplingThread::GetInstance(); | 
|  | 313 | 
|  | 314   { | 
|  | 315     AutoLock lock(sampler->thread_execution_state_lock_); | 
|  | 316     sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = true; | 
|  | 317   } | 
|  | 318 } | 
|  | 319 | 
|  | 320 // static | 
|  | 321 void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownAssumingIdle( | 
|  | 322     bool simulate_add) { | 
|  | 323   SamplingThread* sampler = SamplingThread::GetInstance(); | 
|  | 324 | 
|  | 325   ThreadExecutionState state; | 
|  | 326   scoped_refptr<SingleThreadTaskRunner> task_runner = | 
|  | 327       sampler->GetTaskRunner(&state); | 
|  | 328   DCHECK_EQ(RUNNING, state); | 
|  | 329   DCHECK(task_runner); | 
|  | 330 | 
|  | 331   int add_events; | 
|  | 332   { | 
|  | 333     AutoLock lock(sampler->thread_execution_state_lock_); | 
|  | 334     add_events = sampler->thread_execution_state_add_events_; | 
|  | 335     if (simulate_add) | 
|  | 336       ++sampler->thread_execution_state_add_events_; | 
|  | 337   } | 
|  | 338 | 
|  | 339   WaitableEvent executed(WaitableEvent::ResetPolicy::MANUAL, | 
|  | 340                          WaitableEvent::InitialState::NOT_SIGNALED); | 
|  | 341   // PostTaskAndReply won't work because thread and associated message-loop may | 
|  | 342   // be shut down. | 
|  | 343   task_runner->PostTask(FROM_HERE, | 
|  | 344                         Bind(&ShutdownTaskAndSignalEvent, Unretained(sampler), | 
|  | 345                              add_events, Unretained(&executed))); | 
|  | 346   executed.Wait(); | 
|  | 347 } | 
|  | 348 | 
|  | 349 // static | 
|  | 350 void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownTaskAndSignalEvent( | 
|  | 351     SamplingThread* sampler, | 
|  | 352     int add_events, | 
|  | 353     WaitableEvent* event) { | 
|  | 354   sampler->ShutdownTask(add_events); | 
|  | 355   event->Signal(); | 
|  | 356 } | 
|  | 357 | 
|  | 358 StaticAtomicSequenceNumber StackSamplingProfiler::SamplingThread:: | 
|  | 359     CollectionContext::next_collection_id_; | 
|  | 360 | 
|  | 361 StackSamplingProfiler::SamplingThread::SamplingThread() | 
|  | 362     : Thread("Chrome_SamplingProfilerThread") {} | 
|  | 363 | 
|  | 364 StackSamplingProfiler::SamplingThread::~SamplingThread() { | 
|  | 365   Stop(); | 
|  | 366 } | 
|  | 367 | 
|  | 368 StackSamplingProfiler::SamplingThread* | 
|  | 369 StackSamplingProfiler::SamplingThread::GetInstance() { | 
|  | 370   return Singleton<SamplingThread, LeakySingletonTraits<SamplingThread>>::get(); | 
|  | 371 } | 
|  | 372 | 
|  | 373 void StackSamplingProfiler::SamplingThread::Start() { | 
|  | 374   Thread::Options options; | 
|  | 375   // Use a higher priority for a more accurate sampling interval. | 
|  | 376   options.priority = ThreadPriority::DISPLAY; | 
|  | 377   Thread::StartWithOptions(options); | 
|  | 378 } | 
|  | 379 | 
|  | 380 int StackSamplingProfiler::SamplingThread::Add( | 
|  | 381     std::unique_ptr<CollectionContext> collection) { | 
|  | 382   int id = collection->collection_id; | 
|  | 383   scoped_refptr<SingleThreadTaskRunner> task_runner = | 
|  | 384       GetOrCreateTaskRunnerForAdd(); | 
|  | 385 | 
|  | 386   task_runner->PostTask(FROM_HERE, Bind(&SamplingThread::AddCollectionTask, | 
|  | 387                                         Unretained(this), Passed(&collection))); | 
|  | 388 | 
|  | 389   return id; | 
|  | 390 } | 
|  | 391 | 
|  | 392 void StackSamplingProfiler::SamplingThread::Remove(int id) { | 
|  | 393   ThreadExecutionState state; | 
|  | 394   scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state); | 
|  | 395   if (state != RUNNING) | 
|  | 396     return; | 
|  | 397   DCHECK(task_runner); | 
|  | 398 | 
|  | 399   // This can fail if the thread were to exit between acquisition of the task | 
|  | 400   // runner above and the call below. In that case, however, everything has | 
|  | 401   // stopped so there's no need to try to stop it. | 
|  | 402   task_runner->PostTask(FROM_HERE, Bind(&SamplingThread::RemoveCollectionTask, | 
|  | 403                                         Unretained(this), id)); | 
|  | 404 } | 
|  | 405 | 
|  | 406 scoped_refptr<SingleThreadTaskRunner> | 
|  | 407 StackSamplingProfiler::SamplingThread::GetOrCreateTaskRunnerForAdd() { | 
|  | 408   AutoLock lock(thread_execution_state_lock_); | 
|  | 409 | 
|  | 410   // The increment of the "add events" count is why this method is to be only | 
|  | 411   // called from "add". | 
|  | 412   ++thread_execution_state_add_events_; | 
|  | 413 | 
|  | 414   if (thread_execution_state_ == RUNNING) { | 
|  | 415     DCHECK(thread_execution_state_task_runner_); | 
|  | 416     // This shouldn't be called from the sampling thread as it's inefficient. | 
|  | 417     // Use GetTaskRunnerOnSamplingThread() instead. | 
|  | 418     DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); | 
|  | 419     return thread_execution_state_task_runner_; | 
|  | 420   } | 
|  | 421 | 
|  | 422   if (thread_execution_state_ == EXITING) { | 
|  | 423     // The previous instance has only been partially cleaned up. It is necessary | 
|  | 424     // to call Stop() before Start(). | 
|  | 425     Stop(); | 
|  | 426   } | 
|  | 427 | 
|  | 428   // The thread is not running. Start it and get associated runner. The task- | 
|  | 429   // runner has to be saved for future use because though it can be used from | 
|  | 430   // any thread, it can be acquired via task_runner() only on the created | 
|  | 431   // thread and the thread that creates it (i.e. this thread). | 
|  | 432   Start(); | 
|  | 433   thread_execution_state_ = RUNNING; | 
|  | 434   thread_execution_state_task_runner_ = Thread::task_runner(); | 
|  | 435 | 
|  | 436   // Detach the sampling thread from the "sequence" (i.e. thread) that | 
|  | 437   // started it so that it can be self-managed or stopped by another thread. | 
|  | 438   DetachFromSequence(); | 
|  | 439 | 
|  | 440   return thread_execution_state_task_runner_; | 
|  | 441 } | 
|  | 442 | 
|  | 443 scoped_refptr<SingleThreadTaskRunner> | 
|  | 444 StackSamplingProfiler::SamplingThread::GetTaskRunner( | 
|  | 445     ThreadExecutionState* out_state) { | 
|  | 446   AutoLock lock(thread_execution_state_lock_); | 
|  | 447   if (out_state) | 
|  | 448     *out_state = thread_execution_state_; | 
|  | 449   if (thread_execution_state_ == RUNNING) { | 
|  | 450     // This shouldn't be called from the sampling thread as it's inefficient. | 
|  | 451     // Use GetTaskRunnerOnSamplingThread() instead. | 
|  | 452     DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); | 
|  | 453     DCHECK(thread_execution_state_task_runner_); | 
|  | 454   } else { | 
|  | 455     DCHECK(!thread_execution_state_task_runner_); | 
|  | 456   } | 
|  | 457 | 
|  | 458   return thread_execution_state_task_runner_; | 
|  | 459 } | 
|  | 460 | 
|  | 461 scoped_refptr<SingleThreadTaskRunner> | 
|  | 462 StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() { | 
|  | 463   // This should be called only from the sampling thread as it has limited | 
|  | 464   // accessibility. | 
|  | 465   DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 
|  | 466 | 
|  | 467   return Thread::task_runner(); | 
|  | 468 } | 
|  | 469 | 
|  | 470 void StackSamplingProfiler::SamplingThread::FinishCollection( | 
|  | 471     CollectionContext* collection) { | 
|  | 472   // If there is no duration for the final profile (because it was stopped), | 
|  | 473   // calculate it now. | 
|  | 474   if (!collection->profiles.empty() && | 
|  | 475       collection->profiles.back().profile_duration == TimeDelta()) { | 
|  | 476     collection->profiles.back().profile_duration = | 
|  | 477         Time::Now() - collection->profile_start_time; | 
|  | 478   } | 
|  | 479 | 
|  | 480   // Extract some information so callback and event-signalling can still be | 
|  | 481   // done after the collection has been removed from the list of "active" ones. | 
|  | 482   // This allows the the controlling object (and tests using it) to be confident | 
|  | 483   // that collection is fully finished when those things occur. | 
|  | 484   const CompletedCallback callback = collection->callback; | 
|  | 485   CallStackProfiles profiles = std::move(collection->profiles); | 
|  | 486   WaitableEvent* finished = collection->finished; | 
|  | 487 | 
|  | 488   // Remove this collection from the map of known ones.  The |collection| | 
|  | 489   // parameter is invalid after this point. | 
|  | 490   size_t count = active_collections_.erase(collection->collection_id); | 
|  | 491   DCHECK_EQ(1U, count); | 
|  | 492 | 
|  | 493   // Run the associated callback, passing the collected profiles. | 
|  | 494   callback.Run(std::move(profiles)); | 
|  | 495 | 
|  | 496   // Signal that this collection is finished. | 
|  | 497   finished->Signal(); | 
|  | 498 } | 
|  | 499 | 
|  | 500 void StackSamplingProfiler::SamplingThread::RecordSample( | 
|  | 501     CollectionContext* collection) { | 
|  | 502   DCHECK(collection->native_sampler); | 
|  | 503 | 
|  | 504   // If this is the first sample of a burst, a new Profile needs to be created | 
|  | 505   // and filled. | 
|  | 506   if (collection->sample == 0) { | 
|  | 507     collection->profiles.push_back(CallStackProfile()); | 
|  | 508     CallStackProfile& profile = collection->profiles.back(); | 
|  | 509     profile.sampling_period = collection->params.sampling_interval; | 
|  | 510     collection->profile_start_time = Time::Now(); | 
|  | 511     collection->native_sampler->ProfileRecordingStarting(&profile.modules); | 
|  | 512   } | 
|  | 513 | 
|  | 514   // The currently active profile being captured. | 
|  | 515   CallStackProfile& profile = collection->profiles.back(); | 
|  | 516 | 
|  | 517   // Record a single sample. | 
|  | 518   profile.samples.push_back(Sample()); | 
|  | 519   collection->native_sampler->RecordStackSample(&profile.samples.back()); | 
|  | 520 | 
|  | 521   // If this is the last sample of a burst, record the total time. | 
|  | 522   if (collection->sample == collection->params.samples_per_burst - 1) { | 
|  | 523     profile.profile_duration = Time::Now() - collection->profile_start_time; | 
|  | 524     collection->native_sampler->ProfileRecordingStopped(); | 
|  | 525   } | 
|  | 526 } | 
|  | 527 | 
|  | 528 void StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle() { | 
|  | 529   if (!active_collections_.empty()) | 
|  | 530     return; | 
|  | 531 | 
|  | 532   int add_events; | 
|  | 533   { | 
|  | 534     AutoLock lock(thread_execution_state_lock_); | 
|  | 535     if (thread_execution_state_disable_idle_shutdown_for_testing_) | 
|  | 536       return; | 
|  | 537     add_events = thread_execution_state_add_events_; | 
|  | 538   } | 
|  | 539 | 
|  | 540   GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 
|  | 541       FROM_HERE, | 
|  | 542       Bind(&SamplingThread::ShutdownTask, Unretained(this), add_events), | 
|  | 543       TimeDelta::FromSeconds(60)); | 
|  | 544 } | 
|  | 545 | 
|  | 546 void StackSamplingProfiler::SamplingThread::AddCollectionTask( | 
|  | 547     std::unique_ptr<CollectionContext> collection) { | 
|  | 548   const int collection_id = collection->collection_id; | 
|  | 549   const TimeDelta initial_delay = collection->params.initial_delay; | 
|  | 550 | 
|  | 551   active_collections_.insert( | 
|  | 552       std::make_pair(collection_id, std::move(collection))); | 
|  | 553 | 
|  | 554   GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 
|  | 555       FROM_HERE, | 
|  | 556       Bind(&SamplingThread::PerformCollectionTask, Unretained(this), | 
|  | 557            collection_id), | 
|  | 558       initial_delay); | 
|  | 559 | 
|  | 560   // Another increment of "add events" serves to invalidate any pending | 
|  | 561   // shutdown tasks that may have been initiated between the Add() and this | 
|  | 562   // task running. | 
|  | 563   { | 
|  | 564     AutoLock lock(thread_execution_state_lock_); | 
|  | 565     ++thread_execution_state_add_events_; | 
|  | 566   } | 
|  | 567 } | 
|  | 568 | 
|  | 569 void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) { | 
|  | 570   auto found = active_collections_.find(id); | 
|  | 571   if (found == active_collections_.end()) | 
|  | 572     return; | 
|  | 573 | 
|  | 574   FinishCollection(found->second.get()); | 
|  | 575   ScheduleShutdownIfIdle(); | 
|  | 576 } | 
|  | 577 | 
|  | 578 void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { | 
|  | 579   auto found = active_collections_.find(id); | 
|  | 580 | 
|  | 581   // The task won't be found if it has been stopped. | 
|  | 582   if (found == active_collections_.end()) | 
|  | 583     return; | 
|  | 584 | 
|  | 585   CollectionContext* collection = found->second.get(); | 
|  | 586 | 
|  | 587   // Handle first-run with no "next time". | 
|  | 588   if (collection->next_sample_time == Time()) | 
|  | 589     collection->next_sample_time = Time::Now(); | 
|  | 590 | 
|  | 591   // Do the collection of a single sample. | 
|  | 592   RecordSample(collection); | 
|  | 593 | 
|  | 594   // Update the time of the next sample recording. | 
|  | 595   if (UpdateNextSampleTime(collection)) { | 
|  | 596     bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 
|  | 597         FROM_HERE, | 
|  | 598         Bind(&SamplingThread::PerformCollectionTask, Unretained(this), id), | 
|  | 599         std::max(collection->next_sample_time - Time::Now(), TimeDelta())); | 
|  | 600     DCHECK(success); | 
|  | 601   } else { | 
|  | 602     // All capturing has completed so finish the collection. Let object expire. | 
|  | 603     // The |collection| variable will be invalid after this call. | 
|  | 604     FinishCollection(collection); | 
|  | 605     ScheduleShutdownIfIdle(); | 
|  | 606   } | 
|  | 607 } | 
|  | 608 | 
|  | 609 void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) { | 
|  | 610   // Holding this lock ensures that any attempt to start another job will | 
|  | 611   // get postponed until thread_execution_state_task_runner_ is cleared, thus | 
|  | 612   // eliminating the race. | 
|  | 613   AutoLock lock(thread_execution_state_lock_); | 
|  | 614 | 
|  | 615   // If the current count of creation requests doesn't match the passed count | 
|  | 616   // then other tasks have been created since this was posted. Abort shutdown. | 
|  | 617   if (thread_execution_state_add_events_ != add_events) | 
|  | 618     return; | 
|  | 619 | 
|  | 620   // There can be no new AddCollectionTasks at this point because creating | 
|  | 621   // those always increments "add events". There may be other requests, like | 
|  | 622   // Remove, but it's okay to schedule the thread to stop once they've been | 
|  | 623   // executed (i.e. "soon"). | 
|  | 624   DCHECK(active_collections_.empty()); | 
|  | 625   StopSoon(); | 
|  | 626 | 
|  | 627   // StopSoon will have set the owning sequence (again) so it must be detached | 
|  | 628   // (again) in order for Stop/Start to be called (again) should more work | 
|  | 629   // come in. Holding the |thread_execution_state_lock_| ensures the necessary | 
|  | 630   // happens-after with regard to this detach and future Thread API calls. | 
|  | 631   DetachFromSequence(); | 
|  | 632 | 
|  | 633   // Set the thread_state variable so the thread will be restarted when new | 
|  | 634   // work comes in. Remove the thread_execution_state_task_runner_ to avoid | 
|  | 635   // confusion. | 
|  | 636   thread_execution_state_ = EXITING; | 
|  | 637   thread_execution_state_task_runner_ = nullptr; | 
|  | 638 } | 
|  | 639 | 
|  | 640 bool StackSamplingProfiler::SamplingThread::UpdateNextSampleTime( | 
|  | 641     CollectionContext* collection) { | 
|  | 642   // This will keep a consistent average interval between samples but will | 
|  | 643   // result in constant series of acquisitions, thus nearly locking out the | 
|  | 644   // target thread, if the interval is smaller than the time it takes to | 
|  | 645   // actually acquire the sample. Anything sampling that quickly is going | 
|  | 646   // to be a problem anyway so don't worry about it. | 
|  | 647   if (++collection->sample < collection->params.samples_per_burst) { | 
|  | 648     collection->next_sample_time += collection->params.sampling_interval; | 
|  | 649     return true; | 
|  | 650   } | 
|  | 651 | 
|  | 652   if (++collection->burst < collection->params.bursts) { | 
|  | 653     collection->sample = 0; | 
|  | 654     collection->next_sample_time += collection->params.burst_interval; | 
|  | 655     return true; | 
|  | 656   } | 
|  | 657 | 
|  | 658   return false; | 
|  | 659 } | 
|  | 660 | 
|  | 661 void StackSamplingProfiler::SamplingThread::CleanUp() { | 
|  | 662   // There should be no collections remaining when the thread stops. | 
|  | 663   DCHECK(active_collections_.empty()); | 
|  | 664 | 
|  | 665   // Let the parent clean up. | 
|  | 666   Thread::CleanUp(); | 
|  | 667 } | 
|  | 668 | 
|  | 669 // StackSamplingProfiler ------------------------------------------------------ | 
|  | 670 | 
|  | 671 // static | 
|  | 672 void StackSamplingProfiler::TestAPI::Reset() { | 
|  | 673   SamplingThread::TestAPI::Reset(); | 
|  | 674 } | 
|  | 675 | 
|  | 676 // static | 
|  | 677 bool StackSamplingProfiler::TestAPI::IsSamplingThreadRunning() { | 
|  | 678   return SamplingThread::GetInstance()->IsRunning(); | 
|  | 679 } | 
|  | 680 | 
|  | 681 // static | 
|  | 682 void StackSamplingProfiler::TestAPI::DisableIdleShutdown() { | 
|  | 683   SamplingThread::TestAPI::DisableIdleShutdown(); | 
|  | 684 } | 
|  | 685 | 
|  | 686 // static | 
|  | 687 void StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown( | 
|  | 688     bool simulate_start) { | 
|  | 689   SamplingThread::TestAPI::ShutdownAssumingIdle(simulate_start); | 
|  | 690 } | 
|  | 691 | 
|  | 692 subtle::Atomic32 StackSamplingProfiler::process_milestones_ = 0; | 
|  | 693 | 
|  | 694 StackSamplingProfiler::StackSamplingProfiler( | 
| 165     const SamplingParams& params, | 695     const SamplingParams& params, | 
| 166     const CompletedCallback& completed_callback) | 696     const CompletedCallback& callback, | 
| 167     : native_sampler_(std::move(native_sampler)), | 697     NativeStackSamplerTestDelegate* test_delegate) | 
| 168       params_(params), | 698     : StackSamplingProfiler(base::PlatformThread::CurrentId(), | 
| 169       stop_event_(WaitableEvent::ResetPolicy::AUTOMATIC, | 699                             params, | 
| 170                   WaitableEvent::InitialState::NOT_SIGNALED), | 700                             callback, | 
| 171       completed_callback_(completed_callback) {} | 701                             test_delegate) {} | 
| 172 |  | 
| 173 StackSamplingProfiler::SamplingThread::~SamplingThread() {} |  | 
| 174 |  | 
| 175 void StackSamplingProfiler::SamplingThread::ThreadMain() { |  | 
| 176   PlatformThread::SetName("Chrome_SamplingProfilerThread"); |  | 
| 177 |  | 
| 178   // For now, just ignore any requests to profile while another profiler is |  | 
| 179   // working. |  | 
| 180   if (!concurrent_profiling_lock.Get().Try()) |  | 
| 181     return; |  | 
| 182 |  | 
| 183   CallStackProfiles profiles; |  | 
| 184   CollectProfiles(&profiles); |  | 
| 185   concurrent_profiling_lock.Get().Release(); |  | 
| 186   completed_callback_.Run(std::move(profiles)); |  | 
| 187 } |  | 
| 188 |  | 
| 189 // Depending on how long the sampling takes and the length of the sampling |  | 
| 190 // interval, a burst of samples could take arbitrarily longer than |  | 
| 191 // samples_per_burst * sampling_interval. In this case, we (somewhat |  | 
| 192 // arbitrarily) honor the number of samples requested rather than strictly |  | 
| 193 // adhering to the sampling intervals. Once we have established users for the |  | 
| 194 // StackSamplingProfiler and the collected data to judge, we may go the other |  | 
| 195 // way or make this behavior configurable. |  | 
| 196 void StackSamplingProfiler::SamplingThread::CollectProfile( |  | 
| 197     CallStackProfile* profile, |  | 
| 198     TimeDelta* elapsed_time, |  | 
| 199     bool* was_stopped) { |  | 
| 200   ElapsedTimer profile_timer; |  | 
| 201   native_sampler_->ProfileRecordingStarting(&profile->modules); |  | 
| 202   profile->sampling_period = params_.sampling_interval; |  | 
| 203   *was_stopped = false; |  | 
| 204   TimeDelta previous_elapsed_sample_time; |  | 
| 205   for (int i = 0; i < params_.samples_per_burst; ++i) { |  | 
| 206     if (i != 0) { |  | 
| 207       // Always wait, even if for 0 seconds, so we can observe a signal on |  | 
| 208       // stop_event_. |  | 
| 209       if (stop_event_.TimedWait( |  | 
| 210               std::max(params_.sampling_interval - previous_elapsed_sample_time, |  | 
| 211                        TimeDelta()))) { |  | 
| 212         *was_stopped = true; |  | 
| 213         break; |  | 
| 214       } |  | 
| 215     } |  | 
| 216     ElapsedTimer sample_timer; |  | 
| 217     profile->samples.push_back(Sample()); |  | 
| 218     native_sampler_->RecordStackSample(&profile->samples.back()); |  | 
| 219     previous_elapsed_sample_time = sample_timer.Elapsed(); |  | 
| 220   } |  | 
| 221 |  | 
| 222   *elapsed_time = profile_timer.Elapsed(); |  | 
| 223   profile->profile_duration = *elapsed_time; |  | 
| 224   native_sampler_->ProfileRecordingStopped(); |  | 
| 225 } |  | 
| 226 |  | 
| 227 // In an analogous manner to CollectProfile() and samples exceeding the expected |  | 
| 228 // total sampling time, bursts may also exceed the burst_interval. We adopt the |  | 
| 229 // same wait-and-see approach here. |  | 
| 230 void StackSamplingProfiler::SamplingThread::CollectProfiles( |  | 
| 231     CallStackProfiles* profiles) { |  | 
| 232   if (stop_event_.TimedWait(params_.initial_delay)) |  | 
| 233     return; |  | 
| 234 |  | 
| 235   TimeDelta previous_elapsed_profile_time; |  | 
| 236   for (int i = 0; i < params_.bursts; ++i) { |  | 
| 237     if (i != 0) { |  | 
| 238       // Always wait, even if for 0 seconds, so we can observe a signal on |  | 
| 239       // stop_event_. |  | 
| 240       if (stop_event_.TimedWait( |  | 
| 241               std::max(params_.burst_interval - previous_elapsed_profile_time, |  | 
| 242                        TimeDelta()))) |  | 
| 243         return; |  | 
| 244     } |  | 
| 245 |  | 
| 246     CallStackProfile profile; |  | 
| 247     bool was_stopped = false; |  | 
| 248     CollectProfile(&profile, &previous_elapsed_profile_time, &was_stopped); |  | 
| 249     if (!profile.samples.empty()) |  | 
| 250       profiles->push_back(std::move(profile)); |  | 
| 251 |  | 
| 252     if (was_stopped) |  | 
| 253       return; |  | 
| 254   } |  | 
| 255 } |  | 
| 256 |  | 
| 257 void StackSamplingProfiler::SamplingThread::Stop() { |  | 
| 258   stop_event_.Signal(); |  | 
| 259 } |  | 
| 260 |  | 
| 261 // StackSamplingProfiler ------------------------------------------------------ |  | 
| 262 |  | 
| 263 subtle::Atomic32 StackSamplingProfiler::process_milestones_ = 0; |  | 
| 264 |  | 
| 265 StackSamplingProfiler::StackSamplingProfiler( |  | 
| 266     PlatformThreadId thread_id, |  | 
| 267     const SamplingParams& params, |  | 
| 268     const CompletedCallback& callback) |  | 
| 269     : StackSamplingProfiler(thread_id, params, callback, nullptr) {} |  | 
| 270 | 702 | 
| 271 StackSamplingProfiler::StackSamplingProfiler( | 703 StackSamplingProfiler::StackSamplingProfiler( | 
| 272     PlatformThreadId thread_id, | 704     PlatformThreadId thread_id, | 
| 273     const SamplingParams& params, | 705     const SamplingParams& params, | 
| 274     const CompletedCallback& callback, | 706     const CompletedCallback& callback, | 
| 275     NativeStackSamplerTestDelegate* test_delegate) | 707     NativeStackSamplerTestDelegate* test_delegate) | 
| 276     : thread_id_(thread_id), params_(params), completed_callback_(callback), | 708     : thread_id_(thread_id), | 
| 277       test_delegate_(test_delegate) { | 709       params_(params), | 
| 278 } | 710       completed_callback_(callback), | 
|  | 711       // The event starts "signaled" so code knows it's safe to start thread. | 
|  | 712       profiling_inactive_(WaitableEvent::ResetPolicy::MANUAL, | 
|  | 713                           WaitableEvent::InitialState::SIGNALED), | 
|  | 714       collection_id_(NULL_COLLECTION_ID), | 
|  | 715       test_delegate_(test_delegate) {} | 
| 279 | 716 | 
| 280 StackSamplingProfiler::~StackSamplingProfiler() { | 717 StackSamplingProfiler::~StackSamplingProfiler() { | 
|  | 718   // Stop is immediate but asynchronous. There is a non-zero probability that | 
|  | 719   // one more sample will be taken after this call returns. | 
| 281   Stop(); | 720   Stop(); | 
| 282   if (!sampling_thread_handle_.is_null()) | 721 | 
| 283     PlatformThread::Join(sampling_thread_handle_); | 722   // The behavior of sampling a thread that has exited is undefined and could | 
| 284 } | 723   // cause Bad Things(tm) to occur. The safety model provided by this class is | 
| 285 | 724   // that an instance of this object is expected to live at least as long as | 
| 286 // static | 725   // the thread it is sampling. However, because the sampling is performed | 
| 287 void StackSamplingProfiler::StartAndRunAsync( | 726   // asynchronously by the SamplingThread, there is no way to guarantee this | 
| 288     PlatformThreadId thread_id, | 727   // is true without waiting for it to signal that it has finished. | 
| 289     const SamplingParams& params, | 728   // | 
| 290     const CompletedCallback& callback) { | 729   // The wait time should, at most, be only as long as it takes to collect one | 
| 291   CHECK(ThreadTaskRunnerHandle::Get()); | 730   // sample (~200us) or none at all if sampling has already completed. | 
| 292   AsyncRunner::Run(thread_id, params, callback); | 731   ThreadRestrictions::ScopedAllowWait allow_wait; | 
|  | 732   profiling_inactive_.Wait(); | 
| 293 } | 733 } | 
| 294 | 734 | 
| 295 void StackSamplingProfiler::Start() { | 735 void StackSamplingProfiler::Start() { | 
| 296   if (completed_callback_.is_null()) | 736   if (completed_callback_.is_null()) | 
| 297     return; | 737     return; | 
| 298 | 738 | 
| 299   std::unique_ptr<NativeStackSampler> native_sampler = | 739   std::unique_ptr<NativeStackSampler> native_sampler = | 
| 300       NativeStackSampler::Create(thread_id_, &RecordAnnotations, | 740       NativeStackSampler::Create(thread_id_, &RecordAnnotations, | 
| 301                                  test_delegate_); | 741                                  test_delegate_); | 
|  | 742 | 
| 302   if (!native_sampler) | 743   if (!native_sampler) | 
| 303     return; | 744     return; | 
| 304 | 745 | 
| 305   sampling_thread_.reset(new SamplingThread(std::move(native_sampler), params_, | 746   // Wait for profiling to be "inactive", then reset it for the upcoming run. | 
| 306                                             completed_callback_)); | 747   profiling_inactive_.Wait(); | 
| 307   if (!PlatformThread::Create(0, sampling_thread_.get(), | 748   profiling_inactive_.Reset(); | 
| 308                               &sampling_thread_handle_)) | 749 | 
| 309     sampling_thread_.reset(); | 750   DCHECK_EQ(NULL_COLLECTION_ID, collection_id_); | 
|  | 751   collection_id_ = SamplingThread::GetInstance()->Add( | 
|  | 752       MakeUnique<SamplingThread::CollectionContext>( | 
|  | 753           thread_id_, params_, completed_callback_, &profiling_inactive_, | 
|  | 754           std::move(native_sampler))); | 
|  | 755   DCHECK_NE(NULL_COLLECTION_ID, collection_id_); | 
| 310 } | 756 } | 
| 311 | 757 | 
| 312 void StackSamplingProfiler::Stop() { | 758 void StackSamplingProfiler::Stop() { | 
| 313   if (sampling_thread_) | 759   SamplingThread::GetInstance()->Remove(collection_id_); | 
| 314     sampling_thread_->Stop(); | 760   collection_id_ = NULL_COLLECTION_ID; | 
| 315 } | 761 } | 
| 316 | 762 | 
| 317 // static | 763 // static | 
| 318 void StackSamplingProfiler::SetProcessMilestone(int milestone) { | 764 void StackSamplingProfiler::SetProcessMilestone(int milestone) { | 
| 319   DCHECK_LE(0, milestone); | 765   DCHECK_LE(0, milestone); | 
| 320   DCHECK_GT(static_cast<int>(sizeof(process_milestones_) * 8), milestone); | 766   DCHECK_GT(static_cast<int>(sizeof(process_milestones_) * 8), milestone); | 
| 321   DCHECK_EQ(0, subtle::NoBarrier_Load(&process_milestones_) & (1 << milestone)); | 767   DCHECK_EQ(0, subtle::NoBarrier_Load(&process_milestones_) & (1 << milestone)); | 
| 322   ChangeAtomicFlags(&process_milestones_, 1 << milestone, 0); | 768   ChangeAtomicFlags(&process_milestones_, 1 << milestone, 0); | 
| 323 } | 769 } | 
| 324 | 770 | 
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 370 } | 816 } | 
| 371 | 817 | 
| 372 bool operator<(const StackSamplingProfiler::Frame &a, | 818 bool operator<(const StackSamplingProfiler::Frame &a, | 
| 373                const StackSamplingProfiler::Frame &b) { | 819                const StackSamplingProfiler::Frame &b) { | 
| 374   return (a.module_index < b.module_index) || | 820   return (a.module_index < b.module_index) || | 
| 375       (a.module_index == b.module_index && | 821       (a.module_index == b.module_index && | 
| 376        a.instruction_pointer < b.instruction_pointer); | 822        a.instruction_pointer < b.instruction_pointer); | 
| 377 } | 823 } | 
| 378 | 824 | 
| 379 }  // namespace base | 825 }  // namespace base | 
| OLD | NEW | 
|---|