Upload upstream chromium 94.0.4606.31
[platform/framework/web/chromium-efl.git] / components / metrics / call_stack_profile_builder.cc
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.
4
5 #include "components/metrics/call_stack_profile_builder.h"
6
7 #include <algorithm>
8 #include <iterator>
9 #include <map>
10 #include <string>
11 #include <tuple>
12 #include <utility>
13
14 #include "base/files/file_path.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/metrics/metrics_hashes.h"
18 #include "base/no_destructor.h"
19 #include "build/build_config.h"
20 #include "components/metrics/call_stack_profile_encoding.h"
21
22 namespace metrics {
23
24 namespace {
25
26 // Only used by child processes.
27 base::LazyInstance<ChildCallStackProfileCollector>::Leaky
28     g_child_call_stack_profile_collector = LAZY_INSTANCE_INITIALIZER;
29
30 base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
31 GetBrowserProcessReceiverCallbackInstance() {
32   static base::NoDestructor<
33       base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>>
34       instance;
35   return *instance;
36 }
37
38 // Convert |filename| to its MD5 hash.
39 uint64_t HashModuleFilename(const base::FilePath& filename) {
40   const base::FilePath::StringType basename = filename.BaseName().value();
41   // Copy the bytes in basename into a string buffer.
42   size_t basename_length_in_bytes =
43       basename.size() * sizeof(base::FilePath::CharType);
44   std::string name_bytes(basename_length_in_bytes, '\0');
45   memcpy(&name_bytes[0], &basename[0], basename_length_in_bytes);
46   return base::HashMetricName(name_bytes);
47 }
48
49 }  // namespace
50
51 CallStackProfileBuilder::CallStackProfileBuilder(
52     const CallStackProfileParams& profile_params,
53     const WorkIdRecorder* work_id_recorder,
54     base::OnceClosure completed_callback)
55     : work_id_recorder_(work_id_recorder) {
56   completed_callback_ = std::move(completed_callback);
57   sampled_profile_.set_process(
58       ToExecutionContextProcess(profile_params.process));
59   sampled_profile_.set_thread(ToExecutionContextThread(profile_params.thread));
60   sampled_profile_.set_trigger_event(
61       ToSampledProfileTriggerEvent(profile_params.trigger));
62 }
63
64 CallStackProfileBuilder::~CallStackProfileBuilder() = default;
65
66 base::ModuleCache* CallStackProfileBuilder::GetModuleCache() {
67   return &module_cache_;
68 }
69
70 // This function is invoked on the profiler thread while the target thread is
71 // suspended so must not take any locks, including indirectly through use of
72 // heap allocation, LOG, CHECK, or DCHECK.
73 void CallStackProfileBuilder::RecordMetadata(
74     const base::MetadataRecorder::MetadataProvider& metadata_provider) {
75   if (work_id_recorder_) {
76     unsigned int work_id = work_id_recorder_->RecordWorkId();
77     // A work id of 0 indicates that the message loop has not yet started.
78     if (work_id != 0) {
79       is_continued_work_ = (last_work_id_ == work_id);
80       last_work_id_ = work_id;
81     }
82   }
83
84   metadata_.RecordMetadata(metadata_provider);
85 }
86
87 void CallStackProfileBuilder::ApplyMetadataRetrospectively(
88     base::TimeTicks period_start,
89     base::TimeTicks period_end,
90     const base::MetadataRecorder::Item& item) {
91   DCHECK_LE(period_start, period_end);
92   DCHECK_LE(period_end, base::TimeTicks::Now());
93
94   // We don't set metadata if the period extends before the start of the
95   // sampling, to avoid biasing against the unobserved execution. This will
96   // introduce bias due to dropping periods longer than the sampling time, but
97   // that bias is easier to reason about and account for.
98   if (period_start < profile_start_time_)
99     return;
100
101   CallStackProfile* call_stack_profile =
102       sampled_profile_.mutable_call_stack_profile();
103   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>* samples =
104       call_stack_profile->mutable_stack_sample();
105
106   DCHECK_EQ(sample_timestamps_.size(), static_cast<size_t>(samples->size()));
107
108   const ptrdiff_t start_offset =
109       std::lower_bound(sample_timestamps_.begin(), sample_timestamps_.end(),
110                        period_start) -
111       sample_timestamps_.begin();
112   const ptrdiff_t end_offset =
113       std::upper_bound(sample_timestamps_.begin(), sample_timestamps_.end(),
114                        period_end) -
115       sample_timestamps_.begin();
116
117   metadata_.ApplyMetadata(item, samples->begin() + start_offset,
118                           samples->begin() + end_offset, samples,
119                           call_stack_profile->mutable_metadata_name_hash());
120 }
121
122 void CallStackProfileBuilder::OnSampleCompleted(
123     std::vector<base::Frame> frames,
124     base::TimeTicks sample_timestamp) {
125   OnSampleCompleted(std::move(frames), sample_timestamp, 1, 1);
126 }
127
128 void CallStackProfileBuilder::OnSampleCompleted(
129     std::vector<base::Frame> frames,
130     base::TimeTicks sample_timestamp,
131     size_t weight,
132     size_t count) {
133   // Write CallStackProfile::Stack protobuf message.
134   CallStackProfile::Stack stack;
135
136   for (const auto& frame : frames) {
137     // keep the frame information even if its module is invalid so we have
138     // visibility into how often this issue is happening on the server.
139     CallStackProfile::Location* location = stack.add_frame();
140     if (!frame.module)
141       continue;
142
143     // Dedup modules.
144     auto module_loc = module_index_.find(frame.module);
145     if (module_loc == module_index_.end()) {
146       modules_.push_back(frame.module);
147       size_t index = modules_.size() - 1;
148       module_loc = module_index_.emplace(frame.module, index).first;
149     }
150
151     // Write CallStackProfile::Location protobuf message.
152     uintptr_t instruction_pointer = frame.instruction_pointer;
153 #if defined(OS_IOS)
154 #if !TARGET_IPHONE_SIMULATOR
155     // Some iOS devices enable pointer authentication, which uses the
156     // higher-order bits of pointers to store a signature. Strip that signature
157     // off before computing the module_offset.
158     // TODO(crbug.com/1084272): Use the ptrauth_strip() macro once it is
159     // available.
160     instruction_pointer &= 0xFFFFFFFFF;
161 #endif  // !TARGET_IPHONE_SIMULATOR
162 #endif  // defined(OS_IOS)
163
164     ptrdiff_t module_offset =
165         reinterpret_cast<const char*>(instruction_pointer) -
166         reinterpret_cast<const char*>(frame.module->GetBaseAddress());
167     DCHECK_GE(module_offset, 0);
168     location->set_address(static_cast<uint64_t>(module_offset));
169     location->set_module_id_index(module_loc->second);
170   }
171
172   CallStackProfile* call_stack_profile =
173       sampled_profile_.mutable_call_stack_profile();
174
175   // Dedup Stacks.
176   auto stack_loc = stack_index_.find(&stack);
177   if (stack_loc == stack_index_.end()) {
178     *call_stack_profile->add_stack() = std::move(stack);
179     int stack_index = call_stack_profile->stack_size() - 1;
180     // It is safe to store the Stack pointer because the repeated message
181     // representation ensures pointer stability.
182     stack_loc = stack_index_
183                     .emplace(call_stack_profile->mutable_stack(stack_index),
184                              stack_index)
185                     .first;
186   }
187
188   // Write CallStackProfile::StackSample protobuf message.
189   CallStackProfile::StackSample* stack_sample_proto =
190       call_stack_profile->add_stack_sample();
191   stack_sample_proto->set_stack_index(stack_loc->second);
192   if (weight != 1)
193     stack_sample_proto->set_weight(weight);
194   if (count != 1)
195     stack_sample_proto->set_count(count);
196   if (is_continued_work_)
197     stack_sample_proto->set_continued_work(is_continued_work_);
198
199   *stack_sample_proto->mutable_metadata() = metadata_.CreateSampleMetadata(
200       call_stack_profile->mutable_metadata_name_hash());
201
202   if (profile_start_time_.is_null())
203     profile_start_time_ = sample_timestamp;
204
205   sample_timestamps_.push_back(sample_timestamp);
206 }
207
208 void CallStackProfileBuilder::OnProfileCompleted(
209     base::TimeDelta profile_duration,
210     base::TimeDelta sampling_period) {
211   // Build the SampledProfile protobuf message.
212   CallStackProfile* call_stack_profile =
213       sampled_profile_.mutable_call_stack_profile();
214   call_stack_profile->set_profile_duration_ms(
215       profile_duration.InMilliseconds());
216   call_stack_profile->set_sampling_period_ms(sampling_period.InMilliseconds());
217
218   // Write CallStackProfile::ModuleIdentifier protobuf message.
219   for (const auto* module : modules_) {
220     CallStackProfile::ModuleIdentifier* module_id =
221         call_stack_profile->add_module_id();
222     module_id->set_build_id(module->GetId());
223     module_id->set_name_md5_prefix(
224         HashModuleFilename(module->GetDebugBasename()));
225   }
226
227   PassProfilesToMetricsProvider(profile_start_time_,
228                                 std::move(sampled_profile_));
229
230   // Run the completed callback if there is one.
231   if (!completed_callback_.is_null())
232     std::move(completed_callback_).Run();
233
234   // Clear the caches.
235   stack_index_.clear();
236   module_index_.clear();
237   modules_.clear();
238 }
239
240 // static
241 void CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
242     const base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
243         callback) {
244   GetBrowserProcessReceiverCallbackInstance() = callback;
245 }
246
247 // static
248 void CallStackProfileBuilder::SetParentProfileCollectorForChildProcess(
249     mojo::PendingRemote<metrics::mojom::CallStackProfileCollector>
250         browser_interface) {
251   g_child_call_stack_profile_collector.Get().SetParentProfileCollector(
252       std::move(browser_interface));
253 }
254
255 void CallStackProfileBuilder::PassProfilesToMetricsProvider(
256     base::TimeTicks profile_start_time,
257     SampledProfile sampled_profile) {
258   if (sampled_profile.process() == BROWSER_PROCESS) {
259     GetBrowserProcessReceiverCallbackInstance().Run(profile_start_time,
260                                                     std::move(sampled_profile));
261   } else {
262     g_child_call_stack_profile_collector.Get()
263         .ChildCallStackProfileCollector::Collect(profile_start_time,
264                                                  std::move(sampled_profile));
265   }
266 }
267
268 bool CallStackProfileBuilder::StackComparer::operator()(
269     const CallStackProfile::Stack* stack1,
270     const CallStackProfile::Stack* stack2) const {
271   return std::lexicographical_compare(
272       stack1->frame().begin(), stack1->frame().end(), stack2->frame().begin(),
273       stack2->frame().end(),
274       [](const CallStackProfile::Location& loc1,
275          const CallStackProfile::Location& loc2) {
276         return std::make_pair(loc1.address(), loc1.module_id_index()) <
277                std::make_pair(loc2.address(), loc2.module_id_index());
278       });
279 }
280
281 }  // namespace metrics