902233ff5afbe204d097779344a895b341780eed
[platform/framework/web/chromium-efl.git] / components / metrics / call_stack_profile_builder.cc
1 // Copyright 2018 The Chromium Authors
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 <stdint.h>
8 #include <algorithm>
9 #include <iterator>
10 #include <map>
11 #include <memory>
12 #include <string>
13 #include <tuple>
14 #include <utility>
15
16 #include "base/check.h"
17 #include "base/files/file_path.h"
18 #include "base/logging.h"
19 #include "base/metrics/metrics_hashes.h"
20 #include "base/no_destructor.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/time/time.h"
23 #include "build/build_config.h"
24 #include "components/metrics/call_stack_profile_encoding.h"
25
26 namespace metrics {
27
28 namespace {
29
30 // Only used by child processes. This returns a unique_ptr so that it can be
31 // reset during tests.
32 std::unique_ptr<ChildCallStackProfileCollector>&
33 GetChildCallStackProfileCollector() {
34   static base::NoDestructor<std::unique_ptr<ChildCallStackProfileCollector>>
35       instance(std::make_unique<ChildCallStackProfileCollector>());
36   return *instance;
37 }
38
39 base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
40 GetBrowserProcessReceiverCallbackInstance() {
41   static base::NoDestructor<
42       base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>>
43       instance;
44   return *instance;
45 }
46
47 // Convert |filename| to its MD5 hash.
48 uint64_t HashModuleFilename(const base::FilePath& filename) {
49   const base::FilePath::StringType basename = filename.BaseName().value();
50   // Copy the bytes in basename into a string buffer.
51   size_t basename_length_in_bytes =
52       basename.size() * sizeof(base::FilePath::CharType);
53   std::string name_bytes(basename_length_in_bytes, '\0');
54   memcpy(&name_bytes[0], &basename[0], basename_length_in_bytes);
55   return base::HashMetricName(name_bytes);
56 }
57
58 }  // namespace
59
60 CallStackProfileBuilder::CallStackProfileBuilder(
61     const CallStackProfileParams& profile_params,
62     const WorkIdRecorder* work_id_recorder,
63     base::OnceClosure completed_callback)
64     : work_id_recorder_(work_id_recorder) {
65   completed_callback_ = std::move(completed_callback);
66   sampled_profile_.set_process(
67       ToExecutionContextProcess(profile_params.process));
68   sampled_profile_.set_thread(ToExecutionContextThread(profile_params.thread));
69   sampled_profile_.set_trigger_event(
70       ToSampledProfileTriggerEvent(profile_params.trigger));
71   if (!profile_params.time_offset.is_zero()) {
72     DCHECK(profile_params.time_offset.is_positive());
73     CallStackProfile* call_stack_profile =
74         sampled_profile_.mutable_call_stack_profile();
75     call_stack_profile->set_profile_time_offset_ms(
76         profile_params.time_offset.InMilliseconds());
77   }
78 }
79
80 CallStackProfileBuilder::~CallStackProfileBuilder() = default;
81
82 base::ModuleCache* CallStackProfileBuilder::GetModuleCache() {
83   return &module_cache_;
84 }
85
86 // This function is invoked on the profiler thread while the target thread is
87 // suspended so must not take any locks, including indirectly through use of
88 // heap allocation, LOG, CHECK, or DCHECK.
89 void CallStackProfileBuilder::RecordMetadata(
90     const base::MetadataRecorder::MetadataProvider& metadata_provider) {
91   if (work_id_recorder_) {
92     unsigned int work_id = work_id_recorder_->RecordWorkId();
93     // A work id of 0 indicates that the message loop has not yet started.
94     if (work_id != 0) {
95       is_continued_work_ = (last_work_id_ == work_id);
96       last_work_id_ = work_id;
97     }
98   }
99
100   metadata_.RecordMetadata(metadata_provider);
101 }
102
103 void CallStackProfileBuilder::ApplyMetadataRetrospectively(
104     base::TimeTicks period_start,
105     base::TimeTicks period_end,
106     const base::MetadataRecorder::Item& item) {
107   CHECK_LE(period_start, period_end);
108   CHECK_LE(period_end, base::TimeTicks::Now());
109
110   // We don't set metadata if the period extends before the start of the
111   // sampling, to avoid biasing against the unobserved execution. This will
112   // introduce bias due to dropping periods longer than the sampling time, but
113   // that bias is easier to reason about and account for.
114   if (period_start < profile_start_time_)
115     return;
116
117   CallStackProfile* call_stack_profile =
118       sampled_profile_.mutable_call_stack_profile();
119   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>* samples =
120       call_stack_profile->mutable_stack_sample();
121
122   CHECK_EQ(sample_timestamps_.size(), static_cast<size_t>(samples->size()));
123
124   const ptrdiff_t start_offset =
125       std::lower_bound(sample_timestamps_.begin(), sample_timestamps_.end(),
126                        period_start) -
127       sample_timestamps_.begin();
128   const ptrdiff_t end_offset =
129       std::upper_bound(sample_timestamps_.begin(), sample_timestamps_.end(),
130                        period_end) -
131       sample_timestamps_.begin();
132
133   metadata_.ApplyMetadata(item, samples->begin() + start_offset,
134                           samples->begin() + end_offset, samples,
135                           call_stack_profile->mutable_metadata_name_hash());
136 }
137
138 void CallStackProfileBuilder::AddProfileMetadata(
139     const base::MetadataRecorder::Item& item) {
140   CallStackProfile* call_stack_profile =
141       sampled_profile_.mutable_call_stack_profile();
142
143   metadata_.SetMetadata(item,
144                         call_stack_profile->mutable_profile_metadata()->Add(),
145                         call_stack_profile->mutable_metadata_name_hash());
146 }
147
148 void CallStackProfileBuilder::OnSampleCompleted(
149     std::vector<base::Frame> frames,
150     base::TimeTicks sample_timestamp) {
151   OnSampleCompleted(std::move(frames), sample_timestamp, 1, 1);
152 }
153
154 void CallStackProfileBuilder::OnSampleCompleted(
155     std::vector<base::Frame> frames,
156     base::TimeTicks sample_timestamp,
157     size_t weight,
158     size_t count) {
159   // Write CallStackProfile::Stack protobuf message.
160   CallStackProfile::Stack stack;
161
162   for (const auto& frame : frames) {
163     // The function name should never be provided in UMA profiler usage.
164     DCHECK(frame.function_name.empty());
165
166     // keep the frame information even if its module is invalid so we have
167     // visibility into how often this issue is happening on the server.
168     CallStackProfile::Location* location = stack.add_frame();
169     if (!frame.module)
170       continue;
171
172     // Dedup modules.
173     auto module_loc = module_index_.find(frame.module);
174     if (module_loc == module_index_.end()) {
175       modules_.push_back(frame.module);
176       size_t index = modules_.size() - 1;
177       module_loc = module_index_.emplace(frame.module, index).first;
178     }
179
180     // Write CallStackProfile::Location protobuf message.
181     uintptr_t instruction_pointer = frame.instruction_pointer;
182 #if BUILDFLAG(IS_IOS)
183 #if !TARGET_IPHONE_SIMULATOR
184     // Some iOS devices enable pointer authentication, which uses the
185     // higher-order bits of pointers to store a signature. Strip that signature
186     // off before computing the module_offset.
187     // TODO(crbug.com/1084272): Use the ptrauth_strip() macro once it is
188     // available.
189     instruction_pointer &= 0xFFFFFFFFF;
190 #endif  // !TARGET_IPHONE_SIMULATOR
191 #endif  // BUILDFLAG(IS_IOS)
192
193     ptrdiff_t module_offset =
194         reinterpret_cast<const char*>(instruction_pointer) -
195         reinterpret_cast<const char*>(frame.module->GetBaseAddress());
196     DCHECK_GE(module_offset, 0);
197     location->set_address(static_cast<uint64_t>(module_offset));
198     location->set_module_id_index(module_loc->second);
199   }
200
201   CallStackProfile* call_stack_profile =
202       sampled_profile_.mutable_call_stack_profile();
203
204   // Dedup Stacks.
205   auto stack_loc = stack_index_.find(&stack);
206   if (stack_loc == stack_index_.end()) {
207     *call_stack_profile->add_stack() = std::move(stack);
208     int stack_index = call_stack_profile->stack_size() - 1;
209     // It is safe to store the Stack pointer because the repeated message
210     // representation ensures pointer stability.
211     stack_loc = stack_index_
212                     .emplace(call_stack_profile->mutable_stack(stack_index),
213                              stack_index)
214                     .first;
215   }
216
217   // Write CallStackProfile::StackSample protobuf message.
218   CallStackProfile::StackSample* stack_sample_proto =
219       call_stack_profile->add_stack_sample();
220   stack_sample_proto->set_stack_index(stack_loc->second);
221   if (weight != 1)
222     stack_sample_proto->set_weight(weight);
223   if (count != 1)
224     stack_sample_proto->set_count(count);
225   if (is_continued_work_)
226     stack_sample_proto->set_continued_work(is_continued_work_);
227
228   *stack_sample_proto->mutable_metadata() = metadata_.CreateSampleMetadata(
229       call_stack_profile->mutable_metadata_name_hash());
230
231   if (profile_start_time_.is_null())
232     profile_start_time_ = sample_timestamp;
233
234   // Write timestamps to protobuf message. Currently the timestamps are only
235   // used for browser process to apply LCP tags. The browser process will clear
236   // the timestamps information once it is done with LCP tagging. Timestamps
237   // will not be included in the final profile sent to UMA.
238   const int64_t offset =
239       (sample_timestamp - profile_start_time_).InMilliseconds();
240   stack_sample_proto->set_sample_time_offset_ms(
241       base::saturated_cast<int32_t>(offset));
242
243   sample_timestamps_.push_back(sample_timestamp);
244 }
245
246 void CallStackProfileBuilder::OnProfileCompleted(
247     base::TimeDelta profile_duration,
248     base::TimeDelta sampling_period) {
249   // Build the SampledProfile protobuf message.
250   CallStackProfile* call_stack_profile =
251       sampled_profile_.mutable_call_stack_profile();
252   call_stack_profile->set_profile_duration_ms(
253       profile_duration.InMilliseconds());
254   call_stack_profile->set_sampling_period_ms(sampling_period.InMilliseconds());
255
256   // Heap profiler sets `profile_time_offset_ms` through constructor.
257   // For CPU profiles, `profile_time_offset_ms` is the time of the first
258   // sample.
259   if (!call_stack_profile->has_profile_time_offset_ms()) {
260     call_stack_profile->set_profile_time_offset_ms(
261         profile_start_time_.since_origin().InMilliseconds());
262   }
263
264   // Write CallStackProfile::ModuleIdentifier protobuf message.
265   for (const auto* module : modules_) {
266     CallStackProfile::ModuleIdentifier* module_id =
267         call_stack_profile->add_module_id();
268     module_id->set_build_id(module->GetId());
269     module_id->set_name_md5_prefix(
270         HashModuleFilename(module->GetDebugBasename()));
271   }
272   // sampled_profile_ cannot be reused after it is cleared by this function.
273   // Check we still have the information from the constructor.
274   CHECK(sampled_profile_.has_process());
275   CHECK(sampled_profile_.has_thread());
276   CHECK(sampled_profile_.has_trigger_event());
277
278   PassProfilesToMetricsProvider(profile_start_time_,
279                                 std::move(sampled_profile_));
280   // Protobuffers are in an uncertain state after moving from; clear to get
281   // back to known state.
282   sampled_profile_.Clear();
283
284   // Run the completed callback if there is one.
285   if (!completed_callback_.is_null())
286     std::move(completed_callback_).Run();
287
288   // Clear the caches.
289   stack_index_.clear();
290   module_index_.clear();
291   modules_.clear();
292   sample_timestamps_.clear();
293 }
294
295 // static
296 void CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
297     const base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
298         callback) {
299   GetBrowserProcessReceiverCallbackInstance() = callback;
300 }
301
302 // static
303 void CallStackProfileBuilder::SetParentProfileCollectorForChildProcess(
304     mojo::PendingRemote<metrics::mojom::CallStackProfileCollector>
305         browser_interface) {
306   GetChildCallStackProfileCollector()->SetParentProfileCollector(
307       std::move(browser_interface));
308 }
309
310 // static
311 void CallStackProfileBuilder::ResetChildCallStackProfileCollectorForTesting() {
312   GetChildCallStackProfileCollector() =
313       std::make_unique<ChildCallStackProfileCollector>();
314 }
315
316 void CallStackProfileBuilder::PassProfilesToMetricsProvider(
317     base::TimeTicks profile_start_time,
318     SampledProfile sampled_profile) {
319   if (sampled_profile.process() == BROWSER_PROCESS) {
320     GetBrowserProcessReceiverCallbackInstance().Run(profile_start_time,
321                                                     std::move(sampled_profile));
322   } else {
323     GetChildCallStackProfileCollector()->Collect(profile_start_time,
324                                                  std::move(sampled_profile));
325   }
326 }
327
328 bool CallStackProfileBuilder::StackComparer::operator()(
329     const CallStackProfile::Stack* stack1,
330     const CallStackProfile::Stack* stack2) const {
331   return std::lexicographical_compare(
332       stack1->frame().begin(), stack1->frame().end(), stack2->frame().begin(),
333       stack2->frame().end(),
334       [](const CallStackProfile::Location& loc1,
335          const CallStackProfile::Location& loc2) {
336         return std::make_pair(loc1.address(), loc1.module_id_index()) <
337                std::make_pair(loc2.address(), loc2.module_id_index());
338       });
339 }
340
341 }  // namespace metrics