1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/profiler/stack_sampling_profiler.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <map>
10 #include <optional>
11 #include <utility>
12
13 #include "base/atomic_sequence_num.h"
14 #include "base/atomicops.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/functional/callback_helpers.h"
18 #include "base/location.h"
19 #include "base/memory/ptr_util.h"
20 #include "base/memory/raw_ptr.h"
21 #include "base/memory/singleton.h"
22 #include "base/profiler/profiler_buildflags.h"
23 #include "base/profiler/stack_buffer.h"
24 #include "base/profiler/stack_sampler.h"
25 #include "base/profiler/unwinder.h"
26 #include "base/synchronization/lock.h"
27 #include "base/synchronization/waitable_event.h"
28 #include "base/thread_annotations.h"
29 #include "base/threading/thread.h"
30 #include "base/threading/thread_restrictions.h"
31 #include "base/time/time.h"
32 #include "base/trace_event/base_tracing.h"
33 #include "build/build_config.h"
34
35 #if BUILDFLAG(IS_WIN)
36 #include "base/win/static_constants.h"
37 #endif
38
39 #if BUILDFLAG(IS_APPLE)
40 #include "base/mac/mac_util.h"
41 #endif
42
43 namespace base {
44
45 // Allows StackSamplingProfiler to recall a thread which should already pretty
46 // much be dead (thus it should be a fast Join()).
47 class ScopedAllowThreadRecallForStackSamplingProfiler
48 : public ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {};
49
50 namespace {
51
52 // This value is used to initialize the WaitableEvent object. This MUST BE set
53 // to MANUAL for correct operation of the IsSignaled() call in Start(). See the
54 // comment there for why.
55 constexpr WaitableEvent::ResetPolicy kResetPolicy =
56 WaitableEvent::ResetPolicy::MANUAL;
57
58 // This value is used when there is no collection in progress and thus no ID
59 // for referencing the active collection to the SamplingThread.
60 const int kNullProfilerId = -1;
61
GetNextSampleTimeImpl(TimeTicks scheduled_current_sample_time,TimeDelta sampling_interval,TimeTicks now)62 TimeTicks GetNextSampleTimeImpl(TimeTicks scheduled_current_sample_time,
63 TimeDelta sampling_interval,
64 TimeTicks now) {
65 // Schedule the next sample at the next sampling_interval-aligned time in
66 // the future that's sufficiently far enough from the current sample. In the
67 // general case this will be one sampling_interval from the current
68 // sample. In cases where sample tasks were unable to be executed, such as
69 // during system suspend or bad system-wide jank, we may have missed some
70 // samples. The right thing to do for those cases is to skip the missed
71 // samples since the rest of the systems also wasn't executing.
72
73 // Ensure that the next sample time is at least half a sampling interval
74 // away. This causes the second sample after resume to be taken between 0.5
75 // and 1.5 samples after the first, or 1 sample interval on average. The delay
76 // also serves to provide a grace period in the normal sampling case where the
77 // current sample may be taken slightly later than its scheduled time.
78 const TimeTicks earliest_next_sample_time = now + sampling_interval / 2;
79
80 const TimeDelta minimum_time_delta_to_next_sample =
81 earliest_next_sample_time - scheduled_current_sample_time;
82
83 // The minimum number of sampling intervals required to get from the scheduled
84 // current sample time to the earliest next sample time.
85 const int64_t required_sampling_intervals = static_cast<int64_t>(
86 std::ceil(minimum_time_delta_to_next_sample / sampling_interval));
87 return scheduled_current_sample_time +
88 required_sampling_intervals * sampling_interval;
89 }
90
91 } // namespace
92
93 // StackSamplingProfiler::SamplingThread --------------------------------------
94
95 class StackSamplingProfiler::SamplingThread : public Thread {
96 public:
97 class TestPeer {
98 public:
99 // Reset the existing sampler. This will unfortunately create the object
100 // unnecessarily if it doesn't already exist but there's no way around that.
101 static void Reset();
102
103 // Disables inherent idle-shutdown behavior.
104 static void DisableIdleShutdown();
105
106 // Begins an idle shutdown as if the idle-timer had expired and wait for
107 // it to execute. Since the timer would have only been started at a time
108 // when the sampling thread actually was idle, this must be called only
109 // when it is known that there are no active sampling threads. If
110 // |simulate_intervening_add| is true then, when executed, the shutdown
111 // task will believe that a new collection has been added since it was
112 // posted.
113 static void ShutdownAssumingIdle(bool simulate_intervening_add);
114
115 private:
116 // Calls the sampling threads ShutdownTask and then signals an event.
117 static void ShutdownTaskAndSignalEvent(SamplingThread* sampler,
118 int add_events,
119 WaitableEvent* event);
120 };
121
122 struct CollectionContext {
CollectionContextbase::StackSamplingProfiler::SamplingThread::CollectionContext123 CollectionContext(PlatformThreadId thread_id,
124 const SamplingParams& params,
125 WaitableEvent* finished,
126 std::unique_ptr<StackSampler> sampler,
127 std::unique_ptr<ProfileBuilder> profile_builder)
128 : collection_id(next_collection_id.GetNext()),
129 thread_id(thread_id),
130 params(params),
131 finished(finished),
132 profile_builder(std::move(profile_builder)),
133 sampler(std::move(sampler)) {}
134 ~CollectionContext() = default;
135
136 // An identifier for this collection, used to uniquely identify the
137 // collection to outside interests.
138 const int collection_id;
139 const PlatformThreadId thread_id; // Thread id of the sampled thread.
140
141 const SamplingParams params; // Information about how to sample.
142 const raw_ptr<WaitableEvent>
143 finished; // Signaled when all sampling complete.
144
145 // Receives the sampling data and builds a CallStackProfile.
146 std::unique_ptr<ProfileBuilder> profile_builder;
147
148 // Platform-specific module that does the actual sampling.
149 std::unique_ptr<StackSampler> sampler;
150
151 // The absolute time for the next sample.
152 TimeTicks next_sample_time;
153
154 // The time that a profile was started, for calculating the total duration.
155 TimeTicks profile_start_time;
156
157 // Counter that indicates the current sample position along the acquisition.
158 int sample_count = 0;
159
160 // Sequence number for generating new collection ids.
161 static AtomicSequenceNumber next_collection_id;
162 };
163
164 // Gets the single instance of this class.
165 static SamplingThread* GetInstance();
166
167 SamplingThread(const SamplingThread&) = delete;
168 SamplingThread& operator=(const SamplingThread&) = delete;
169
170 // Adds a new CollectionContext to the thread. This can be called externally
171 // from any thread. This returns a collection id that can later be used to
172 // stop the sampling.
173 int Add(std::unique_ptr<CollectionContext> collection);
174
175 // Adds an auxiliary unwinder to be used for the collection, to handle
176 // additional, non-native-code unwind scenarios.
177 void AddAuxUnwinder(int collection_id, std::unique_ptr<Unwinder> unwinder);
178
179 // Applies the metadata to already recorded samples in all collections.
180 void ApplyMetadataToPastSamples(base::TimeTicks period_start,
181 base::TimeTicks period_end,
182 uint64_t name_hash,
183 std::optional<int64_t> key,
184 int64_t value,
185 std::optional<PlatformThreadId> thread_id);
186
187 // Adds the metadata as profile metadata. Profile metadata stores metadata
188 // global to the profile.
189 void AddProfileMetadata(uint64_t name_hash,
190 std::optional<int64_t> key,
191 int64_t value,
192 std::optional<PlatformThreadId> thread_id);
193
194 // Removes an active collection based on its collection id, forcing it to run
195 // its callback if any data has been collected. This can be called externally
196 // from any thread.
197 void Remove(int collection_id);
198
199 private:
200 friend struct DefaultSingletonTraits<SamplingThread>;
201
202 // The different states in which the sampling-thread can be.
203 enum ThreadExecutionState {
204 // The thread is not running because it has never been started. It will be
205 // started when a sampling request is received.
206 NOT_STARTED,
207
208 // The thread is running and processing tasks. This is the state when any
209 // sampling requests are active and during the "idle" period afterward
210 // before the thread is stopped.
211 RUNNING,
212
213 // Once all sampling requests have finished and the "idle" period has
214 // expired, the thread will be set to this state and its shutdown
215 // initiated. A call to Stop() must be made to ensure the previous thread
216 // has completely exited before calling Start() and moving back to the
217 // RUNNING state.
218 EXITING,
219 };
220
221 SamplingThread();
222 ~SamplingThread() override;
223
224 // Get task runner that is usable from the outside.
225 scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd();
226 scoped_refptr<SingleThreadTaskRunner> GetTaskRunner(
227 ThreadExecutionState* out_state);
228
229 // Get task runner that is usable from the sampling thread itself.
230 scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread();
231
232 // Finishes a collection. The collection's |finished| waitable event will be
233 // signalled. The |collection| should already have been removed from
234 // |active_collections_| by the caller, as this is needed to avoid flakiness
235 // in unit tests.
236 void FinishCollection(std::unique_ptr<CollectionContext> collection);
237
238 // Check if the sampling thread is idle and begin a shutdown if it is.
239 void ScheduleShutdownIfIdle();
240
241 // These methods are tasks that get posted to the internal message queue.
242 void AddCollectionTask(std::unique_ptr<CollectionContext> collection);
243 void AddAuxUnwinderTask(int collection_id,
244 std::unique_ptr<Unwinder> unwinder);
245 void ApplyMetadataToPastSamplesTask(
246 base::TimeTicks period_start,
247 base::TimeTicks period_end,
248 uint64_t name_hash,
249 std::optional<int64_t> key,
250 int64_t value,
251 std::optional<PlatformThreadId> thread_id);
252 void AddProfileMetadataTask(uint64_t name_hash,
253 std::optional<int64_t> key,
254 int64_t value,
255 std::optional<PlatformThreadId> thread_id);
256 void RemoveCollectionTask(int collection_id);
257 void RecordSampleTask(int collection_id);
258 void ShutdownTask(int add_events);
259
260 // Thread:
261 void CleanUp() override;
262
263 // A stack-buffer used by the sampler for its work. This buffer is re-used
264 // across multiple sampler objects since their execution is serialized on the
265 // sampling thread.
266 std::unique_ptr<StackBuffer> stack_buffer_;
267
268 // A map of collection ids to collection contexts. Because this class is a
269 // singleton that is never destroyed, context objects will never be destructed
270 // except by explicit action. Thus, it's acceptable to pass unretained
271 // pointers to these objects when posting tasks.
272 std::map<int, std::unique_ptr<CollectionContext>> active_collections_;
273
274 // State maintained about the current execution (or non-execution) of
275 // the thread. This state must always be accessed while holding the
276 // lock. A copy of the task-runner is maintained here for use by any
277 // calling thread; this is necessary because Thread's accessor for it is
278 // not itself thread-safe. The lock is also used to order calls to the
279 // Thread API (Start, Stop, StopSoon, & DetachFromSequence) so that
280 // multiple threads may make those calls.
281 Lock thread_execution_state_lock_; // Protects all thread_execution_state_*
282 ThreadExecutionState thread_execution_state_
283 GUARDED_BY(thread_execution_state_lock_) = NOT_STARTED;
284 scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_
285 GUARDED_BY(thread_execution_state_lock_);
286 bool thread_execution_state_disable_idle_shutdown_for_testing_
287 GUARDED_BY(thread_execution_state_lock_) = false;
288
289 // A counter that notes adds of new collection requests. It is incremented
290 // when changes occur so that delayed shutdown tasks are able to detect if
291 // something new has happened while it was waiting. Like all "execution_state"
292 // vars, this must be accessed while holding |thread_execution_state_lock_|.
293 int thread_execution_state_add_events_
294 GUARDED_BY(thread_execution_state_lock_) = 0;
295 };
296
297 // static
Reset()298 void StackSamplingProfiler::SamplingThread::TestPeer::Reset() {
299 SamplingThread* sampler = SamplingThread::GetInstance();
300
301 ThreadExecutionState state;
302 {
303 AutoLock lock(sampler->thread_execution_state_lock_);
304 state = sampler->thread_execution_state_;
305 DCHECK(sampler->active_collections_.empty());
306 }
307
308 // Stop the thread and wait for it to exit. This has to be done through by
309 // the thread itself because it has taken ownership of its own lifetime.
310 if (state == RUNNING) {
311 ShutdownAssumingIdle(false);
312 state = EXITING;
313 }
314 // Make sure thread is cleaned up since state will be reset to NOT_STARTED.
315 if (state == EXITING)
316 sampler->Stop();
317
318 // Reset internal variables to the just-initialized state.
319 {
320 AutoLock lock(sampler->thread_execution_state_lock_);
321 sampler->thread_execution_state_ = NOT_STARTED;
322 sampler->thread_execution_state_task_runner_ = nullptr;
323 sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = false;
324 sampler->thread_execution_state_add_events_ = 0;
325 }
326 }
327
328 // static
DisableIdleShutdown()329 void StackSamplingProfiler::SamplingThread::TestPeer::DisableIdleShutdown() {
330 SamplingThread* sampler = SamplingThread::GetInstance();
331
332 {
333 AutoLock lock(sampler->thread_execution_state_lock_);
334 sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = true;
335 }
336 }
337
338 // static
ShutdownAssumingIdle(bool simulate_intervening_add)339 void StackSamplingProfiler::SamplingThread::TestPeer::ShutdownAssumingIdle(
340 bool simulate_intervening_add) {
341 SamplingThread* sampler = SamplingThread::GetInstance();
342
343 ThreadExecutionState state;
344 scoped_refptr<SingleThreadTaskRunner> task_runner =
345 sampler->GetTaskRunner(&state);
346 DCHECK_EQ(RUNNING, state);
347 DCHECK(task_runner);
348
349 int add_events;
350 {
351 AutoLock lock(sampler->thread_execution_state_lock_);
352 add_events = sampler->thread_execution_state_add_events_;
353 if (simulate_intervening_add)
354 ++sampler->thread_execution_state_add_events_;
355 }
356
357 WaitableEvent executed(WaitableEvent::ResetPolicy::MANUAL,
358 WaitableEvent::InitialState::NOT_SIGNALED);
359 // PostTaskAndReply won't work because thread and associated message-loop may
360 // be shut down.
361 task_runner->PostTask(
362 FROM_HERE, BindOnce(&ShutdownTaskAndSignalEvent, Unretained(sampler),
363 add_events, Unretained(&executed)));
364 executed.Wait();
365 }
366
367 // static
368 void StackSamplingProfiler::SamplingThread::TestPeer::
ShutdownTaskAndSignalEvent(SamplingThread * sampler,int add_events,WaitableEvent * event)369 ShutdownTaskAndSignalEvent(SamplingThread* sampler,
370 int add_events,
371 WaitableEvent* event) {
372 sampler->ShutdownTask(add_events);
373 event->Signal();
374 }
375
376 AtomicSequenceNumber StackSamplingProfiler::SamplingThread::CollectionContext::
377 next_collection_id;
378
SamplingThread()379 StackSamplingProfiler::SamplingThread::SamplingThread()
380 : Thread("StackSamplingProfiler") {}
381
382 StackSamplingProfiler::SamplingThread::~SamplingThread() = default;
383
384 StackSamplingProfiler::SamplingThread*
GetInstance()385 StackSamplingProfiler::SamplingThread::GetInstance() {
386 return Singleton<SamplingThread, LeakySingletonTraits<SamplingThread>>::get();
387 }
388
Add(std::unique_ptr<CollectionContext> collection)389 int StackSamplingProfiler::SamplingThread::Add(
390 std::unique_ptr<CollectionContext> collection) {
391 // This is not to be run on the sampling thread.
392
393 int collection_id = collection->collection_id;
394 scoped_refptr<SingleThreadTaskRunner> task_runner =
395 GetOrCreateTaskRunnerForAdd();
396
397 task_runner->PostTask(
398 FROM_HERE, BindOnce(&SamplingThread::AddCollectionTask, Unretained(this),
399 std::move(collection)));
400
401 return collection_id;
402 }
403
AddAuxUnwinder(int collection_id,std::unique_ptr<Unwinder> unwinder)404 void StackSamplingProfiler::SamplingThread::AddAuxUnwinder(
405 int collection_id,
406 std::unique_ptr<Unwinder> unwinder) {
407 ThreadExecutionState state;
408 scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
409 if (state != RUNNING)
410 return;
411 DCHECK(task_runner);
412 task_runner->PostTask(
413 FROM_HERE, BindOnce(&SamplingThread::AddAuxUnwinderTask, Unretained(this),
414 collection_id, std::move(unwinder)));
415 }
416
ApplyMetadataToPastSamples(base::TimeTicks period_start,base::TimeTicks period_end,uint64_t name_hash,std::optional<int64_t> key,int64_t value,std::optional<PlatformThreadId> thread_id)417 void StackSamplingProfiler::SamplingThread::ApplyMetadataToPastSamples(
418 base::TimeTicks period_start,
419 base::TimeTicks period_end,
420 uint64_t name_hash,
421 std::optional<int64_t> key,
422 int64_t value,
423 std::optional<PlatformThreadId> thread_id) {
424 ThreadExecutionState state;
425 scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
426 if (state != RUNNING)
427 return;
428 DCHECK(task_runner);
429 task_runner->PostTask(
430 FROM_HERE, BindOnce(&SamplingThread::ApplyMetadataToPastSamplesTask,
431 Unretained(this), period_start, period_end, name_hash,
432 key, value, thread_id));
433 }
434
AddProfileMetadata(uint64_t name_hash,std::optional<int64_t> key,int64_t value,std::optional<PlatformThreadId> thread_id)435 void StackSamplingProfiler::SamplingThread::AddProfileMetadata(
436 uint64_t name_hash,
437 std::optional<int64_t> key,
438 int64_t value,
439 std::optional<PlatformThreadId> thread_id) {
440 ThreadExecutionState state;
441 scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
442 if (state != RUNNING) {
443 return;
444 }
445 DCHECK(task_runner);
446 task_runner->PostTask(
447 FROM_HERE, BindOnce(&SamplingThread::AddProfileMetadataTask,
448 Unretained(this), name_hash, key, value, thread_id));
449 }
450
Remove(int collection_id)451 void StackSamplingProfiler::SamplingThread::Remove(int collection_id) {
452 // This is not to be run on the sampling thread.
453
454 ThreadExecutionState state;
455 scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
456 if (state != RUNNING)
457 return;
458 DCHECK(task_runner);
459
460 // This can fail if the thread were to exit between acquisition of the task
461 // runner above and the call below. In that case, however, everything has
462 // stopped so there's no need to try to stop it.
463 task_runner->PostTask(FROM_HERE,
464 BindOnce(&SamplingThread::RemoveCollectionTask,
465 Unretained(this), collection_id));
466 }
467
468 scoped_refptr<SingleThreadTaskRunner>
GetOrCreateTaskRunnerForAdd()469 StackSamplingProfiler::SamplingThread::GetOrCreateTaskRunnerForAdd() {
470 AutoLock lock(thread_execution_state_lock_);
471
472 // The increment of the "add events" count is why this method is to be only
473 // called from "add".
474 ++thread_execution_state_add_events_;
475
476 if (thread_execution_state_ == RUNNING) {
477 DCHECK(thread_execution_state_task_runner_);
478 // This shouldn't be called from the sampling thread as it's inefficient.
479 // Use GetTaskRunnerOnSamplingThread() instead.
480 DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
481 return thread_execution_state_task_runner_;
482 }
483
484 if (thread_execution_state_ == EXITING) {
485 // StopSoon() was previously called to shut down the thread
486 // asynchonously. Stop() must now be called before calling Start() again to
487 // reset the thread state.
488 //
489 // We must allow blocking here to satisfy the Thread implementation, but in
490 // practice the Stop() call is unlikely to actually block. For this to
491 // happen a new profiling request would have to be made within the narrow
492 // window between StopSoon() and thread exit following the end of the 60
493 // second idle period.
494 ScopedAllowThreadRecallForStackSamplingProfiler allow_thread_join;
495 Stop();
496 }
497
498 DCHECK(!stack_buffer_);
499 stack_buffer_ = StackSampler::CreateStackBuffer();
500
501 // The thread is not running. Start it and get associated runner. The task-
502 // runner has to be saved for future use because though it can be used from
503 // any thread, it can be acquired via task_runner() only on the created
504 // thread and the thread that creates it (i.e. this thread) for thread-safety
505 // reasons which are alleviated in SamplingThread by gating access to it with
506 // the |thread_execution_state_lock_|.
507 Start();
508 thread_execution_state_ = RUNNING;
509 thread_execution_state_task_runner_ = Thread::task_runner();
510
511 // Detach the sampling thread from the "sequence" (i.e. thread) that
512 // started it so that it can be self-managed or stopped by another thread.
513 DetachFromSequence();
514
515 return thread_execution_state_task_runner_;
516 }
517
518 scoped_refptr<SingleThreadTaskRunner>
GetTaskRunner(ThreadExecutionState * out_state)519 StackSamplingProfiler::SamplingThread::GetTaskRunner(
520 ThreadExecutionState* out_state) {
521 AutoLock lock(thread_execution_state_lock_);
522 if (out_state)
523 *out_state = thread_execution_state_;
524 if (thread_execution_state_ == RUNNING) {
525 // This shouldn't be called from the sampling thread as it's inefficient.
526 // Use GetTaskRunnerOnSamplingThread() instead.
527 DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
528 DCHECK(thread_execution_state_task_runner_);
529 } else {
530 DCHECK(!thread_execution_state_task_runner_);
531 }
532
533 return thread_execution_state_task_runner_;
534 }
535
536 scoped_refptr<SingleThreadTaskRunner>
GetTaskRunnerOnSamplingThread()537 StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() {
538 // This should be called only from the sampling thread as it has limited
539 // accessibility.
540 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
541
542 return Thread::task_runner();
543 }
544
FinishCollection(std::unique_ptr<CollectionContext> collection)545 void StackSamplingProfiler::SamplingThread::FinishCollection(
546 std::unique_ptr<CollectionContext> collection) {
547 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
548 DCHECK_EQ(0u, active_collections_.count(collection->collection_id));
549
550 TimeDelta profile_duration = TimeTicks::Now() -
551 collection->profile_start_time +
552 collection->params.sampling_interval;
553
554 collection->profile_builder->OnProfileCompleted(
555 profile_duration, collection->params.sampling_interval);
556
557 // Signal that this collection is finished.
558 WaitableEvent* collection_finished = collection->finished;
559 // Ensure the collection is destroyed before signaling, so that it may
560 // not outlive StackSamplingProfiler.
561 collection.reset();
562 collection_finished->Signal();
563
564 ScheduleShutdownIfIdle();
565 }
566
ScheduleShutdownIfIdle()567 void StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle() {
568 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
569
570 if (!active_collections_.empty())
571 return;
572
573 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
574 "StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle");
575
576 int add_events;
577 {
578 AutoLock lock(thread_execution_state_lock_);
579 if (thread_execution_state_disable_idle_shutdown_for_testing_)
580 return;
581 add_events = thread_execution_state_add_events_;
582 }
583
584 GetTaskRunnerOnSamplingThread()->PostDelayedTask(
585 FROM_HERE,
586 BindOnce(&SamplingThread::ShutdownTask, Unretained(this), add_events),
587 Seconds(60));
588 }
589
AddAuxUnwinderTask(int collection_id,std::unique_ptr<Unwinder> unwinder)590 void StackSamplingProfiler::SamplingThread::AddAuxUnwinderTask(
591 int collection_id,
592 std::unique_ptr<Unwinder> unwinder) {
593 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
594
595 auto loc = active_collections_.find(collection_id);
596 if (loc == active_collections_.end())
597 return;
598
599 loc->second->sampler->AddAuxUnwinder(std::move(unwinder));
600 }
601
ApplyMetadataToPastSamplesTask(base::TimeTicks period_start,base::TimeTicks period_end,uint64_t name_hash,std::optional<int64_t> key,int64_t value,std::optional<PlatformThreadId> thread_id)602 void StackSamplingProfiler::SamplingThread::ApplyMetadataToPastSamplesTask(
603 base::TimeTicks period_start,
604 base::TimeTicks period_end,
605 uint64_t name_hash,
606 std::optional<int64_t> key,
607 int64_t value,
608 std::optional<PlatformThreadId> thread_id) {
609 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
610 MetadataRecorder::Item item(name_hash, key, thread_id, value);
611 for (auto& id_collection_pair : active_collections_) {
612 if (thread_id && id_collection_pair.second->thread_id != thread_id)
613 continue;
614 id_collection_pair.second->profile_builder->ApplyMetadataRetrospectively(
615 period_start, period_end, item);
616 }
617 }
618
AddProfileMetadataTask(uint64_t name_hash,std::optional<int64_t> key,int64_t value,std::optional<PlatformThreadId> thread_id)619 void StackSamplingProfiler::SamplingThread::AddProfileMetadataTask(
620 uint64_t name_hash,
621 std::optional<int64_t> key,
622 int64_t value,
623 std::optional<PlatformThreadId> thread_id) {
624 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
625 MetadataRecorder::Item item(name_hash, key, thread_id, value);
626 for (auto& id_collection_pair : active_collections_) {
627 if (thread_id && id_collection_pair.second->thread_id != thread_id) {
628 continue;
629 }
630 id_collection_pair.second->profile_builder->AddProfileMetadata(item);
631 }
632 }
633
AddCollectionTask(std::unique_ptr<CollectionContext> collection)634 void StackSamplingProfiler::SamplingThread::AddCollectionTask(
635 std::unique_ptr<CollectionContext> collection) {
636 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
637
638 const int collection_id = collection->collection_id;
639 const TimeDelta initial_delay = collection->params.initial_delay;
640
641 collection->sampler->Initialize();
642
643 active_collections_.insert(
644 std::make_pair(collection_id, std::move(collection)));
645
646 GetTaskRunnerOnSamplingThread()->PostDelayedTask(
647 FROM_HERE,
648 BindOnce(&SamplingThread::RecordSampleTask, Unretained(this),
649 collection_id),
650 initial_delay);
651
652 // Another increment of "add events" serves to invalidate any pending
653 // shutdown tasks that may have been initiated between the Add() and this
654 // task running.
655 {
656 AutoLock lock(thread_execution_state_lock_);
657 ++thread_execution_state_add_events_;
658 }
659 }
660
RemoveCollectionTask(int collection_id)661 void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(
662 int collection_id) {
663 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
664
665 auto found = active_collections_.find(collection_id);
666 if (found == active_collections_.end())
667 return;
668
669 // Remove |collection| from |active_collections_|.
670 std::unique_ptr<CollectionContext> collection = std::move(found->second);
671 size_t count = active_collections_.erase(collection_id);
672 DCHECK_EQ(1U, count);
673
674 FinishCollection(std::move(collection));
675 }
676
RecordSampleTask(int collection_id)677 void StackSamplingProfiler::SamplingThread::RecordSampleTask(
678 int collection_id) {
679 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
680
681 auto found = active_collections_.find(collection_id);
682
683 // The task won't be found if it has been stopped.
684 if (found == active_collections_.end())
685 return;
686
687 CollectionContext* collection = found->second.get();
688
689 // If this is the first sample, the collection params need to be filled.
690 if (collection->sample_count == 0) {
691 collection->profile_start_time = TimeTicks::Now();
692 collection->next_sample_time = TimeTicks::Now();
693 }
694
695 // Record a single sample.
696 collection->sampler->RecordStackFrames(stack_buffer_.get(),
697 collection->profile_builder.get(),
698 collection->thread_id);
699
700 // Schedule the next sample recording if there is one.
701 if (++collection->sample_count < collection->params.samples_per_profile) {
702 collection->next_sample_time = GetNextSampleTimeImpl(
703 collection->next_sample_time, collection->params.sampling_interval,
704 TimeTicks::Now());
705 bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask(
706 FROM_HERE,
707 BindOnce(&SamplingThread::RecordSampleTask, Unretained(this),
708 collection_id),
709 std::max(collection->next_sample_time - TimeTicks::Now(), TimeDelta()));
710 DCHECK(success);
711 return;
712 }
713
714 // Take ownership of |collection| and remove it from the map.
715 std::unique_ptr<CollectionContext> owned_collection =
716 std::move(found->second);
717 size_t count = active_collections_.erase(collection_id);
718 DCHECK_EQ(1U, count);
719
720 // All capturing has completed so finish the collection.
721 FinishCollection(std::move(owned_collection));
722 }
723
ShutdownTask(int add_events)724 void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) {
725 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
726
727 // Holding this lock ensures that any attempt to start another job will
728 // get postponed until |thread_execution_state_| is updated, thus eliminating
729 // the race in starting a new thread while the previous one is exiting.
730 AutoLock lock(thread_execution_state_lock_);
731
732 // If the current count of creation requests doesn't match the passed count
733 // then other tasks have been created since this was posted. Abort shutdown.
734 if (thread_execution_state_add_events_ != add_events)
735 return;
736
737 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
738 "StackSamplingProfiler::SamplingThread::ShutdownTask");
739
740 // There can be no new AddCollectionTasks at this point because creating
741 // those always increments "add events". There may be other requests, like
742 // Remove, but it's okay to schedule the thread to stop once they've been
743 // executed (i.e. "soon").
744 DCHECK(active_collections_.empty());
745 StopSoon();
746
747 // StopSoon will have set the owning sequence (again) so it must be detached
748 // (again) in order for Stop/Start to be called (again) should more work
749 // come in. Holding the |thread_execution_state_lock_| ensures the necessary
750 // happens-after with regard to this detach and future Thread API calls.
751 DetachFromSequence();
752
753 // Set the thread_state variable so the thread will be restarted when new
754 // work comes in. Remove the |thread_execution_state_task_runner_| to avoid
755 // confusion.
756 thread_execution_state_ = EXITING;
757 thread_execution_state_task_runner_ = nullptr;
758 stack_buffer_.reset();
759 }
760
CleanUp()761 void StackSamplingProfiler::SamplingThread::CleanUp() {
762 DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
763
764 // There should be no collections remaining when the thread stops.
765 DCHECK(active_collections_.empty());
766
767 // Let the parent clean up.
768 Thread::CleanUp();
769 }
770
771 // StackSamplingProfiler ------------------------------------------------------
772
773 // static
Reset()774 void StackSamplingProfiler::TestPeer::Reset() {
775 SamplingThread::TestPeer::Reset();
776 }
777
778 // static
IsSamplingThreadRunning()779 bool StackSamplingProfiler::TestPeer::IsSamplingThreadRunning() {
780 return SamplingThread::GetInstance()->IsRunning();
781 }
782
783 // static
DisableIdleShutdown()784 void StackSamplingProfiler::TestPeer::DisableIdleShutdown() {
785 SamplingThread::TestPeer::DisableIdleShutdown();
786 }
787
788 // static
PerformSamplingThreadIdleShutdown(bool simulate_intervening_start)789 void StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown(
790 bool simulate_intervening_start) {
791 SamplingThread::TestPeer::ShutdownAssumingIdle(simulate_intervening_start);
792 }
793
794 // static
GetNextSampleTime(TimeTicks scheduled_current_sample_time,TimeDelta sampling_interval,TimeTicks now)795 TimeTicks StackSamplingProfiler::TestPeer::GetNextSampleTime(
796 TimeTicks scheduled_current_sample_time,
797 TimeDelta sampling_interval,
798 TimeTicks now) {
799 return GetNextSampleTimeImpl(scheduled_current_sample_time, sampling_interval,
800 now);
801 }
802
803 // static
804 // The profiler is currently supported for Windows x64, macOS, iOS 64-bit,
805 // Android ARM32 and ARM64, and ChromeOS x64 and ARM64.
IsSupportedForCurrentPlatform()806 bool StackSamplingProfiler::IsSupportedForCurrentPlatform() {
807 #if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) || BUILDFLAG(IS_MAC) || \
808 (BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) || \
809 (BUILDFLAG(IS_ANDROID) && \
810 ((defined(ARCH_CPU_ARMEL) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)) || \
811 (defined(ARCH_CPU_ARM64) && \
812 BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)))) || \
813 (BUILDFLAG(IS_CHROMEOS) && \
814 (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)))
815 #if BUILDFLAG(IS_WIN)
816 // Do not start the profiler when Application Verifier is in use; running them
817 // simultaneously can cause crashes and has no known use case.
818 if (GetModuleHandleA(base::win::kApplicationVerifierDllName))
819 return false;
820 // Checks if Trend Micro DLLs are loaded in process, so we can disable the
821 // profiler to avoid hitting their performance bug. See
822 // https://crbug.com/1018291 and https://crbug.com/1113832.
823 if (GetModuleHandleA("tmmon64.dll") || GetModuleHandleA("tmmonmgr64.dll"))
824 return false;
825 #endif
826 return true;
827 #else
828 return false;
829 #endif
830 }
831
StackSamplingProfiler(SamplingProfilerThreadToken thread_token,const SamplingParams & params,std::unique_ptr<ProfileBuilder> profile_builder,UnwindersFactory core_unwinders_factory,RepeatingClosure record_sample_callback,StackSamplerTestDelegate * test_delegate)832 StackSamplingProfiler::StackSamplingProfiler(
833 SamplingProfilerThreadToken thread_token,
834 const SamplingParams& params,
835 std::unique_ptr<ProfileBuilder> profile_builder,
836 UnwindersFactory core_unwinders_factory,
837 RepeatingClosure record_sample_callback,
838 StackSamplerTestDelegate* test_delegate)
839 : thread_token_(thread_token),
840 params_(params),
841 profile_builder_(std::move(profile_builder)),
842 sampler_(StackSampler::Create(thread_token,
843 profile_builder_->GetModuleCache(),
844 std::move(core_unwinders_factory),
845 std::move(record_sample_callback),
846 test_delegate)),
847 // The event starts "signaled" so code knows it's safe to start thread
848 // and "manual" so that it can be waited in multiple places.
849 profiling_inactive_(kResetPolicy, WaitableEvent::InitialState::SIGNALED),
850 profiler_id_(kNullProfilerId) {
851 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
852 "StackSamplingProfiler::StackSamplingProfiler");
853 DCHECK(profile_builder_);
854 }
855
~StackSamplingProfiler()856 StackSamplingProfiler::~StackSamplingProfiler() {
857 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
858 "StackSamplingProfiler::~StackSamplingProfiler");
859
860 // Stop returns immediately but the shutdown runs asynchronously. There is a
861 // non-zero probability that one more sample will be taken after this call
862 // returns.
863 Stop();
864
865 // The behavior of sampling a thread that has exited is undefined and could
866 // cause Bad Things(tm) to occur. The safety model provided by this class is
867 // that an instance of this object is expected to live at least as long as
868 // the thread it is sampling. However, because the sampling is performed
869 // asynchronously by the SamplingThread, there is no way to guarantee this
870 // is true without waiting for it to signal that it has finished.
871 //
872 // The wait time should, at most, be only as long as it takes to collect one
873 // sample (~200us) or none at all if sampling has already completed.
874 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
875 profiling_inactive_.Wait();
876 }
877
Start()878 void StackSamplingProfiler::Start() {
879 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
880 "StackSamplingProfiler::Start");
881
882 // Multiple calls to Start() for a single StackSamplingProfiler object is not
883 // allowed. If profile_builder_ is nullptr, then Start() has been called
884 // already.
885 DCHECK(profile_builder_);
886
887 // |sampler_| will be null if sampling isn't supported on the current
888 // platform.
889 if (!sampler_)
890 return;
891
892 // The IsSignaled() check below requires that the WaitableEvent be manually
893 // reset, to avoid signaling the event in IsSignaled() itself.
894 static_assert(kResetPolicy == WaitableEvent::ResetPolicy::MANUAL,
895 "The reset policy must be set to MANUAL");
896
897 // If a previous profiling phase is still winding down, wait for it to
898 // complete. We can't use task posting for this coordination because the
899 // thread owning the profiler may not have a message loop.
900 if (!profiling_inactive_.IsSignaled())
901 profiling_inactive_.Wait();
902 profiling_inactive_.Reset();
903
904 DCHECK_EQ(kNullProfilerId, profiler_id_);
905 profiler_id_ = SamplingThread::GetInstance()->Add(
906 std::make_unique<SamplingThread::CollectionContext>(
907 thread_token_.id, params_, &profiling_inactive_, std::move(sampler_),
908 std::move(profile_builder_)));
909 DCHECK_NE(kNullProfilerId, profiler_id_);
910
911 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
912 "StackSamplingProfiler::Started", "profiler_id", profiler_id_);
913 }
914
Stop()915 void StackSamplingProfiler::Stop() {
916 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
917 "StackSamplingProfiler::Stop", "profiler_id", profiler_id_);
918
919 SamplingThread::GetInstance()->Remove(profiler_id_);
920 profiler_id_ = kNullProfilerId;
921 }
922
AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder)923 void StackSamplingProfiler::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) {
924 if (profiler_id_ == kNullProfilerId) {
925 // We haven't started sampling, and so we can add |unwinder| to the sampler
926 // directly
927 if (sampler_)
928 sampler_->AddAuxUnwinder(std::move(unwinder));
929 return;
930 }
931
932 SamplingThread::GetInstance()->AddAuxUnwinder(profiler_id_,
933 std::move(unwinder));
934 }
935
936 // static
ApplyMetadataToPastSamples(base::TimeTicks period_start,base::TimeTicks period_end,uint64_t name_hash,std::optional<int64_t> key,int64_t value,std::optional<PlatformThreadId> thread_id)937 void StackSamplingProfiler::ApplyMetadataToPastSamples(
938 base::TimeTicks period_start,
939 base::TimeTicks period_end,
940 uint64_t name_hash,
941 std::optional<int64_t> key,
942 int64_t value,
943 std::optional<PlatformThreadId> thread_id) {
944 SamplingThread::GetInstance()->ApplyMetadataToPastSamples(
945 period_start, period_end, name_hash, key, value, thread_id);
946 }
947
948 // static
AddProfileMetadata(uint64_t name_hash,int64_t key,int64_t value,std::optional<PlatformThreadId> thread_id)949 void StackSamplingProfiler::AddProfileMetadata(
950 uint64_t name_hash,
951 int64_t key,
952 int64_t value,
953 std::optional<PlatformThreadId> thread_id) {
954 SamplingThread::GetInstance()->AddProfileMetadata(name_hash, key, value,
955 thread_id);
956 }
957
958 } // namespace base
959