1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
base / profiler / stack_sampling_profiler.h [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
#define BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
#include <memory>
#include <optional>
#include <vector>
#include "base/base_export.h"
#include "base/functional/callback.h"
#include "base/profiler/profile_builder.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
namespace base {
class Unwinder;
class StackSampler;
class StackSamplerTestDelegate;
// StackSamplingProfiler periodically stops a thread to sample its stack, for
// the purpose of collecting information about which code paths are
// executing. This information is used in aggregate by UMA to identify hot
// and/or janky code paths.
//
// Sample StackSamplingProfiler usage:
//
// // Create and customize params as desired.
// base::StackSamplingProfiler::SamplingParams params;
//
// // Create a ProfileBuilder subclass to process the profiles.
// class ProfileBuilder : public base::ProfileBuilder {...}
//
// // Then create the profiler:
// base::StackSamplingProfiler profiler(
// GetSamplingProfilerCurrentThreadToken(),
// params,
// std::make_unique<ProfileBuilder>(...));
//
// // On Android the caller also must provide a factory function for creating
// // its core stack unwinders. See the ThreadProfiler implementation for an
// // example of how to do this.
// base::StackSamplingProfiler profiler(
// GetSamplingProfilerCurrentThreadToken(),
// params,
// std::make_unique<ProfileBuilder>(...),
// core_unwinders_factory);
//
// // Then start the profiling.
// profiler.Start();
//
// // ... work being done on the target thread here ...
//
// // Optionally stop collection before complete per params.
// profiler.Stop();
//
// The default SamplingParams causes stacks to be recorded in a single profile
// at a 10Hz interval for a total of 30 seconds. All of these parameters may be
// altered as desired.
//
// When a call stack profile is complete, or the profiler is stopped,
// ProfileBuilder's OnProfileCompleted function is called from a thread created
// by the profiler.
class BASE_EXPORT StackSamplingProfiler {
public:
// Factory for generating a set of Unwinders for use by the profiler. More
// general unwinders should appear before more specific unwinders in the
// generated vector, e.g. a system unwinder should appear before a Chrome
// unwinder. The callback will be invoked on the profiler thread.
using UnwindersFactory =
OnceCallback<std::vector<std::unique_ptr<Unwinder>>()>;
// Represents parameters that configure the sampling.
struct BASE_EXPORT SamplingParams {
// Time to delay before first samples are taken.
TimeDelta initial_delay = Milliseconds(0);
// Number of samples to record per profile.
int samples_per_profile = 300;
// Interval between samples during a sampling profile. This is the desired
// duration from the start of one sample to the start of the next sample.
TimeDelta sampling_interval = Milliseconds(100);
};
// Returns true if the profiler is supported on the current platform
// configuration.
static bool IsSupportedForCurrentPlatform();
// Creates a profiler for the the thread associated with |thread_token|,
// generated by GetSamplingProfilerCurrentThreadToken().
// |record_sample_callback| is called for each sample right before recording
// the stack sample. An optional |test_delegate| can be supplied by tests.
//
// The caller must ensure that this object gets destroyed before the thread
// exits.
StackSamplingProfiler(
SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
UnwindersFactory core_unwinders_factory,
RepeatingClosure record_sample_callback = RepeatingClosure(),
StackSamplerTestDelegate* test_delegate = nullptr);
StackSamplingProfiler(const StackSamplingProfiler&) = delete;
StackSamplingProfiler& operator=(const StackSamplingProfiler&) = delete;
// Stops any profiling currently taking place before destroying the profiler.
// This will block until profile_builder_'s OnProfileCompleted function has
// executed if profiling has started but not already finished.
~StackSamplingProfiler();
// Initializes the profiler and starts sampling. Might block on a
// WaitableEvent if this StackSamplingProfiler was previously started and
// recently stopped, while the previous profiling phase winds down.
void Start();
// Stops the profiler and any ongoing sampling. This method will return
// immediately with the profile_builder_'s OnProfileCompleted function being
// run asynchronously. At most one more stack sample will be taken after this
// method returns. Calling this function is optional; if not invoked profiling
// terminates when all the profiling samples specified in the SamplingParams
// are completed or the profiler object is destroyed, whichever occurs first.
void Stop();
// Adds an auxiliary unwinder to handle additional, non-native-code unwind
// scenarios.
void AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder);
// Test peer class. These functions are purely for internal testing of
// StackSamplingProfiler; DO NOT USE within tests outside of this directory.
// The functions are static because they interact with the sampling thread, a
// singleton used by all StackSamplingProfiler objects. The functions can
// only be called by the same thread that started the sampling.
class BASE_EXPORT TestPeer {
public:
// Resets the internal state to that of a fresh start. This is necessary
// so that tests don't inherit state from previous tests.
static void Reset();
// Returns whether the sampling thread is currently running or not.
static bool IsSamplingThreadRunning();
// Disables inherent idle-shutdown behavior.
static void DisableIdleShutdown();
// Initiates an idle shutdown task, as though the idle timer had expired,
// causing the thread to exit. There is no "idle" check so this must be
// called only when all sampling tasks have completed. This blocks until
// the task has been executed, though the actual stopping of the thread
// still happens asynchronously. Watch IsSamplingThreadRunning() to know
// when the thread has exited. If |simulate_intervening_start| is true then
// this method will make it appear to the shutdown task that a new profiler
// was started between when the idle-shutdown was initiated and when it
// runs.
static void PerformSamplingThreadIdleShutdown(
bool simulate_intervening_start);
// Provides access to the method computing the next sample time.
static TimeTicks GetNextSampleTime(TimeTicks scheduled_current_sample_time,
TimeDelta sampling_interval,
TimeTicks now);
};
private:
// SamplingThread is a separate thread used to suspend and sample stacks from
// the target thread.
class SamplingThread;
// Friend the global functions from sample_metadata.cc so that it can call
// into the function below.
friend void ApplyMetadataToPastSamplesImpl(
TimeTicks period_start,
TimeTicks period_end,
uint64_t name_hash,
std::optional<int64_t> key,
int64_t value,
std::optional<PlatformThreadId> thread_id);
friend void AddProfileMetadataImpl(uint64_t name_hash,
int64_t key,
int64_t value,
std::optional<PlatformThreadId> thread_id);
// Apply metadata to already recorded samples. See the
// ApplyMetadataToPastSamples() docs in sample_metadata.h.
static void ApplyMetadataToPastSamples(
TimeTicks period_start,
TimeTicks period_end,
uint64_t name_hash,
std::optional<int64_t> key,
int64_t value,
std::optional<PlatformThreadId> thread_id);
// Adds metadata as metadata global to the sampling profile.
static void AddProfileMetadata(uint64_t name_hash,
int64_t key,
int64_t value,
std::optional<PlatformThreadId> thread_id);
// The thread whose stack will be sampled.
SamplingProfilerThreadToken thread_token_;
const SamplingParams params_;
// Stack sampler which stops the thread and collects stack frames. The
// ownership of this object will be transferred to the sampling thread when
// thread sampling starts.
std::unique_ptr<StackSampler> sampler_;
// This starts "signaled", is reset when sampling begins, and is signaled
// when that sampling is complete and the profile builder's
// OnProfileCompleted function has executed.
WaitableEvent profiling_inactive_;
// An ID uniquely identifying this profiler to the sampling thread. This
// will be an internal "null" value when no collection has been started.
int profiler_id_;
};
} // namespace base
#endif // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_