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 |