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"
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/profiler/stack_sampling_profiler.h"
17 #include "components/metrics/metrics_hashes.h"
18 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
20 using base::StackSamplingProfiler
;
26 // Accepts and ignores the completed profiles. Used when metrics reporting is
28 void IgnoreCompletedProfiles(
29 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
32 // The protobuf expects the MD5 checksum prefix of the module name.
33 uint64
HashModuleFilename(const base::FilePath
& filename
) {
34 const base::FilePath::StringType basename
= filename
.BaseName().value();
35 // Copy the bytes in basename into a string buffer.
36 size_t basename_length_in_bytes
=
37 basename
.size() * sizeof(base::FilePath::CharType
);
38 std::string
name_bytes(basename_length_in_bytes
, '\0');
39 memcpy(&name_bytes
[0], &basename
[0], basename_length_in_bytes
);
40 return HashMetricName(name_bytes
);
43 // Transcode |sample| into |proto_sample|, using base addresses in |modules| to
44 // compute module instruction pointer offsets.
45 void CopySampleToProto(
46 const StackSamplingProfiler::Sample
& sample
,
47 const std::vector
<StackSamplingProfiler::Module
>& modules
,
48 CallStackProfile::Sample
* proto_sample
) {
49 for (const StackSamplingProfiler::Frame
& frame
: sample
) {
50 CallStackProfile::Entry
* entry
= proto_sample
->add_entry();
51 // A frame may not have a valid module. If so, we can't compute the
52 // instruction pointer offset, and we don't want to send bare pointers, so
53 // leave call_stack_entry empty.
54 if (frame
.module_index
== StackSamplingProfiler::Frame::kUnknownModuleIndex
)
57 reinterpret_cast<const char*>(frame
.instruction_pointer
) -
58 reinterpret_cast<const char*>(modules
[frame
.module_index
].base_address
);
59 DCHECK_GE(module_offset
, 0);
60 entry
->set_address(static_cast<uint64
>(module_offset
));
61 entry
->set_module_id_index(frame
.module_index
);
65 // Transcode |profile| into |proto_profile|.
66 void CopyProfileToProto(
67 const StackSamplingProfiler::CallStackProfile
& profile
,
68 CallStackProfile
* proto_profile
) {
69 if (profile
.samples
.empty())
72 if (profile
.preserve_sample_ordering
) {
73 // Collapse only consecutive repeated samples together.
74 CallStackProfile::Sample
* current_sample_proto
= nullptr;
75 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
76 if (!current_sample_proto
|| *it
!= *(it
- 1)) {
77 current_sample_proto
= proto_profile
->add_sample();
78 CopySampleToProto(*it
, profile
.modules
, current_sample_proto
);
79 current_sample_proto
->set_count(1);
81 current_sample_proto
->set_count(current_sample_proto
->count() + 1);
85 // Collapse all repeated samples together.
86 std::map
<StackSamplingProfiler::Sample
, int> sample_index
;
87 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
88 auto location
= sample_index
.find(*it
);
89 if (location
== sample_index
.end()) {
90 CallStackProfile::Sample
* sample_proto
= proto_profile
->add_sample();
91 CopySampleToProto(*it
, profile
.modules
, sample_proto
);
92 sample_proto
->set_count(1);
95 *it
, static_cast<int>(proto_profile
->sample().size()) - 1));
97 CallStackProfile::Sample
* sample_proto
=
98 proto_profile
->mutable_sample()->Mutable(location
->second
);
99 sample_proto
->set_count(sample_proto
->count() + 1);
104 for (const StackSamplingProfiler::Module
& module
: profile
.modules
) {
105 CallStackProfile::ModuleIdentifier
* module_id
=
106 proto_profile
->add_module_id();
107 module_id
->set_build_id(module
.id
);
108 module_id
->set_name_md5_prefix(HashModuleFilename(module
.filename
));
111 proto_profile
->set_profile_duration_ms(
112 profile
.profile_duration
.InMilliseconds());
113 proto_profile
->set_sampling_period_ms(
114 profile
.sampling_period
.InMilliseconds());
117 // Translates CallStackProfileMetricsProvider's trigger to the corresponding
118 // SampledProfile TriggerEvent.
119 SampledProfile::TriggerEvent
ToSampledProfileTriggerEvent(
120 CallStackProfileMetricsProvider::Trigger trigger
) {
122 case CallStackProfileMetricsProvider::UNKNOWN
:
123 return SampledProfile::UNKNOWN_TRIGGER_EVENT
;
125 case CallStackProfileMetricsProvider::PROCESS_STARTUP
:
126 return SampledProfile::PROCESS_STARTUP
;
128 case CallStackProfileMetricsProvider::JANKY_TASK
:
129 return SampledProfile::JANKY_TASK
;
131 case CallStackProfileMetricsProvider::THREAD_HUNG
:
132 return SampledProfile::THREAD_HUNG
;
136 return SampledProfile::UNKNOWN_TRIGGER_EVENT
;
141 const char CallStackProfileMetricsProvider::kFieldTrialName
[] =
143 const char CallStackProfileMetricsProvider::kReportProfilesGroupName
[] =
146 CallStackProfileMetricsProvider::CallStackProfileMetricsProvider()
147 : weak_factory_(this) {
150 CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
151 StackSamplingProfiler::SetDefaultCompletedCallback(
152 StackSamplingProfiler::CompletedCallback());
155 void CallStackProfileMetricsProvider::OnRecordingEnabled() {
156 StackSamplingProfiler::SetDefaultCompletedCallback(
157 base::Bind(&CallStackProfileMetricsProvider::ReceiveCompletedProfiles
,
158 base::MessageLoopProxy::current(),
159 weak_factory_
.GetWeakPtr()));
162 void CallStackProfileMetricsProvider::OnRecordingDisabled() {
163 StackSamplingProfiler::SetDefaultCompletedCallback(
164 base::Bind(&IgnoreCompletedProfiles
));
165 pending_profiles_
.clear();
168 void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
169 ChromeUserMetricsExtension
* uma_proto
) {
170 DCHECK(IsSamplingProfilingReportingEnabled() || pending_profiles_
.empty());
171 for (const StackSamplingProfiler::CallStackProfile
& profile
:
173 SampledProfile
* sampled_profile
= uma_proto
->add_sampled_profile();
174 sampled_profile
->set_trigger_event(ToSampledProfileTriggerEvent(
175 static_cast<CallStackProfileMetricsProvider::Trigger
>(
176 profile
.user_data
)));
177 CopyProfileToProto(profile
, sampled_profile
->mutable_call_stack_profile());
179 pending_profiles_
.clear();
182 void CallStackProfileMetricsProvider::AppendSourceProfilesForTesting(
183 const std::vector
<StackSamplingProfiler::CallStackProfile
>& profiles
) {
184 AppendCompletedProfiles(profiles
);
188 bool CallStackProfileMetricsProvider::IsSamplingProfilingReportingEnabled() {
189 const std::string group_name
= base::FieldTrialList::FindFullName(
190 CallStackProfileMetricsProvider::kFieldTrialName
);
192 CallStackProfileMetricsProvider::kReportProfilesGroupName
;
196 // Posts a message back to our own thread to collect the profiles.
197 void CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
198 scoped_refptr
<base::MessageLoopProxy
> message_loop
,
199 base::WeakPtr
<CallStackProfileMetricsProvider
> provider
,
200 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
201 message_loop
->PostTask(
203 base::Bind(&CallStackProfileMetricsProvider::AppendCompletedProfiles
,
204 provider
, profiles
));
207 void CallStackProfileMetricsProvider::AppendCompletedProfiles(
208 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
209 // Don't bother to record profiles if reporting is not enabled.
210 if (IsSamplingProfilingReportingEnabled()) {
211 pending_profiles_
.insert(pending_profiles_
.end(), profiles
.begin(),
216 } // namespace metrics