| 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 <map> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 #include "base/profiler/native_stack_sampler.h" | 21 #include "base/profiler/native_stack_sampler.h" |
| 22 #include "base/synchronization/lock.h" | 22 #include "base/synchronization/lock.h" |
| 23 #include "base/threading/thread.h" | 23 #include "base/threading/thread.h" |
| 24 #include "base/threading/thread_task_runner_handle.h" | 24 #include "base/threading/thread_task_runner_handle.h" |
| 25 #include "base/timer/elapsed_timer.h" | 25 #include "base/timer/elapsed_timer.h" |
| 26 | 26 |
| 27 namespace base { | 27 namespace base { |
| 28 | 28 |
| 29 namespace { | 29 namespace { |
| 30 | 30 |
| 31 // AsyncRunner ---------------------------------------------------------------- | |
| 32 | |
| 33 // Helper class to allow a profiler to be run completely asynchronously from the | |
| 34 // initiator, without being concerned with the profiler's lifetime. | |
| 35 class AsyncRunner { | |
| 36 public: | |
| 37 // Sets up a profiler and arranges for it to be deleted on its completed | |
| 38 // callback. | |
| 39 static void Run(PlatformThreadId thread_id, | |
| 40 const StackSamplingProfiler::SamplingParams& params, | |
| 41 const StackSamplingProfiler::CompletedCallback& callback); | |
| 42 | |
| 43 private: | |
| 44 AsyncRunner(); | |
| 45 | |
| 46 // Runs the callback and deletes the AsyncRunner instance. |profiles| is not | |
| 47 // const& because it must be passed with std::move. | |
| 48 static void RunCallbackAndDeleteInstance( | |
| 49 std::unique_ptr<AsyncRunner> object_to_be_deleted, | |
| 50 const StackSamplingProfiler::CompletedCallback& callback, | |
| 51 scoped_refptr<SingleThreadTaskRunner> task_runner, | |
| 52 StackSamplingProfiler::CallStackProfiles profiles); | |
| 53 | |
| 54 std::unique_ptr<StackSamplingProfiler> profiler_; | |
| 55 | |
| 56 DISALLOW_COPY_AND_ASSIGN(AsyncRunner); | |
| 57 }; | |
| 58 | |
| 59 // static | |
| 60 void AsyncRunner::Run( | |
| 61 PlatformThreadId thread_id, | |
| 62 const StackSamplingProfiler::SamplingParams& params, | |
| 63 const StackSamplingProfiler::CompletedCallback &callback) { | |
| 64 std::unique_ptr<AsyncRunner> runner(new AsyncRunner); | |
| 65 AsyncRunner* temp_ptr = runner.get(); | |
| 66 temp_ptr->profiler_.reset( | |
| 67 new StackSamplingProfiler(thread_id, params, | |
| 68 Bind(&AsyncRunner::RunCallbackAndDeleteInstance, | |
| 69 Passed(&runner), callback, | |
| 70 ThreadTaskRunnerHandle::Get()))); | |
| 71 // The callback won't be called until after Start(), so temp_ptr will still | |
| 72 // be valid here. | |
| 73 temp_ptr->profiler_->Start(); | |
| 74 } | |
| 75 | |
| 76 AsyncRunner::AsyncRunner() {} | |
| 77 | |
| 78 void AsyncRunner::RunCallbackAndDeleteInstance( | |
| 79 std::unique_ptr<AsyncRunner> object_to_be_deleted, | |
| 80 const StackSamplingProfiler::CompletedCallback& callback, | |
| 81 scoped_refptr<SingleThreadTaskRunner> task_runner, | |
| 82 StackSamplingProfiler::CallStackProfiles profiles) { | |
| 83 callback.Run(std::move(profiles)); | |
| 84 // Delete the instance on the original calling thread. | |
| 85 task_runner->DeleteSoon(FROM_HERE, object_to_be_deleted.release()); | |
| 86 } | |
| 87 | |
| 88 void ChangeAtomicFlags(subtle::Atomic32* flags, | 31 void ChangeAtomicFlags(subtle::Atomic32* flags, |
| 89 subtle::Atomic32 set, | 32 subtle::Atomic32 set, |
| 90 subtle::Atomic32 clear) { | 33 subtle::Atomic32 clear) { |
| 91 DCHECK(set != 0 || clear != 0); | 34 DCHECK(set != 0 || clear != 0); |
| 92 DCHECK_EQ(0, set & clear); | 35 DCHECK_EQ(0, set & clear); |
| 93 | 36 |
| 94 subtle::Atomic32 bits = subtle::NoBarrier_Load(flags); | 37 subtle::Atomic32 bits = subtle::NoBarrier_Load(flags); |
| 95 while (true) { | 38 while (true) { |
| 96 subtle::Atomic32 existing = | 39 subtle::Atomic32 existing = |
| 97 subtle::NoBarrier_CompareAndSwap(flags, bits, (bits | set) & ~clear); | 40 subtle::NoBarrier_CompareAndSwap(flags, bits, (bits | set) & ~clear); |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 // Starts the thread. | 152 // Starts the thread. |
| 210 void Start(); | 153 void Start(); |
| 211 | 154 |
| 212 // Adds a new CollectionContext to the thread. This can be called externally | 155 // Adds a new CollectionContext to the thread. This can be called externally |
| 213 // from any thread. This returns an ID that can later be used to stop | 156 // from any thread. This returns an ID that can later be used to stop |
| 214 // the sampling. | 157 // the sampling. |
| 215 int Add(std::unique_ptr<CollectionContext> collection); | 158 int Add(std::unique_ptr<CollectionContext> collection); |
| 216 | 159 |
| 217 // Removes an active collection based on its ID, forcing it to run its | 160 // Removes an active collection based on its ID, forcing it to run its |
| 218 // callback if any data has been collected. This can be called externally | 161 // callback if any data has been collected. This can be called externally |
| 219 // from any thread. | 162 // from any thread. The |done| event, if not null, will be signaled upon |
| 220 void Remove(int id); | 163 // completion. |done| must live until after it has been signaled. |
| 164 void Remove(int id, WaitableEvent* done); |
| 221 | 165 |
| 222 // Removes all active collections and stops the underlying thread. | 166 // Removes all active collections and stops the underlying thread. |
| 223 void Shutdown(); | 167 void Shutdown(); |
| 224 | 168 |
| 225 // Begins an idle shutdown as if the idle-timer had expired. | 169 // Begins an idle shutdown as if the idle-timer had expired. |
| 226 void ShutdownIfIdle(); | 170 void ShutdownIfIdle(); |
| 227 | 171 |
| 228 // Undoes the "permanent" effect of Shutdown() so the thread can restart. | 172 // Undoes the "permanent" effect of Shutdown() so the thread can restart. |
| 229 void UndoShutdown(); | 173 void UndoShutdown(); |
| 230 | 174 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 248 void FinishCollection(CollectionContext* collection); | 192 void FinishCollection(CollectionContext* collection); |
| 249 | 193 |
| 250 // Records a single sample of a collection. | 194 // Records a single sample of a collection. |
| 251 void RecordSample(CollectionContext* collection); | 195 void RecordSample(CollectionContext* collection); |
| 252 | 196 |
| 253 // Check if the sampling thread is idle. | 197 // Check if the sampling thread is idle. |
| 254 void CheckForIdle(); | 198 void CheckForIdle(); |
| 255 | 199 |
| 256 // These methods are tasks that get posted to the internal message queue. | 200 // These methods are tasks that get posted to the internal message queue. |
| 257 void AddCollectionTask(std::unique_ptr<CollectionContext> collection_ptr); | 201 void AddCollectionTask(std::unique_ptr<CollectionContext> collection_ptr); |
| 258 void RemoveCollectionTask(int id); | 202 void RemoveCollectionTask(int id, WaitableEvent* done); |
| 259 void PerformCollectionTask(int id); | 203 void PerformCollectionTask(int id); |
| 260 void ShutdownTask(); | 204 void ShutdownTask(); |
| 261 | 205 |
| 262 // Updates the |next_sample_time| time based on configured parameters. | 206 // Updates the |next_sample_time| time based on configured parameters. |
| 263 bool UpdateNextSampleTime(CollectionContext* collection); | 207 bool UpdateNextSampleTime(CollectionContext* collection); |
| 264 | 208 |
| 265 // Thread: | 209 // Thread: |
| 266 void CleanUp() override; | 210 void CleanUp() override; |
| 267 | 211 |
| 268 // The task-runner for the sampling thread and some information about it. | 212 // The task-runner for the sampling thread and some information about it. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 // There may be no task-runner if the sampling thread has been permanently | 257 // There may be no task-runner if the sampling thread has been permanently |
| 314 // shut down. | 258 // shut down. |
| 315 if (task_runner) { | 259 if (task_runner) { |
| 316 task_runner->PostTask( | 260 task_runner->PostTask( |
| 317 FROM_HERE, Bind(&SamplingThread::AddCollectionTask, Unretained(this), | 261 FROM_HERE, Bind(&SamplingThread::AddCollectionTask, Unretained(this), |
| 318 Passed(&collection))); | 262 Passed(&collection))); |
| 319 } | 263 } |
| 320 return id; | 264 return id; |
| 321 } | 265 } |
| 322 | 266 |
| 323 void StackSamplingProfiler::SamplingThread::Remove(int id) { | 267 void StackSamplingProfiler::SamplingThread::Remove(int id, |
| 268 WaitableEvent* done) { |
| 324 scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(); | 269 scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(); |
| 325 if (!task_runner) | 270 if (task_runner) { |
| 326 return; // Everything has already stopped. | 271 // This can fail if the thread were to exit between acquisition of the task |
| 272 // runner above and the call below. Because there may be something waiting |
| 273 // for a signal on the |done| event, return only if the task was success- |
| 274 // fully posted. |
| 275 if (task_runner->PostTask(FROM_HERE, |
| 276 Bind(&SamplingThread::RemoveCollectionTask, |
| 277 Unretained(this), id, Unretained(done)))) { |
| 278 return; |
| 279 } |
| 280 } |
| 327 | 281 |
| 328 // This can fail if the thread were to exit between acquisition of the task | 282 // The sampling thread has already exited so signal the event here. |
| 329 // runner above and the call below. In that case, however, everything has | 283 if (done) |
| 330 // stopped so there's no need to try to stop it. | 284 done->Signal(); |
| 331 task_runner->PostTask(FROM_HERE, Bind(&SamplingThread::RemoveCollectionTask, | |
| 332 Unretained(this), id)); | |
| 333 } | 285 } |
| 334 | 286 |
| 335 void StackSamplingProfiler::SamplingThread::Shutdown() { | 287 void StackSamplingProfiler::SamplingThread::Shutdown() { |
| 336 // Record that a shutdown has been requested so nothing can cause it to | 288 // Record that a shutdown has been requested so nothing can cause it to |
| 337 // start up again. | 289 // start up again. |
| 338 { | 290 { |
| 339 AutoLock lock(task_runner_lock_); | 291 AutoLock lock(task_runner_lock_); |
| 340 task_runner_forced_shutdown_ = true; | 292 task_runner_forced_shutdown_ = true; |
| 341 } | 293 } |
| 342 | 294 |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 CollectionContext* collection = collection_ptr.get(); | 459 CollectionContext* collection = collection_ptr.get(); |
| 508 active_collections_.insert( | 460 active_collections_.insert( |
| 509 std::make_pair(collection->collection_id, std::move(collection_ptr))); | 461 std::make_pair(collection->collection_id, std::move(collection_ptr))); |
| 510 | 462 |
| 511 GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 463 GetTaskRunnerOnSamplingThread()->PostDelayedTask( |
| 512 FROM_HERE, Bind(&SamplingThread::PerformCollectionTask, Unretained(this), | 464 FROM_HERE, Bind(&SamplingThread::PerformCollectionTask, Unretained(this), |
| 513 collection->collection_id), | 465 collection->collection_id), |
| 514 collection->params.initial_delay); | 466 collection->params.initial_delay); |
| 515 } | 467 } |
| 516 | 468 |
| 517 void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) { | 469 void StackSamplingProfiler::SamplingThread::RemoveCollectionTask( |
| 470 int id, |
| 471 WaitableEvent* done) { |
| 518 auto found = active_collections_.find(id); | 472 auto found = active_collections_.find(id); |
| 519 if (found == active_collections_.end()) | 473 if (found != active_collections_.end()) { |
| 520 return; | 474 FinishCollection(found->second.get()); |
| 475 CheckForIdle(); |
| 476 } |
| 521 | 477 |
| 522 FinishCollection(found->second.get()); | 478 if (done) |
| 523 CheckForIdle(); | 479 done->Signal(); |
| 524 } | 480 } |
| 525 | 481 |
| 526 void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { | 482 void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { |
| 527 auto found = active_collections_.find(id); | 483 auto found = active_collections_.find(id); |
| 528 | 484 |
| 529 // The task won't be found if it has been stopped. | 485 // The task won't be found if it has been stopped. |
| 530 if (found == active_collections_.end()) | 486 if (found == active_collections_.end()) |
| 531 return; | 487 return; |
| 532 | 488 |
| 533 CollectionContext* collection = found->second.get(); | 489 CollectionContext* collection = found->second.get(); |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 651 const SamplingParams& params, | 607 const SamplingParams& params, |
| 652 const CompletedCallback& callback, | 608 const CompletedCallback& callback, |
| 653 NativeStackSamplerTestDelegate* test_delegate) | 609 NativeStackSamplerTestDelegate* test_delegate) |
| 654 : thread_id_(thread_id), params_(params), completed_callback_(callback), | 610 : thread_id_(thread_id), params_(params), completed_callback_(callback), |
| 655 test_delegate_(test_delegate) { | 611 test_delegate_(test_delegate) { |
| 656 native_sampler_ = NativeStackSampler::Create(thread_id_, &RecordAnnotations, | 612 native_sampler_ = NativeStackSampler::Create(thread_id_, &RecordAnnotations, |
| 657 test_delegate_); | 613 test_delegate_); |
| 658 } | 614 } |
| 659 | 615 |
| 660 StackSamplingProfiler::~StackSamplingProfiler() { | 616 StackSamplingProfiler::~StackSamplingProfiler() { |
| 661 Stop(); | 617 WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL, |
| 662 | 618 WaitableEvent::InitialState::NOT_SIGNALED); |
| 663 #if !defined(OS_WIN) | 619 Stop(&done); |
| 664 // This is to prevent use of this object on non-Windows builds until such | 620 done.Wait(); |
| 665 // time as the following race condition is addressed: | |
| 666 // Thread A creates a local (stack) StackSamplingProfiler. | |
| 667 // Sampling is initiated against thread A ("A samples A"). | |
| 668 // The StackSamplingProfiler is destructed and thread A exits. | |
| 669 // Sampling continues briefly until the async Stop() gets through. | |
| 670 // Another thread with the same ID replaces the first one. | |
| 671 // Sampling access the new thread and Bad Things(tm) happen. | |
| 672 // Windows is immune to this because the thread handle held internally | |
| 673 // prevents re-use of the thread-ID but this may not be the case on other | |
| 674 // platforms. | |
| 675 NOTREACHED(); | |
| 676 #endif | |
| 677 } | |
| 678 | |
| 679 // static | |
| 680 void StackSamplingProfiler::StartAndRunAsync( | |
| 681 PlatformThreadId thread_id, | |
| 682 const SamplingParams& params, | |
| 683 const CompletedCallback& callback) { | |
| 684 CHECK(ThreadTaskRunnerHandle::Get()); | |
| 685 AsyncRunner::Run(thread_id, params, callback); | |
| 686 } | 621 } |
| 687 | 622 |
| 688 void StackSamplingProfiler::Start() { | 623 void StackSamplingProfiler::Start() { |
| 689 if (completed_callback_.is_null()) | 624 if (completed_callback_.is_null()) |
| 690 return; | 625 return; |
| 691 | 626 |
| 692 if (!native_sampler_) | 627 if (!native_sampler_) |
| 693 return; | 628 return; |
| 694 | 629 |
| 695 collection_id_ = SamplingThread::GetInstance()->Add( | 630 collection_id_ = SamplingThread::GetInstance()->Add( |
| 696 MakeUnique<SamplingThread::CollectionContext>( | 631 MakeUnique<SamplingThread::CollectionContext>( |
| 697 thread_id_, params_, completed_callback_, | 632 thread_id_, params_, completed_callback_, |
| 698 std::move(native_sampler_))); | 633 std::move(native_sampler_))); |
| 699 } | 634 } |
| 700 | 635 |
| 701 void StackSamplingProfiler::Stop() { | 636 void StackSamplingProfiler::Stop(WaitableEvent* done) { |
| 702 SamplingThread::GetInstance()->Remove(collection_id_); | 637 SamplingThread::GetInstance()->Remove(collection_id_, done); |
| 703 } | 638 } |
| 704 | 639 |
| 705 // static | 640 // static |
| 706 void StackSamplingProfiler::Shutdown() { | 641 void StackSamplingProfiler::Shutdown() { |
| 707 SamplingThread::GetInstance()->Shutdown(); | 642 SamplingThread::GetInstance()->Shutdown(); |
| 708 } | 643 } |
| 709 | 644 |
| 710 // static | 645 // static |
| 711 void StackSamplingProfiler::UndoShutdownForTesting() { | 646 void StackSamplingProfiler::UndoShutdownForTesting() { |
| 712 SamplingThread::GetInstance()->UndoShutdown(); | 647 SamplingThread::GetInstance()->UndoShutdown(); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 784 } | 719 } |
| 785 | 720 |
| 786 bool operator<(const StackSamplingProfiler::Frame &a, | 721 bool operator<(const StackSamplingProfiler::Frame &a, |
| 787 const StackSamplingProfiler::Frame &b) { | 722 const StackSamplingProfiler::Frame &b) { |
| 788 return (a.module_index < b.module_index) || | 723 return (a.module_index < b.module_index) || |
| 789 (a.module_index == b.module_index && | 724 (a.module_index == b.module_index && |
| 790 a.instruction_pointer < b.instruction_pointer); | 725 a.instruction_pointer < b.instruction_pointer); |
| 791 } | 726 } |
| 792 | 727 |
| 793 } // namespace base | 728 } // namespace base |
| OLD | NEW |