Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1074)

Side by Side Diff: base/profiler/stack_sampling_profiler.cc

Issue 2680703004: Wait for sampling to complete before destructing. (Closed)
Patch Set: cleanup, comments, and fixes Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « base/profiler/stack_sampling_profiler.h ('k') | base/profiler/stack_sampling_profiler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698