1 // Copyright 2018 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_builder.h"
11 #include "base/files/file_path.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/metrics/metrics_hashes.h"
15 #include "base/no_destructor.h"
16 #include "base/stl_util.h"
17 #include "components/metrics/call_stack_profile_encoding.h"
23 // Only used by child processes.
24 base::LazyInstance<ChildCallStackProfileCollector>::Leaky
25 g_child_call_stack_profile_collector = LAZY_INSTANCE_INITIALIZER;
27 base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
28 GetBrowserProcessReceiverCallbackInstance() {
29 static base::NoDestructor<
30 base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>>
35 // Convert |filename| to its MD5 hash.
36 uint64_t HashModuleFilename(const base::FilePath& filename) {
37 const base::FilePath::StringType basename = filename.BaseName().value();
38 // Copy the bytes in basename into a string buffer.
39 size_t basename_length_in_bytes =
40 basename.size() * sizeof(base::FilePath::CharType);
41 std::string name_bytes(basename_length_in_bytes, '\0');
42 memcpy(&name_bytes[0], &basename[0], basename_length_in_bytes);
43 return base::HashMetricName(name_bytes);
48 CallStackProfileBuilder::CallStackProfileBuilder(
49 const CallStackProfileParams& profile_params,
50 base::OnceClosure completed_callback)
51 : profile_start_time_(base::TimeTicks::Now()) {
52 completed_callback_ = std::move(completed_callback);
53 sampled_profile_.set_process(
54 ToExecutionContextProcess(profile_params.process));
55 sampled_profile_.set_thread(ToExecutionContextThread(profile_params.thread));
56 sampled_profile_.set_trigger_event(
57 ToSampledProfileTriggerEvent(profile_params.trigger));
60 CallStackProfileBuilder::~CallStackProfileBuilder() = default;
63 void CallStackProfileBuilder::OnSampleCompleted(
64 std::vector<base::StackSamplingProfiler::Frame> frames) {
65 OnSampleCompleted(std::move(frames), 1);
68 // TODO(chengx): record |count| as per-Stacksample metadata in the new proto
70 void CallStackProfileBuilder::OnSampleCompleted(
71 std::vector<base::StackSamplingProfiler::Frame> frames,
73 // Write CallStackProfile::Stack protobuf message.
74 CallStackProfile::Stack stack;
76 for (const auto& frame : frames) {
77 // keep the frame information even if its module is invalid so we have
78 // visibility into how often this issue is happening on the server.
79 CallStackProfile::Location* location = stack.add_frame();
80 if (!frame.module.is_valid)
84 const base::ModuleCache::Module& module = frame.module;
85 auto module_loc = module_index_.find(module.base_address);
86 if (module_loc == module_index_.end()) {
87 modules_.push_back(module);
88 size_t index = modules_.size() - 1;
89 module_loc = module_index_.emplace(module.base_address, index).first;
92 // Write CallStackProfile::Location protobuf message.
93 ptrdiff_t module_offset =
94 reinterpret_cast<const char*>(frame.instruction_pointer) -
95 reinterpret_cast<const char*>(module.base_address);
96 DCHECK_GE(module_offset, 0);
97 location->set_address(static_cast<uint64_t>(module_offset));
98 location->set_module_id_index(module_loc->second);
101 CallStackProfile* call_stack_profile =
102 sampled_profile_.mutable_call_stack_profile();
105 auto stack_loc = stack_index_.find(&stack);
106 if (stack_loc == stack_index_.end()) {
107 *call_stack_profile->add_stack() = std::move(stack);
108 int stack_index = call_stack_profile->stack_size() - 1;
109 // It is safe to store the Stack pointer because the repeated message
110 // representation ensures pointer stability.
111 stack_loc = stack_index_
112 .emplace(call_stack_profile->mutable_stack(stack_index),
117 // Write CallStackProfile::StackSample protobuf message.
118 CallStackProfile::StackSample* stack_sample_proto =
119 call_stack_profile->add_stack_sample();
120 stack_sample_proto->set_stack_index(stack_loc->second);
123 void CallStackProfileBuilder::OnProfileCompleted(
124 base::TimeDelta profile_duration,
125 base::TimeDelta sampling_period) {
126 // Build the SampledProfile protobuf message.
127 CallStackProfile* call_stack_profile =
128 sampled_profile_.mutable_call_stack_profile();
129 call_stack_profile->set_profile_duration_ms(
130 profile_duration.InMilliseconds());
131 call_stack_profile->set_sampling_period_ms(sampling_period.InMilliseconds());
133 // Write CallStackProfile::ModuleIdentifier protobuf message.
134 for (const auto& module : modules_) {
135 CallStackProfile::ModuleIdentifier* module_id =
136 call_stack_profile->add_module_id();
137 module_id->set_build_id(module.id);
138 module_id->set_name_md5_prefix(HashModuleFilename(module.filename));
141 PassProfilesToMetricsProvider(std::move(sampled_profile_));
143 // Run the completed callback if there is one.
144 if (!completed_callback_.is_null())
145 std::move(completed_callback_).Run();
148 stack_index_.clear();
149 module_index_.clear();
154 void CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
155 const base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
157 GetBrowserProcessReceiverCallbackInstance() = callback;
161 void CallStackProfileBuilder::SetParentProfileCollectorForChildProcess(
162 metrics::mojom::CallStackProfileCollectorPtr browser_interface) {
163 g_child_call_stack_profile_collector.Get().SetParentProfileCollector(
164 std::move(browser_interface));
167 void CallStackProfileBuilder::PassProfilesToMetricsProvider(
168 SampledProfile sampled_profile) {
169 if (sampled_profile.process() == BROWSER_PROCESS) {
170 GetBrowserProcessReceiverCallbackInstance().Run(profile_start_time_,
171 std::move(sampled_profile));
173 g_child_call_stack_profile_collector.Get()
174 .ChildCallStackProfileCollector::Collect(profile_start_time_,
175 std::move(sampled_profile));
179 bool CallStackProfileBuilder::StackComparer::operator()(
180 const CallStackProfile::Stack* stack1,
181 const CallStackProfile::Stack* stack2) const {
182 return std::lexicographical_compare(
183 stack1->frame().begin(), stack1->frame().end(), stack2->frame().begin(),
184 stack2->frame().end(),
185 [](const CallStackProfile::Location& loc1,
186 const CallStackProfile::Location& loc2) {
187 return std::make_pair(loc1.address(), loc1.module_id_index()) <
188 std::make_pair(loc2.address(), loc2.module_id_index());
192 } // namespace metrics