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 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 static void ShutdownAssumingIdle(bool simulate_intervening_add); | 132 static void ShutdownAssumingIdle(bool simulate_intervening_add); |
133 | 133 |
134 private: | 134 private: |
135 // Calls the sampling threads ShutdownTask and then signals an event. | 135 // Calls the sampling threads ShutdownTask and then signals an event. |
136 static void ShutdownTaskAndSignalEvent(SamplingThread* sampler, | 136 static void ShutdownTaskAndSignalEvent(SamplingThread* sampler, |
137 int add_events, | 137 int add_events, |
138 WaitableEvent* event); | 138 WaitableEvent* event); |
139 }; | 139 }; |
140 | 140 |
141 struct CollectionContext { | 141 struct CollectionContext { |
142 CollectionContext(PlatformThreadId target, | 142 CollectionContext(int collection_id, |
143 PlatformThreadId target, | |
143 const SamplingParams& params, | 144 const SamplingParams& params, |
144 const CompletedCallback& callback, | 145 const CompletedCallback& callback, |
145 WaitableEvent* finished, | 146 WaitableEvent* finished, |
146 std::unique_ptr<NativeStackSampler> sampler) | 147 std::unique_ptr<NativeStackSampler> sampler) |
147 : collection_id(next_collection_id_.GetNext()), | 148 : collection_id(collection_id), |
148 target(target), | 149 target(target), |
149 params(params), | 150 params(params), |
150 callback(callback), | 151 callback(callback), |
151 finished(finished), | 152 finished(finished), |
152 native_sampler(std::move(sampler)) {} | 153 native_sampler(std::move(sampler)) {} |
153 ~CollectionContext() {} | 154 ~CollectionContext() {} |
154 | 155 |
155 // An identifier for this collection, used to uniquely identify it to | 156 // An identifier for this collection, used to uniquely identify it to |
156 // outside interests. | 157 // outside interests. |
157 const int collection_id; | 158 const int collection_id; |
Mike Wittman
2017/07/06 17:25:52
We probably should rename this to something like "
Alexei Svitkine (slow)
2017/07/06 19:36:26
Done.
| |
158 | 159 |
159 const PlatformThreadId target; // ID of The thread being sampled. | 160 const PlatformThreadId target; // ID of The thread being sampled. |
160 const SamplingParams params; // Information about how to sample. | 161 const SamplingParams params; // Information about how to sample. |
161 const CompletedCallback callback; // Callback made when sampling complete. | 162 const CompletedCallback callback; // Callback made when sampling complete. |
162 WaitableEvent* const finished; // Signaled when all sampling complete. | 163 WaitableEvent* const finished; // Signaled when all sampling complete. |
163 | 164 |
164 // Platform-specific module that does the actual sampling. | 165 // Platform-specific module that does the actual sampling. |
165 std::unique_ptr<NativeStackSampler> native_sampler; | 166 std::unique_ptr<NativeStackSampler> native_sampler; |
166 | 167 |
167 // The absolute time for the next sample. | 168 // The absolute time for the next sample. |
168 Time next_sample_time; | 169 Time next_sample_time; |
169 | 170 |
170 // The time that a profile was started, for calculating the total duration. | 171 // The time that a profile was started, for calculating the total duration. |
171 Time profile_start_time; | 172 Time profile_start_time; |
172 | 173 |
173 // Counters that indicate the current position along the acquisition. | 174 // Counters that indicate the current position along the acquisition. |
174 int burst = 0; | 175 int burst = 0; |
175 int sample = 0; | 176 int sample = 0; |
176 | 177 |
177 // The collected stack samples. The active profile is always at the back(). | 178 // The collected stack samples. The active profile is always at the back(). |
178 CallStackProfiles profiles; | 179 CallStackProfiles profiles; |
179 | 180 |
180 private: | |
181 static StaticAtomicSequenceNumber next_collection_id_; | 181 static StaticAtomicSequenceNumber next_collection_id_; |
Mike Wittman
2017/07/06 17:25:53
nit: drop trailing underscore
Alexei Svitkine (slow)
2017/07/06 19:36:26
Done.
| |
182 }; | 182 }; |
183 | 183 |
184 // Gets the single instance of this class. | 184 // Gets the single instance of this class. |
185 static SamplingThread* GetInstance(); | 185 static SamplingThread* GetInstance(); |
186 | 186 |
187 // Adds a new CollectionContext to the thread. This can be called externally | 187 // Adds a new CollectionContext to the thread. This can be called externally |
188 // from any thread. This returns an ID that can later be used to stop | 188 // from any thread. This returns an ID that can later be used to stop |
189 // the sampling. | 189 // the sampling. |
190 int Add(std::unique_ptr<CollectionContext> collection); | 190 int Add(std::unique_ptr<CollectionContext> collection); |
191 | 191 |
(...skipping 29 matching lines...) Expand all Loading... | |
221 ~SamplingThread() override; | 221 ~SamplingThread() override; |
222 | 222 |
223 // Get task runner that is usable from the outside. | 223 // Get task runner that is usable from the outside. |
224 scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd(); | 224 scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd(); |
225 scoped_refptr<SingleThreadTaskRunner> GetTaskRunner( | 225 scoped_refptr<SingleThreadTaskRunner> GetTaskRunner( |
226 ThreadExecutionState* out_state); | 226 ThreadExecutionState* out_state); |
227 | 227 |
228 // Get task runner that is usable from the sampling thread itself. | 228 // Get task runner that is usable from the sampling thread itself. |
229 scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread(); | 229 scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread(); |
230 | 230 |
231 // Finishes a collection and reports collected data via callback. | 231 // Finishes a collection and reports collected data via callback. Returns |
232 void FinishCollection(CollectionContext* collection); | 232 // the new collection params, if a new collection should be started. |
233 Optional<SamplingParams> FinishCollection(CollectionContext* collection); | |
233 | 234 |
234 // Records a single sample of a collection. | 235 // Records a single sample of a collection. |
235 void RecordSample(CollectionContext* collection); | 236 void RecordSample(CollectionContext* collection); |
236 | 237 |
237 // Check if the sampling thread is idle and begin a shutdown if it is. | 238 // Check if the sampling thread is idle and begin a shutdown if it is. |
238 void ScheduleShutdownIfIdle(); | 239 void ScheduleShutdownIfIdle(); |
239 | 240 |
240 // These methods are tasks that get posted to the internal message queue. | 241 // These methods are tasks that get posted to the internal message queue. |
241 void AddCollectionTask(std::unique_ptr<CollectionContext> collection); | 242 void AddCollectionTask(std::unique_ptr<CollectionContext> collection); |
242 void RemoveCollectionTask(int id); | 243 void RemoveCollectionTask(int id); |
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
468 | 469 |
469 scoped_refptr<SingleThreadTaskRunner> | 470 scoped_refptr<SingleThreadTaskRunner> |
470 StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() { | 471 StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() { |
471 // This should be called only from the sampling thread as it has limited | 472 // This should be called only from the sampling thread as it has limited |
472 // accessibility. | 473 // accessibility. |
473 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 474 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); |
474 | 475 |
475 return Thread::task_runner(); | 476 return Thread::task_runner(); |
476 } | 477 } |
477 | 478 |
478 void StackSamplingProfiler::SamplingThread::FinishCollection( | 479 Optional<StackSamplingProfiler::SamplingParams> |
480 StackSamplingProfiler::SamplingThread::FinishCollection( | |
479 CollectionContext* collection) { | 481 CollectionContext* collection) { |
480 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 482 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); |
481 | 483 |
482 // If there is no duration for the final profile (because it was stopped), | 484 // If there is no duration for the final profile (because it was stopped), |
483 // calculate it now. | 485 // calculate it now. |
484 if (!collection->profiles.empty() && | 486 if (!collection->profiles.empty() && |
485 collection->profiles.back().profile_duration == TimeDelta()) { | 487 collection->profiles.back().profile_duration == TimeDelta()) { |
486 collection->profiles.back().profile_duration = | 488 collection->profiles.back().profile_duration = |
487 Time::Now() - collection->profile_start_time; | 489 Time::Now() - collection->profile_start_time; |
488 } | 490 } |
489 | 491 |
490 // Extract some information so callback and event-signalling can still be | 492 // Extract some information so callback and event-signalling can still be |
491 // done after the collection has been removed from the list of "active" ones. | 493 // done after the collection has been removed from the list of "active" ones. |
492 // This allows the the controlling object (and tests using it) to be confident | 494 // This allows the the controlling object (and tests using it) to be confident |
493 // that collection is fully finished when those things occur. | 495 // that collection is fully finished when those things occur. |
494 const CompletedCallback callback = collection->callback; | 496 const CompletedCallback callback = collection->callback; |
495 CallStackProfiles profiles = std::move(collection->profiles); | 497 CallStackProfiles profiles = std::move(collection->profiles); |
496 WaitableEvent* finished = collection->finished; | 498 WaitableEvent* finished = collection->finished; |
497 | 499 |
498 // Remove this collection from the map of known ones. The |collection| | |
499 // parameter is invalid after this point. | |
500 size_t count = active_collections_.erase(collection->collection_id); | |
501 DCHECK_EQ(1U, count); | |
502 | |
503 // Run the associated callback, passing the collected profiles. | 500 // Run the associated callback, passing the collected profiles. |
504 callback.Run(std::move(profiles)); | 501 Optional<SamplingParams> new_params = callback.Run(std::move(profiles)); |
505 | 502 |
506 // Signal that this collection is finished. | 503 // Signal that this collection is finished. |
507 finished->Signal(); | 504 finished->Signal(); |
505 | |
506 return new_params; | |
508 } | 507 } |
509 | 508 |
510 void StackSamplingProfiler::SamplingThread::RecordSample( | 509 void StackSamplingProfiler::SamplingThread::RecordSample( |
511 CollectionContext* collection) { | 510 CollectionContext* collection) { |
512 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 511 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); |
513 DCHECK(collection->native_sampler); | 512 DCHECK(collection->native_sampler); |
514 | 513 |
515 // If this is the first sample of a burst, a new Profile needs to be created | 514 // If this is the first sample of a burst, a new Profile needs to be created |
516 // and filled. | 515 // and filled. |
517 if (collection->sample == 0) { | 516 if (collection->sample == 0) { |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
582 } | 581 } |
583 } | 582 } |
584 | 583 |
585 void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) { | 584 void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) { |
586 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 585 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); |
587 | 586 |
588 auto found = active_collections_.find(id); | 587 auto found = active_collections_.find(id); |
589 if (found == active_collections_.end()) | 588 if (found == active_collections_.end()) |
590 return; | 589 return; |
591 | 590 |
592 FinishCollection(found->second.get()); | 591 auto* collection = found->second.get(); |
592 FinishCollection(collection); | |
593 | |
594 // Remove this collection from the map of known ones. After this, | |
595 // |collection| will be invalid. | |
596 size_t count = active_collections_.erase(collection->collection_id); | |
597 DCHECK_EQ(1U, count); | |
598 | |
593 ScheduleShutdownIfIdle(); | 599 ScheduleShutdownIfIdle(); |
594 } | 600 } |
595 | 601 |
596 void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { | 602 void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) { |
597 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 603 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); |
598 | 604 |
599 auto found = active_collections_.find(id); | 605 auto found = active_collections_.find(id); |
600 | 606 |
601 // The task won't be found if it has been stopped. | 607 // The task won't be found if it has been stopped. |
602 if (found == active_collections_.end()) | 608 if (found == active_collections_.end()) |
603 return; | 609 return; |
604 | 610 |
605 CollectionContext* collection = found->second.get(); | 611 CollectionContext* collection = found->second.get(); |
606 | 612 |
607 // Handle first-run with no "next time". | 613 // Handle first-run with no "next time". |
608 if (collection->next_sample_time == Time()) | 614 if (collection->next_sample_time == Time()) |
609 collection->next_sample_time = Time::Now(); | 615 collection->next_sample_time = Time::Now(); |
610 | 616 |
611 // Do the collection of a single sample. | 617 // Do the collection of a single sample. |
612 RecordSample(collection); | 618 RecordSample(collection); |
613 | 619 |
614 // Update the time of the next sample recording. | 620 // Update the time of the next sample recording. |
615 if (UpdateNextSampleTime(collection)) { | 621 const bool collection_finished = !UpdateNextSampleTime(collection); |
616 bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask( | 622 if (collection_finished) { |
617 FROM_HERE, | 623 // All capturing has completed so finish the collection. If no new params |
618 BindOnce(&SamplingThread::PerformCollectionTask, Unretained(this), id), | 624 // are returned, a new collection should not be started. |
619 std::max(collection->next_sample_time - Time::Now(), TimeDelta())); | 625 Optional<SamplingParams> new_params = FinishCollection(collection); |
620 DCHECK(success); | 626 if (!new_params.has_value()) { |
621 } else { | 627 // Remove this collection from the map of known ones. After this, |
622 // All capturing has completed so finish the collection. By not re-adding | 628 // |collection| will be invalid. |
623 // it to the task queue, the collection will "expire" (i.e. no further work | 629 size_t count = active_collections_.erase(collection->collection_id); |
624 // will be done). The |collection| variable will be invalid after this call. | 630 DCHECK_EQ(1U, count); |
625 FinishCollection(collection); | 631 |
626 ScheduleShutdownIfIdle(); | 632 // By not adding it to the task queue, the collection will "expire" (i.e. |
633 // no further work will be done). | |
634 ScheduleShutdownIfIdle(); | |
635 return; | |
636 } | |
637 | |
638 // Restart the collection with the new params. Keep the same collection | |
639 // id so the Stop() operation continues to work. | |
640 auto new_collection = MakeUnique<SamplingThread::CollectionContext>( | |
641 collection->collection_id, collection->target, new_params.value(), | |
642 collection->callback, collection->finished, | |
643 std::move(collection->native_sampler)); | |
644 new_collection->next_sample_time = | |
Mike Wittman
2017/07/06 17:25:52
Can we reuse the logic in AddCollectionTask for re
Alexei Svitkine (slow)
2017/07/06 19:36:25
The way it's structured right now, it re-uses the
Mike Wittman
2017/07/06 22:14:01
I was thinking that we could remove the old collec
| |
645 Time::Now() + collection->params.initial_delay; | |
646 // Replace |collection| in the map by |new_collection|. After this, | |
647 // |collection| will be invalid. | |
648 found->second = std::move(new_collection); | |
627 } | 649 } |
650 | |
651 bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask( | |
652 FROM_HERE, | |
653 BindOnce(&SamplingThread::PerformCollectionTask, Unretained(this), id), | |
654 std::max(collection->next_sample_time - Time::Now(), TimeDelta())); | |
655 DCHECK(success); | |
628 } | 656 } |
629 | 657 |
630 void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) { | 658 void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) { |
631 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); | 659 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); |
632 | 660 |
633 // Holding this lock ensures that any attempt to start another job will | 661 // Holding this lock ensures that any attempt to start another job will |
634 // get postponed until |thread_execution_state_| is updated, thus eliminating | 662 // get postponed until |thread_execution_state_| is updated, thus eliminating |
635 // the race in starting a new thread while the previous one is exiting. | 663 // the race in starting a new thread while the previous one is exiting. |
636 AutoLock lock(thread_execution_state_lock_); | 664 AutoLock lock(thread_execution_state_lock_); |
637 | 665 |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
777 if (!native_sampler) | 805 if (!native_sampler) |
778 return; | 806 return; |
779 | 807 |
780 // Wait for profiling to be "inactive", then reset it for the upcoming run. | 808 // Wait for profiling to be "inactive", then reset it for the upcoming run. |
781 profiling_inactive_.Wait(); | 809 profiling_inactive_.Wait(); |
782 profiling_inactive_.Reset(); | 810 profiling_inactive_.Reset(); |
783 | 811 |
784 DCHECK_EQ(NULL_COLLECTION_ID, collection_id_); | 812 DCHECK_EQ(NULL_COLLECTION_ID, collection_id_); |
785 collection_id_ = SamplingThread::GetInstance()->Add( | 813 collection_id_ = SamplingThread::GetInstance()->Add( |
786 MakeUnique<SamplingThread::CollectionContext>( | 814 MakeUnique<SamplingThread::CollectionContext>( |
815 SamplingThread::CollectionContext::next_collection_id_.GetNext(), | |
787 thread_id_, params_, completed_callback_, &profiling_inactive_, | 816 thread_id_, params_, completed_callback_, &profiling_inactive_, |
788 std::move(native_sampler))); | 817 std::move(native_sampler))); |
789 DCHECK_NE(NULL_COLLECTION_ID, collection_id_); | 818 DCHECK_NE(NULL_COLLECTION_ID, collection_id_); |
790 } | 819 } |
791 | 820 |
792 void StackSamplingProfiler::Stop() { | 821 void StackSamplingProfiler::Stop() { |
793 SamplingThread::GetInstance()->Remove(collection_id_); | 822 SamplingThread::GetInstance()->Remove(collection_id_); |
794 collection_id_ = NULL_COLLECTION_ID; | 823 collection_id_ = NULL_COLLECTION_ID; |
795 } | 824 } |
796 | 825 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
845 } | 874 } |
846 | 875 |
847 bool operator<(const StackSamplingProfiler::Frame &a, | 876 bool operator<(const StackSamplingProfiler::Frame &a, |
848 const StackSamplingProfiler::Frame &b) { | 877 const StackSamplingProfiler::Frame &b) { |
849 return (a.module_index < b.module_index) || | 878 return (a.module_index < b.module_index) || |
850 (a.module_index == b.module_index && | 879 (a.module_index == b.module_index && |
851 a.instruction_pointer < b.instruction_pointer); | 880 a.instruction_pointer < b.instruction_pointer); |
852 } | 881 } |
853 | 882 |
854 } // namespace base | 883 } // namespace base |
OLD | NEW |