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
3 // found in the LICENSE file.
5 #include "components/metrics/call_stack_profile_metrics_provider.h"
13 #include "base/bind.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/memory/singleton.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/profiler/stack_sampling_profiler.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/synchronization/lock.h"
22 #include "base/thread_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "components/metrics/metrics_hashes.h"
25 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
27 using base::StackSamplingProfiler
;
33 // ProfilesState --------------------------------------------------------------
35 // A set of profiles and the CallStackProfileMetricsProvider state associated
37 struct ProfilesState
{
38 ProfilesState(const CallStackProfileMetricsProvider::Params
& params
,
39 const base::StackSamplingProfiler::CallStackProfiles
& profiles
,
40 base::TimeTicks start_timestamp
);
42 // The metrics-related parameters provided to
43 // CallStackProfileMetricsProvider::GetProfilerCallback().
44 CallStackProfileMetricsProvider::Params params
;
46 // The call stack profiles collected by the profiler.
47 base::StackSamplingProfiler::CallStackProfiles profiles
;
49 // The time at which the CallStackProfileMetricsProvider became aware of the
50 // request for profiling. In particular, this is when callback was requested
51 // via CallStackProfileMetricsProvider::GetProfilerCallback(). Used to
52 // determine if collection was disabled during the collection of the profile.
53 base::TimeTicks start_timestamp
;
56 ProfilesState::ProfilesState(
57 const CallStackProfileMetricsProvider::Params
& params
,
58 const base::StackSamplingProfiler::CallStackProfiles
& profiles
,
59 base::TimeTicks start_timestamp
)
62 start_timestamp(start_timestamp
) {
65 // PendingProfiles ------------------------------------------------------------
67 // Singleton class responsible for retaining profiles received via the callback
68 // created by CallStackProfileMetricsProvider::GetProfilerCallback(). These are
69 // then sent to UMA on the invocation of
70 // CallStackProfileMetricsProvider::ProvideGeneralMetrics(). We need to store
71 // the profiles outside of a CallStackProfileMetricsProvider instance since
72 // callers may start profiling before the CallStackProfileMetricsProvider is
75 // Member functions on this class may be called on any thread.
76 class PendingProfiles
{
78 static PendingProfiles
* GetInstance();
81 void Swap(std::vector
<ProfilesState
>* profiles
);
83 // Enables the collection of profiles by CollectProfilesIfCollectionEnabled if
84 // |enabled| is true. Otherwise, clears current profiles and ignores profiles
85 // provided to future invocations of CollectProfilesIfCollectionEnabled.
86 void SetCollectionEnabled(bool enabled
);
88 // True if profiles are being collected.
89 bool IsCollectionEnabled() const;
91 // Adds |profile| to the list of profiles if collection is enabled.
92 void CollectProfilesIfCollectionEnabled(const ProfilesState
& profiles
);
94 // Allows testing against the initial state multiple times.
95 void ResetToDefaultStateForTesting();
98 friend struct base::DefaultSingletonTraits
<PendingProfiles
>;
103 mutable base::Lock lock_
;
105 // If true, profiles provided to CollectProfilesIfCollectionEnabled should be
106 // collected. Otherwise they will be ignored.
107 bool collection_enabled_
;
109 // The last time collection was disabled. Used to determine if collection was
110 // disabled at any point since a profile was started.
111 base::TimeTicks last_collection_disable_time_
;
113 // The set of completed profiles that should be reported.
114 std::vector
<ProfilesState
> profiles_
;
116 DISALLOW_COPY_AND_ASSIGN(PendingProfiles
);
120 PendingProfiles
* PendingProfiles::GetInstance() {
121 // Leaky for performance rather than correctness reasons.
122 return base::Singleton
<PendingProfiles
,
123 base::LeakySingletonTraits
<PendingProfiles
>>::get();
126 void PendingProfiles::Clear() {
127 base::AutoLock
scoped_lock(lock_
);
131 void PendingProfiles::Swap(std::vector
<ProfilesState
>* profiles
) {
132 base::AutoLock
scoped_lock(lock_
);
133 profiles_
.swap(*profiles
);
136 void PendingProfiles::SetCollectionEnabled(bool enabled
) {
137 base::AutoLock
scoped_lock(lock_
);
139 collection_enabled_
= enabled
;
141 if (!collection_enabled_
) {
143 last_collection_disable_time_
= base::TimeTicks::Now();
147 bool PendingProfiles::IsCollectionEnabled() const {
148 base::AutoLock
scoped_lock(lock_
);
149 return collection_enabled_
;
152 void PendingProfiles::CollectProfilesIfCollectionEnabled(
153 const ProfilesState
& profiles
) {
154 base::AutoLock
scoped_lock(lock_
);
156 // Only collect if collection is not disabled and hasn't been disabled
157 // since the start of collection for this profile.
158 if (!collection_enabled_
||
159 (!last_collection_disable_time_
.is_null() &&
160 last_collection_disable_time_
>= profiles
.start_timestamp
)) {
164 profiles_
.push_back(profiles
);
167 void PendingProfiles::ResetToDefaultStateForTesting() {
168 base::AutoLock
scoped_lock(lock_
);
170 collection_enabled_
= true;
171 last_collection_disable_time_
= base::TimeTicks();
175 // |collection_enabled_| is initialized to true to collect any profiles that are
176 // generated prior to creation of the CallStackProfileMetricsProvider. The
177 // ultimate disposition of these pre-creation collected profiles will be
178 // determined by the initial recording state provided to
179 // CallStackProfileMetricsProvider.
180 PendingProfiles::PendingProfiles() : collection_enabled_(true) {}
182 PendingProfiles::~PendingProfiles() {}
184 // Functions to process completed profiles ------------------------------------
186 // Invoked on the profiler's thread. Provides the profiles to PendingProfiles to
187 // append, if the collecting state allows.
188 void ReceiveCompletedProfiles(
189 const CallStackProfileMetricsProvider::Params
& params
,
190 base::TimeTicks start_timestamp
,
191 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
192 PendingProfiles::GetInstance()->CollectProfilesIfCollectionEnabled(
193 ProfilesState(params
, profiles
, start_timestamp
));
196 // Invoked on an arbitrary thread. Ignores the provided profiles.
197 void IgnoreCompletedProfiles(
198 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
201 // Functions to encode protobufs ----------------------------------------------
203 // The protobuf expects the MD5 checksum prefix of the module name.
204 uint64
HashModuleFilename(const base::FilePath
& filename
) {
205 const base::FilePath::StringType basename
= filename
.BaseName().value();
206 // Copy the bytes in basename into a string buffer.
207 size_t basename_length_in_bytes
=
208 basename
.size() * sizeof(base::FilePath::CharType
);
209 std::string
name_bytes(basename_length_in_bytes
, '\0');
210 memcpy(&name_bytes
[0], &basename
[0], basename_length_in_bytes
);
211 return HashMetricName(name_bytes
);
214 // Transcode |sample| into |proto_sample|, using base addresses in |modules| to
215 // compute module instruction pointer offsets.
216 void CopySampleToProto(
217 const StackSamplingProfiler::Sample
& sample
,
218 const std::vector
<StackSamplingProfiler::Module
>& modules
,
219 CallStackProfile::Sample
* proto_sample
) {
220 for (const StackSamplingProfiler::Frame
& frame
: sample
) {
221 CallStackProfile::Entry
* entry
= proto_sample
->add_entry();
222 // A frame may not have a valid module. If so, we can't compute the
223 // instruction pointer offset, and we don't want to send bare pointers, so
224 // leave call_stack_entry empty.
225 if (frame
.module_index
== StackSamplingProfiler::Frame::kUnknownModuleIndex
)
227 int64 module_offset
=
228 reinterpret_cast<const char*>(frame
.instruction_pointer
) -
229 reinterpret_cast<const char*>(modules
[frame
.module_index
].base_address
);
230 DCHECK_GE(module_offset
, 0);
231 entry
->set_address(static_cast<uint64
>(module_offset
));
232 entry
->set_module_id_index(frame
.module_index
);
236 // Transcode |profile| into |proto_profile|.
237 void CopyProfileToProto(
238 const StackSamplingProfiler::CallStackProfile
& profile
,
239 bool preserve_sample_ordering
,
240 CallStackProfile
* proto_profile
) {
241 if (profile
.samples
.empty())
244 if (preserve_sample_ordering
) {
245 // Collapse only consecutive repeated samples together.
246 CallStackProfile::Sample
* current_sample_proto
= nullptr;
247 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
248 if (!current_sample_proto
|| *it
!= *(it
- 1)) {
249 current_sample_proto
= proto_profile
->add_sample();
250 CopySampleToProto(*it
, profile
.modules
, current_sample_proto
);
251 current_sample_proto
->set_count(1);
253 current_sample_proto
->set_count(current_sample_proto
->count() + 1);
257 // Collapse all repeated samples together.
258 std::map
<StackSamplingProfiler::Sample
, int> sample_index
;
259 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
260 auto location
= sample_index
.find(*it
);
261 if (location
== sample_index
.end()) {
262 CallStackProfile::Sample
* sample_proto
= proto_profile
->add_sample();
263 CopySampleToProto(*it
, profile
.modules
, sample_proto
);
264 sample_proto
->set_count(1);
267 *it
, static_cast<int>(proto_profile
->sample().size()) - 1));
269 CallStackProfile::Sample
* sample_proto
=
270 proto_profile
->mutable_sample()->Mutable(location
->second
);
271 sample_proto
->set_count(sample_proto
->count() + 1);
276 for (const StackSamplingProfiler::Module
& module
: profile
.modules
) {
277 CallStackProfile::ModuleIdentifier
* module_id
=
278 proto_profile
->add_module_id();
279 module_id
->set_build_id(module
.id
);
280 module_id
->set_name_md5_prefix(HashModuleFilename(module
.filename
));
283 proto_profile
->set_profile_duration_ms(
284 profile
.profile_duration
.InMilliseconds());
285 proto_profile
->set_sampling_period_ms(
286 profile
.sampling_period
.InMilliseconds());
289 // Translates CallStackProfileMetricsProvider's trigger to the corresponding
290 // SampledProfile TriggerEvent.
291 SampledProfile::TriggerEvent
ToSampledProfileTriggerEvent(
292 CallStackProfileMetricsProvider::Trigger trigger
) {
294 case CallStackProfileMetricsProvider::UNKNOWN
:
295 return SampledProfile::UNKNOWN_TRIGGER_EVENT
;
297 case CallStackProfileMetricsProvider::PROCESS_STARTUP
:
298 return SampledProfile::PROCESS_STARTUP
;
300 case CallStackProfileMetricsProvider::JANKY_TASK
:
301 return SampledProfile::JANKY_TASK
;
303 case CallStackProfileMetricsProvider::THREAD_HUNG
:
304 return SampledProfile::THREAD_HUNG
;
308 return SampledProfile::UNKNOWN_TRIGGER_EVENT
;
313 // CallStackProfileMetricsProvider::Params ------------------------------------
315 CallStackProfileMetricsProvider::Params::Params(
316 CallStackProfileMetricsProvider::Trigger trigger
)
317 : Params(trigger
, false) {
320 CallStackProfileMetricsProvider::Params::Params(
321 CallStackProfileMetricsProvider::Trigger trigger
,
322 bool preserve_sample_ordering
)
324 preserve_sample_ordering(preserve_sample_ordering
) {
327 // CallStackProfileMetricsProvider --------------------------------------------
329 const char CallStackProfileMetricsProvider::kFieldTrialName
[] =
331 const char CallStackProfileMetricsProvider::kReportProfilesGroupName
[] =
334 CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {
337 CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
340 // This function can be invoked on an abitrary thread.
341 base::StackSamplingProfiler::CompletedCallback
342 CallStackProfileMetricsProvider::GetProfilerCallback(const Params
& params
) {
343 // Ignore the profiles if the collection is disabled. If the collection state
344 // changes while collecting, this will be detected by the callback and
345 // profiles will be ignored at that point.
346 if (!PendingProfiles::GetInstance()->IsCollectionEnabled())
347 return base::Bind(&IgnoreCompletedProfiles
);
349 return base::Bind(&ReceiveCompletedProfiles
, params
, base::TimeTicks::Now());
352 void CallStackProfileMetricsProvider::OnRecordingEnabled() {
353 PendingProfiles::GetInstance()->SetCollectionEnabled(true);
356 void CallStackProfileMetricsProvider::OnRecordingDisabled() {
357 PendingProfiles::GetInstance()->SetCollectionEnabled(false);
360 void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
361 ChromeUserMetricsExtension
* uma_proto
) {
362 std::vector
<ProfilesState
> pending_profiles
;
363 PendingProfiles::GetInstance()->Swap(&pending_profiles
);
365 DCHECK(IsReportingEnabledByFieldTrial() || pending_profiles
.empty());
367 for (const ProfilesState
& profiles_state
: pending_profiles
) {
368 for (const StackSamplingProfiler::CallStackProfile
& profile
:
369 profiles_state
.profiles
) {
370 SampledProfile
* sampled_profile
= uma_proto
->add_sampled_profile();
371 sampled_profile
->set_trigger_event(ToSampledProfileTriggerEvent(
372 profiles_state
.params
.trigger
));
373 CopyProfileToProto(profile
,
374 profiles_state
.params
.preserve_sample_ordering
,
375 sampled_profile
->mutable_call_stack_profile());
381 void CallStackProfileMetricsProvider::ResetStaticStateForTesting() {
382 PendingProfiles::GetInstance()->ResetToDefaultStateForTesting();
386 bool CallStackProfileMetricsProvider::IsReportingEnabledByFieldTrial() {
387 const std::string group_name
= base::FieldTrialList::FindFullName(
388 CallStackProfileMetricsProvider::kFieldTrialName
);
390 CallStackProfileMetricsProvider::kReportProfilesGroupName
;
393 } // namespace metrics