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/logging.h"
12 #include "base/macros.h"
13 #include "base/profiler/stack_sampling_profiler.h"
14 #include "components/metrics/metrics_hashes.h"
15 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
17 using base::StackSamplingProfiler
;
23 // The protobuf expects the MD5 checksum prefix of the module name.
24 uint64
HashModuleFilename(const base::FilePath
& filename
) {
25 const base::FilePath::StringType basename
= filename
.BaseName().value();
26 // Copy the bytes in basename into a string buffer.
27 size_t basename_length_in_bytes
=
28 basename
.size() * sizeof(base::FilePath::CharType
);
29 std::string
name_bytes(basename_length_in_bytes
, '\0');
30 memcpy(&name_bytes
[0], &basename
[0], basename_length_in_bytes
);
31 return HashMetricName(name_bytes
);
34 // Transcode |sample| into |proto_sample|, using base addresses in |modules| to
35 // compute module instruction pointer offsets.
36 void CopySampleToProto(
37 const StackSamplingProfiler::Sample
& sample
,
38 const std::vector
<StackSamplingProfiler::Module
>& modules
,
39 CallStackProfile::Sample
* proto_sample
) {
40 for (const StackSamplingProfiler::Frame
& frame
: sample
) {
41 CallStackProfile::Entry
* entry
= proto_sample
->add_entry();
42 // A frame may not have a valid module. If so, we can't compute the
43 // instruction pointer offset, and we don't want to send bare pointers, so
44 // leave call_stack_entry empty.
45 if (frame
.module_index
< 0)
48 reinterpret_cast<const char*>(frame
.instruction_pointer
) -
49 reinterpret_cast<const char*>(modules
[frame
.module_index
].base_address
);
50 DCHECK_GE(module_offset
, 0);
51 entry
->set_address(static_cast<uint64
>(module_offset
));
52 entry
->set_module_id_index(frame
.module_index
);
56 // Transcode |profile| into |proto_profile|.
57 void CopyProfileToProto(
58 const StackSamplingProfiler::Profile
& profile
,
59 CallStackProfile
* proto_profile
) {
60 if (profile
.samples
.empty())
63 if (profile
.preserve_sample_ordering
) {
64 // Collapse only consecutive repeated samples together.
65 CallStackProfile::Sample
* current_sample_proto
= nullptr;
66 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
67 if (!current_sample_proto
|| *it
!= *(it
- 1)) {
68 current_sample_proto
= proto_profile
->add_sample();
69 CopySampleToProto(*it
, profile
.modules
, current_sample_proto
);
70 current_sample_proto
->set_count(1);
72 current_sample_proto
->set_count(current_sample_proto
->count() + 1);
76 // Collapse all repeated samples together.
77 std::map
<StackSamplingProfiler::Sample
, int> sample_index
;
78 for (auto it
= profile
.samples
.begin(); it
!= profile
.samples
.end(); ++it
) {
79 auto location
= sample_index
.find(*it
);
80 if (location
== sample_index
.end()) {
81 CallStackProfile::Sample
* sample_proto
= proto_profile
->add_sample();
82 CopySampleToProto(*it
, profile
.modules
, sample_proto
);
83 sample_proto
->set_count(1);
86 *it
, static_cast<int>(proto_profile
->sample().size()) - 1));
88 CallStackProfile::Sample
* sample_proto
=
89 proto_profile
->mutable_sample()->Mutable(location
->second
);
90 sample_proto
->set_count(sample_proto
->count() + 1);
95 for (const StackSamplingProfiler::Module
& module
: profile
.modules
) {
96 CallStackProfile::ModuleIdentifier
* module_id
=
97 proto_profile
->add_module_id();
98 module_id
->set_build_id(module
.id
);
99 module_id
->set_name_md5_prefix(HashModuleFilename(module
.filename
));
102 proto_profile
->set_profile_duration_ms(
103 profile
.profile_duration
.InMilliseconds());
104 proto_profile
->set_sampling_period_ms(
105 profile
.sampling_period
.InMilliseconds());
109 CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {}
111 CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {}
113 void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
114 ChromeUserMetricsExtension
* uma_proto
) {
115 std::vector
<StackSamplingProfiler::Profile
> profiles
;
116 if (!source_profiles_for_test_
.empty())
117 profiles
.swap(source_profiles_for_test_
);
119 StackSamplingProfiler::GetPendingProfiles(&profiles
);
121 for (const StackSamplingProfiler::Profile
& profile
: profiles
) {
122 CallStackProfile
* call_stack_profile
=
123 uma_proto
->add_sampled_profile()->mutable_call_stack_profile();
124 CopyProfileToProto(profile
, call_stack_profile
);
128 void CallStackProfileMetricsProvider::SetSourceProfilesForTesting(
129 const std::vector
<StackSamplingProfiler::Profile
>& profiles
) {
130 source_profiles_for_test_
= profiles
;
133 } // namespace metrics