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/location.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/profiler/stack_sampling_profiler.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "components/metrics/metrics_hashes.h"
20 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
22 using base::StackSamplingProfiler
;
28 // Accepts and ignores the completed profiles. Used when metrics reporting is
30 void IgnoreCompletedProfiles(
31 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
34 // The protobuf expects the MD5 checksum prefix of the module name.
35 uint64
HashModuleFilename(const base::FilePath
& filename
) {
36 const base::FilePath::StringType basename
= filename
.BaseName().value();
37 // Copy the bytes in basename into a string buffer.
38 size_t basename_length_in_bytes
=
39 basename
.size() * sizeof(base::FilePath::CharType
);
40 std::string
name_bytes(basename_length_in_bytes
, '\0');
41 memcpy(&name_bytes
[0], &basename
[0], basename_length_in_bytes
);
42 return HashMetricName(name_bytes
);
45 // Transcode |sample| into |proto_sample|, using base addresses in |modules| to
46 // compute module instruction pointer offsets.
47 void CopySampleToProto(
48 const StackSamplingProfiler::Sample
& sample
,
49 const std::vector
<StackSamplingProfiler::Module
>& modules
,
50 CallStackProfile::Sample
* proto_sample
) {
51 for (const StackSamplingProfiler::Frame
& frame
: sample
) {
52 CallStackProfile::Entry
* entry
= proto_sample
->add_entry();
53 // A frame may not have a valid module. If so, we can't compute the
54 // instruction pointer offset, and we don't want to send bare pointers, so
55 // leave call_stack_entry empty.
56 if (frame
.module_index
== StackSamplingProfiler::Frame::kUnknownModuleIndex
)
59 reinterpret_cast<const char*>(frame
.instruction_pointer
) -
60 reinterpret_cast<const char*>(modules
[frame
.module_index
].base_address
);
61 DCHECK_GE(module_offset
, 0);
62 entry
->set_address(static_cast<uint64
>(module_offset
));
63 entry
->set_module_id_index(frame
.module_index
);
67 // Transcode |profile| into |proto_profile|.
68 void CopyProfileToProto(
69 const StackSamplingProfiler::CallStackProfile
& profile
,
70 CallStackProfile
* proto_profile
) {
71 if (profile
.samples
.empty())
74 if (profile
.preserve_sample_ordering
) {
75 // Collapse only consecutive repeated samples together.
76 CallStackProfile::Sample
* current_sample_proto
= nullptr;
77 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
78 if (!current_sample_proto
|| *it
!= *(it
- 1)) {
79 current_sample_proto
= proto_profile
->add_sample();
80 CopySampleToProto(*it
, profile
.modules
, current_sample_proto
);
81 current_sample_proto
->set_count(1);
83 current_sample_proto
->set_count(current_sample_proto
->count() + 1);
87 // Collapse all repeated samples together.
88 std::map
<StackSamplingProfiler::Sample
, int> sample_index
;
89 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
90 auto location
= sample_index
.find(*it
);
91 if (location
== sample_index
.end()) {
92 CallStackProfile::Sample
* sample_proto
= proto_profile
->add_sample();
93 CopySampleToProto(*it
, profile
.modules
, sample_proto
);
94 sample_proto
->set_count(1);
97 *it
, static_cast<int>(proto_profile
->sample().size()) - 1));
99 CallStackProfile::Sample
* sample_proto
=
100 proto_profile
->mutable_sample()->Mutable(location
->second
);
101 sample_proto
->set_count(sample_proto
->count() + 1);
106 for (const StackSamplingProfiler::Module
& module
: profile
.modules
) {
107 CallStackProfile::ModuleIdentifier
* module_id
=
108 proto_profile
->add_module_id();
109 module_id
->set_build_id(module
.id
);
110 module_id
->set_name_md5_prefix(HashModuleFilename(module
.filename
));
113 proto_profile
->set_profile_duration_ms(
114 profile
.profile_duration
.InMilliseconds());
115 proto_profile
->set_sampling_period_ms(
116 profile
.sampling_period
.InMilliseconds());
119 // Translates CallStackProfileMetricsProvider's trigger to the corresponding
120 // SampledProfile TriggerEvent.
121 SampledProfile::TriggerEvent
ToSampledProfileTriggerEvent(
122 CallStackProfileMetricsProvider::Trigger trigger
) {
124 case CallStackProfileMetricsProvider::UNKNOWN
:
125 return SampledProfile::UNKNOWN_TRIGGER_EVENT
;
127 case CallStackProfileMetricsProvider::PROCESS_STARTUP
:
128 return SampledProfile::PROCESS_STARTUP
;
130 case CallStackProfileMetricsProvider::JANKY_TASK
:
131 return SampledProfile::JANKY_TASK
;
133 case CallStackProfileMetricsProvider::THREAD_HUNG
:
134 return SampledProfile::THREAD_HUNG
;
138 return SampledProfile::UNKNOWN_TRIGGER_EVENT
;
143 const char CallStackProfileMetricsProvider::kFieldTrialName
[] =
145 const char CallStackProfileMetricsProvider::kReportProfilesGroupName
[] =
148 CallStackProfileMetricsProvider::CallStackProfileMetricsProvider()
149 : weak_factory_(this) {
152 CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
153 StackSamplingProfiler::SetDefaultCompletedCallback(
154 StackSamplingProfiler::CompletedCallback());
157 void CallStackProfileMetricsProvider::OnRecordingEnabled() {
158 StackSamplingProfiler::SetDefaultCompletedCallback(base::Bind(
159 &CallStackProfileMetricsProvider::ReceiveCompletedProfiles
,
160 base::ThreadTaskRunnerHandle::Get(), weak_factory_
.GetWeakPtr()));
163 void CallStackProfileMetricsProvider::OnRecordingDisabled() {
164 StackSamplingProfiler::SetDefaultCompletedCallback(
165 base::Bind(&IgnoreCompletedProfiles
));
166 pending_profiles_
.clear();
169 void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
170 ChromeUserMetricsExtension
* uma_proto
) {
171 DCHECK(IsSamplingProfilingReportingEnabled() || pending_profiles_
.empty());
172 for (const StackSamplingProfiler::CallStackProfile
& profile
:
174 SampledProfile
* sampled_profile
= uma_proto
->add_sampled_profile();
175 sampled_profile
->set_trigger_event(ToSampledProfileTriggerEvent(
176 static_cast<CallStackProfileMetricsProvider::Trigger
>(
177 profile
.user_data
)));
178 CopyProfileToProto(profile
, sampled_profile
->mutable_call_stack_profile());
180 pending_profiles_
.clear();
183 void CallStackProfileMetricsProvider::AppendSourceProfilesForTesting(
184 const std::vector
<StackSamplingProfiler::CallStackProfile
>& profiles
) {
185 AppendCompletedProfiles(profiles
);
189 bool CallStackProfileMetricsProvider::IsSamplingProfilingReportingEnabled() {
190 const std::string group_name
= base::FieldTrialList::FindFullName(
191 CallStackProfileMetricsProvider::kFieldTrialName
);
193 CallStackProfileMetricsProvider::kReportProfilesGroupName
;
197 // Posts a message back to our own thread to collect the profiles.
198 void CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
199 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
,
200 base::WeakPtr
<CallStackProfileMetricsProvider
> provider
,
201 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
202 task_runner
->PostTask(
204 base::Bind(&CallStackProfileMetricsProvider::AppendCompletedProfiles
,
205 provider
, profiles
));
208 void CallStackProfileMetricsProvider::AppendCompletedProfiles(
209 const StackSamplingProfiler::CallStackProfiles
& profiles
) {
210 // Don't bother to record profiles if reporting is not enabled.
211 if (IsSamplingProfilingReportingEnabled()) {
212 pending_profiles_
.insert(pending_profiles_
.end(), profiles
.begin(),
217 } // namespace metrics