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_stacks/call_stack_profile_builder.h"
6 
7 #include <memory>
8 
9 #include "base/files/file_path.h"
10 #include "base/functional/callback.h"
11 #include "base/profiler/module_cache.h"
12 #include "base/profiler/stack_sampling_profiler_test_util.h"
13 #include "base/test/bind.h"
14 #include "base/test/mock_callback.h"
15 #include "base/time/time.h"
16 #include "build/build_config.h"
17 #include "components/metrics/call_stacks/call_stack_profile_params.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/metrics_proto/sampled_profile.pb.h"
20 
21 namespace metrics {
22 
23 namespace {
24 
25 constexpr CallStackProfileParams kProfileParams = {
26     CallStackProfileParams::Process::kBrowser,
27     CallStackProfileParams::Thread::kMain,
28     CallStackProfileParams::Trigger::kProcessStartup};
29 
30 class TestingCallStackProfileBuilder : public CallStackProfileBuilder {
31  public:
32   TestingCallStackProfileBuilder(
33       const CallStackProfileParams& profile_params,
34       const WorkIdRecorder* work_id_recorder = nullptr,
35       base::OnceClosure completed_callback = base::OnceClosure());
36 
37   ~TestingCallStackProfileBuilder() override;
38 
test_profile_start_time() const39   base::TimeTicks test_profile_start_time() const {
40     return test_profile_start_time_;
41   }
42 
test_sampled_profile() const43   const SampledProfile& test_sampled_profile() const {
44     return test_sampled_profile_;
45   }
46 
47  protected:
48   // Overridden for testing.
49   void PassProfilesToMetricsProvider(base::TimeTicks profile_start_time,
50                                      SampledProfile sampled_profile) override;
51 
52  private:
53   // The start time and completed profile.
54   base::TimeTicks test_profile_start_time_;
55   SampledProfile test_sampled_profile_;
56 };
57 
TestingCallStackProfileBuilder(const CallStackProfileParams & profile_params,const WorkIdRecorder * work_id_recorder,base::OnceClosure completed_callback)58 TestingCallStackProfileBuilder::TestingCallStackProfileBuilder(
59     const CallStackProfileParams& profile_params,
60     const WorkIdRecorder* work_id_recorder,
61     base::OnceClosure completed_callback)
62     : CallStackProfileBuilder(profile_params,
63                               work_id_recorder,
64                               std::move(completed_callback)) {}
65 
66 TestingCallStackProfileBuilder::~TestingCallStackProfileBuilder() = default;
67 
PassProfilesToMetricsProvider(base::TimeTicks profile_start_time,SampledProfile sampled_profile)68 void TestingCallStackProfileBuilder::PassProfilesToMetricsProvider(
69     base::TimeTicks profile_start_time,
70     SampledProfile sampled_profile) {
71   test_profile_start_time_ = profile_start_time;
72   test_sampled_profile_ = std::move(sampled_profile);
73 }
74 
75 }  // namespace
76 
TEST(CallStackProfileBuilderTest,ProfilingCompleted)77 TEST(CallStackProfileBuilderTest, ProfilingCompleted) {
78   // Set up a mock completed callback which will be run once.
79   base::MockCallback<base::OnceClosure> mock_closure;
80   EXPECT_CALL(mock_closure, Run()).Times(1);
81 
82   auto profile_builder = std::make_unique<TestingCallStackProfileBuilder>(
83       kProfileParams, nullptr, mock_closure.Get());
84   base::MetadataRecorder metadata_recorder;
85 
86 #if BUILDFLAG(IS_WIN)
87   uint64_t module_md5 = 0x46C3E4166659AC02ULL;
88   base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
89 #else
90   uint64_t module_md5 = 0x554838A8451AC36CULL;
91   base::FilePath module_path("/some/path/to/chrome");
92 #endif
93 
94   const uintptr_t module_base_address1 = 0x1000;
95   base::TestModule module1(module_base_address1);
96   module1.set_id("1");
97   module1.set_debug_basename(module_path);
98   base::Frame frame1 = {module_base_address1 + 0x10, &module1};
99 
100   const uintptr_t module_base_address2 = 0x1100;
101   base::TestModule module2(module_base_address2);
102   module2.set_id("2");
103   module2.set_debug_basename(module_path);
104   base::Frame frame2 = {module_base_address2 + 0x10, &module2};
105 
106   const uintptr_t module_base_address3 = 0x1010;
107   base::TestModule module3(module_base_address3);
108   module3.set_id("3");
109   module3.set_debug_basename(module_path);
110   base::Frame frame3 = {module_base_address3 + 0x10, &module3};
111 
112   std::vector<base::Frame> frames1 = {frame1, frame2};
113   std::vector<base::Frame> frames2 = {frame3};
114 
115   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
116       &metadata_recorder, base::PlatformThread::CurrentId()));
117   profile_builder->OnSampleCompleted(frames1, base::TimeTicks());
118   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
119       &metadata_recorder, base::PlatformThread::CurrentId()));
120   profile_builder->OnSampleCompleted(frames2, base::TimeTicks());
121   profile_builder->OnProfileCompleted(base::Milliseconds(500),
122                                       base::Milliseconds(100));
123 
124   const SampledProfile& proto = profile_builder->test_sampled_profile();
125 
126   ASSERT_TRUE(proto.has_process());
127   ASSERT_EQ(BROWSER_PROCESS, proto.process());
128   ASSERT_TRUE(proto.has_thread());
129   ASSERT_EQ(MAIN_THREAD, proto.thread());
130   ASSERT_TRUE(proto.has_trigger_event());
131   ASSERT_EQ(SampledProfile::PROCESS_STARTUP, proto.trigger_event());
132 
133   ASSERT_TRUE(proto.has_call_stack_profile());
134   const CallStackProfile& profile = proto.call_stack_profile();
135 
136   ASSERT_EQ(2, profile.stack_size());
137   ASSERT_EQ(2, profile.stack(0).frame_size());
138   ASSERT_TRUE(profile.stack(0).frame(0).has_module_id_index());
139   EXPECT_EQ(0, profile.stack(0).frame(0).module_id_index());
140   ASSERT_TRUE(profile.stack(0).frame(1).has_module_id_index());
141   EXPECT_EQ(1, profile.stack(0).frame(1).module_id_index());
142   ASSERT_EQ(1, profile.stack(1).frame_size());
143   ASSERT_TRUE(profile.stack(1).frame(0).has_module_id_index());
144   EXPECT_EQ(2, profile.stack(1).frame(0).module_id_index());
145 
146   ASSERT_EQ(3, profile.module_id().size());
147   ASSERT_TRUE(profile.module_id(0).has_build_id());
148   EXPECT_EQ("1", profile.module_id(0).build_id());
149   ASSERT_TRUE(profile.module_id(0).has_name_md5_prefix());
150   EXPECT_EQ(module_md5, profile.module_id(0).name_md5_prefix());
151   ASSERT_TRUE(profile.module_id(1).has_build_id());
152   EXPECT_EQ("2", profile.module_id(1).build_id());
153   ASSERT_TRUE(profile.module_id(1).has_name_md5_prefix());
154   EXPECT_EQ(module_md5, profile.module_id(1).name_md5_prefix());
155   ASSERT_TRUE(profile.module_id(2).has_build_id());
156   EXPECT_EQ("3", profile.module_id(2).build_id());
157   ASSERT_TRUE(profile.module_id(2).has_name_md5_prefix());
158   EXPECT_EQ(module_md5, profile.module_id(2).name_md5_prefix());
159 
160   ASSERT_EQ(2, profile.stack_sample_size());
161   EXPECT_EQ(0, profile.stack_sample(0).stack_index());
162   EXPECT_FALSE(profile.stack_sample(0).has_continued_work());
163   EXPECT_FALSE(profile.stack_sample(0).has_weight());
164   EXPECT_EQ(1, profile.stack_sample(1).stack_index());
165   EXPECT_FALSE(profile.stack_sample(1).has_continued_work());
166   EXPECT_FALSE(profile.stack_sample(1).has_weight());
167 
168   ASSERT_TRUE(profile.has_profile_duration_ms());
169   EXPECT_EQ(500, profile.profile_duration_ms());
170   ASSERT_TRUE(profile.has_sampling_period_ms());
171   EXPECT_EQ(100, profile.sampling_period_ms());
172 }
173 
TEST(CallStackProfileBuilderTest,CustomWeightsAndCounts)174 TEST(CallStackProfileBuilderTest, CustomWeightsAndCounts) {
175   auto profile_builder =
176       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
177 
178   base::TestModule module1;
179   base::Frame frame1 = {0x10, &module1};
180   std::vector<base::Frame> frames = {frame1};
181 
182   profile_builder->OnSampleCompleted(frames, base::TimeTicks(), 42, 3);
183   profile_builder->OnSampleCompleted(frames, base::TimeTicks(), 1, 1);
184   profile_builder->OnSampleCompleted(frames, base::TimeTicks());
185   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
186 
187   const SampledProfile& proto = profile_builder->test_sampled_profile();
188 
189   ASSERT_TRUE(proto.has_call_stack_profile());
190   const CallStackProfile& profile = proto.call_stack_profile();
191   ASSERT_EQ(3, profile.stack_sample_size());
192   EXPECT_TRUE(profile.stack_sample(0).has_weight());
193   EXPECT_TRUE(profile.stack_sample(0).has_count());
194   EXPECT_EQ(42, profile.stack_sample(0).weight());
195   EXPECT_EQ(3, profile.stack_sample(0).count());
196   EXPECT_FALSE(profile.stack_sample(1).has_weight());
197   EXPECT_FALSE(profile.stack_sample(1).has_count());
198   EXPECT_FALSE(profile.stack_sample(2).has_weight());
199   EXPECT_FALSE(profile.stack_sample(2).has_count());
200 }
201 
TEST(CallStackProfileBuilderTest,StacksDeduped)202 TEST(CallStackProfileBuilderTest, StacksDeduped) {
203   auto profile_builder =
204       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
205   base::MetadataRecorder metadata_recorder;
206 
207   base::TestModule module1;
208   base::Frame frame1 = {0x10, &module1};
209 
210   base::TestModule module2;
211   base::Frame frame2 = {0x20, &module2};
212 
213   std::vector<base::Frame> frames = {frame1, frame2};
214 
215   // Two stacks are completed with the same frames therefore they are deduped
216   // to one.
217   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
218       &metadata_recorder, base::PlatformThread::CurrentId()));
219   profile_builder->OnSampleCompleted(frames, base::TimeTicks());
220   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
221       &metadata_recorder, base::PlatformThread::CurrentId()));
222   profile_builder->OnSampleCompleted(frames, base::TimeTicks());
223 
224   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
225 
226   const SampledProfile& proto = profile_builder->test_sampled_profile();
227 
228   ASSERT_TRUE(proto.has_process());
229   ASSERT_EQ(BROWSER_PROCESS, proto.process());
230   ASSERT_TRUE(proto.has_thread());
231   ASSERT_EQ(MAIN_THREAD, proto.thread());
232   ASSERT_TRUE(proto.has_trigger_event());
233   ASSERT_EQ(SampledProfile::PROCESS_STARTUP, proto.trigger_event());
234 
235   ASSERT_TRUE(proto.has_call_stack_profile());
236   const CallStackProfile& profile = proto.call_stack_profile();
237   ASSERT_EQ(1, profile.stack_size());
238   ASSERT_EQ(2, profile.stack_sample_size());
239   EXPECT_EQ(0, profile.stack_sample(0).stack_index());
240   EXPECT_EQ(0, profile.stack_sample(1).stack_index());
241 }
242 
TEST(CallStackProfileBuilderTest,StacksNotDeduped)243 TEST(CallStackProfileBuilderTest, StacksNotDeduped) {
244   auto profile_builder =
245       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
246   base::MetadataRecorder metadata_recorder;
247 
248   base::TestModule module1;
249   base::Frame frame1 = {0x10, &module1};
250 
251   base::TestModule module2;
252   base::Frame frame2 = {0x20, &module2};
253 
254   std::vector<base::Frame> frames1 = {frame1};
255   std::vector<base::Frame> frames2 = {frame2};
256 
257   // Two stacks are completed with the different frames therefore not deduped.
258   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
259       &metadata_recorder, base::PlatformThread::CurrentId()));
260   profile_builder->OnSampleCompleted(frames1, base::TimeTicks());
261   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
262       &metadata_recorder, base::PlatformThread::CurrentId()));
263   profile_builder->OnSampleCompleted(frames2, base::TimeTicks());
264 
265   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
266 
267   const SampledProfile& proto = profile_builder->test_sampled_profile();
268 
269   ASSERT_TRUE(proto.has_process());
270   ASSERT_EQ(BROWSER_PROCESS, proto.process());
271   ASSERT_TRUE(proto.has_thread());
272   ASSERT_EQ(MAIN_THREAD, proto.thread());
273   ASSERT_TRUE(proto.has_trigger_event());
274   ASSERT_EQ(SampledProfile::PROCESS_STARTUP, proto.trigger_event());
275 
276   ASSERT_TRUE(proto.has_call_stack_profile());
277   const CallStackProfile& profile = proto.call_stack_profile();
278   ASSERT_EQ(2, profile.stack_size());
279   ASSERT_EQ(2, profile.stack_sample_size());
280   EXPECT_EQ(0, profile.stack_sample(0).stack_index());
281   EXPECT_EQ(1, profile.stack_sample(1).stack_index());
282 }
283 
TEST(CallStackProfileBuilderTest,Modules)284 TEST(CallStackProfileBuilderTest, Modules) {
285   auto profile_builder =
286       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
287   base::MetadataRecorder metadata_recorder;
288 
289   // A frame with no module.
290   base::Frame frame1 = {0x1010, nullptr};
291 
292   const uintptr_t module_base_address2 = 0x1100;
293 #if BUILDFLAG(IS_WIN)
294   uint64_t module_md5 = 0x46C3E4166659AC02ULL;
295   base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
296 #else
297   uint64_t module_md5 = 0x554838A8451AC36CULL;
298   base::FilePath module_path("/some/path/to/chrome");
299 #endif
300   base::TestModule module2(module_base_address2);
301   module2.set_id("2");
302   module2.set_debug_basename(module_path);
303   base::Frame frame2 = {module_base_address2 + 0x10, &module2};
304 
305   std::vector<base::Frame> frames = {frame1, frame2};
306 
307   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
308       &metadata_recorder, base::PlatformThread::CurrentId()));
309   profile_builder->OnSampleCompleted(frames, base::TimeTicks());
310   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
311 
312   const SampledProfile& proto = profile_builder->test_sampled_profile();
313 
314   ASSERT_TRUE(proto.has_call_stack_profile());
315   const CallStackProfile& profile = proto.call_stack_profile();
316 
317   ASSERT_EQ(1, profile.stack_sample_size());
318   EXPECT_EQ(0, profile.stack_sample(0).stack_index());
319 
320   ASSERT_EQ(1, profile.stack_size());
321   ASSERT_EQ(2, profile.stack(0).frame_size());
322 
323   ASSERT_FALSE(profile.stack(0).frame(0).has_module_id_index());
324   ASSERT_FALSE(profile.stack(0).frame(0).has_address());
325 
326   ASSERT_TRUE(profile.stack(0).frame(1).has_module_id_index());
327   EXPECT_EQ(0, profile.stack(0).frame(1).module_id_index());
328   ASSERT_TRUE(profile.stack(0).frame(1).has_address());
329   EXPECT_EQ(0x10ULL, profile.stack(0).frame(1).address());
330 
331   ASSERT_EQ(1, profile.module_id().size());
332   ASSERT_TRUE(profile.module_id(0).has_build_id());
333   EXPECT_EQ("2", profile.module_id(0).build_id());
334   ASSERT_TRUE(profile.module_id(0).has_name_md5_prefix());
335   EXPECT_EQ(module_md5, profile.module_id(0).name_md5_prefix());
336 }
337 
TEST(CallStackProfileBuilderTest,DedupModules)338 TEST(CallStackProfileBuilderTest, DedupModules) {
339   auto profile_builder =
340       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
341   base::MetadataRecorder metadata_recorder;
342 
343   const uintptr_t module_base_address = 0x1000;
344 
345 #if BUILDFLAG(IS_WIN)
346   uint64_t module_md5 = 0x46C3E4166659AC02ULL;
347   base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
348 #else
349   uint64_t module_md5 = 0x554838A8451AC36CULL;
350   base::FilePath module_path("/some/path/to/chrome");
351 #endif
352 
353   base::TestModule module(module_base_address);
354   module.set_id("1");
355   module.set_debug_basename(module_path);
356   base::Frame frame1 = {module_base_address + 0x10, &module};
357   base::Frame frame2 = {module_base_address + 0x20, &module};
358 
359   std::vector<base::Frame> frames = {frame1, frame2};
360 
361   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
362       &metadata_recorder, base::PlatformThread::CurrentId()));
363   profile_builder->OnSampleCompleted(frames, base::TimeTicks());
364   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
365 
366   const SampledProfile& proto = profile_builder->test_sampled_profile();
367 
368   ASSERT_TRUE(proto.has_call_stack_profile());
369   const CallStackProfile& profile = proto.call_stack_profile();
370 
371   ASSERT_EQ(1, profile.stack_sample_size());
372   EXPECT_EQ(0, profile.stack_sample(0).stack_index());
373 
374   ASSERT_EQ(1, profile.stack_size());
375   ASSERT_EQ(2, profile.stack(0).frame_size());
376 
377   // The two frames share the same module, which should be deduped in the
378   // output.
379   ASSERT_TRUE(profile.stack(0).frame(0).has_module_id_index());
380   EXPECT_EQ(0, profile.stack(0).frame(0).module_id_index());
381   ASSERT_TRUE(profile.stack(0).frame(0).has_address());
382   EXPECT_EQ(0x10ULL, profile.stack(0).frame(0).address());
383 
384   ASSERT_TRUE(profile.stack(0).frame(1).has_module_id_index());
385   EXPECT_EQ(0, profile.stack(0).frame(1).module_id_index());
386   ASSERT_TRUE(profile.stack(0).frame(1).has_address());
387   EXPECT_EQ(0x20ULL, profile.stack(0).frame(1).address());
388 
389   ASSERT_EQ(1, profile.module_id().size());
390   ASSERT_TRUE(profile.module_id(0).has_build_id());
391   EXPECT_EQ("1", profile.module_id(0).build_id());
392   ASSERT_TRUE(profile.module_id(0).has_name_md5_prefix());
393   EXPECT_EQ(module_md5, profile.module_id(0).name_md5_prefix());
394 }
395 
TEST(CallStackProfileBuilderTest,WorkIds)396 TEST(CallStackProfileBuilderTest, WorkIds) {
397   class TestWorkIdRecorder : public WorkIdRecorder {
398    public:
399     unsigned int RecordWorkId() const override { return current_id; }
400 
401     unsigned int current_id = 0;
402   };
403 
404   TestWorkIdRecorder work_id_recorder;
405   auto profile_builder = std::make_unique<TestingCallStackProfileBuilder>(
406       kProfileParams, &work_id_recorder);
407   base::MetadataRecorder metadata_recorder;
408 
409   base::TestModule module;
410   base::Frame frame = {0x10, &module};
411 
412   // Id 0 means the message loop hasn't been started yet, so the sample should
413   // not have continued_work set.
414   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
415       &metadata_recorder, base::PlatformThread::CurrentId()));
416   profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
417 
418   // The second sample with the same id should have continued_work set.
419   work_id_recorder.current_id = 1;
420   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
421       &metadata_recorder, base::PlatformThread::CurrentId()));
422   profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
423   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
424       &metadata_recorder, base::PlatformThread::CurrentId()));
425   profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
426 
427   // Ids are in general non-contiguous across multiple samples.
428   work_id_recorder.current_id = 10;
429   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
430       &metadata_recorder, base::PlatformThread::CurrentId()));
431   profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
432   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
433       &metadata_recorder, base::PlatformThread::CurrentId()));
434   profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
435 
436   profile_builder->OnProfileCompleted(base::Milliseconds(500),
437                                       base::Milliseconds(100));
438 
439   const SampledProfile& proto = profile_builder->test_sampled_profile();
440 
441   ASSERT_TRUE(proto.has_call_stack_profile());
442   const CallStackProfile& profile = proto.call_stack_profile();
443 
444   ASSERT_EQ(5, profile.stack_sample_size());
445   EXPECT_FALSE(profile.stack_sample(0).has_continued_work());
446   EXPECT_FALSE(profile.stack_sample(1).has_continued_work());
447   EXPECT_TRUE(profile.stack_sample(2).continued_work());
448   EXPECT_FALSE(profile.stack_sample(3).has_continued_work());
449   EXPECT_TRUE(profile.stack_sample(4).continued_work());
450 }
451 
TEST(CallStackProfileBuilderTest,ProfileStartTime)452 TEST(CallStackProfileBuilderTest, ProfileStartTime) {
453   auto profile_builder =
454       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
455 
456   base::TestModule module;
457   const base::Frame frame = {0x10, &module};
458   const base::TimeTicks first_sample_time = base::TimeTicks::UnixEpoch();
459 
460   profile_builder->OnSampleCompleted({frame}, first_sample_time);
461   profile_builder->OnSampleCompleted({frame},
462                                      first_sample_time + base::Seconds(1));
463   profile_builder->OnProfileCompleted(base::Seconds(1), base::Seconds(1));
464 
465   EXPECT_EQ(first_sample_time, profile_builder->test_profile_start_time());
466 }
467 
468 // A basic test of RecordMetadata at the level of the
469 // CallStackProfileBuilder. The underlying implementation in
470 // CallStackProfileMetadata is tested independently.
TEST(CallStackProfileBuilderTest,RecordMetadata)471 TEST(CallStackProfileBuilderTest, RecordMetadata) {
472   base::MetadataRecorder metadata_recorder;
473   auto profile_builder =
474       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
475 
476   base::TestModule module;
477   base::Frame frame = {0x10, &module};
478 
479   metadata_recorder.Set(100, std::nullopt, std::nullopt, 10);
480   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
481       &metadata_recorder, base::PlatformThread::CurrentId()));
482   profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
483 
484   profile_builder->OnProfileCompleted(base::Milliseconds(500),
485                                       base::Milliseconds(100));
486 
487   const SampledProfile& proto = profile_builder->test_sampled_profile();
488 
489   ASSERT_TRUE(proto.has_call_stack_profile());
490   const CallStackProfile& profile = proto.call_stack_profile();
491 
492   ASSERT_EQ(1, profile.metadata_name_hash_size());
493   EXPECT_EQ(100u, profile.metadata_name_hash(0));
494 
495   ASSERT_EQ(1, profile.stack_sample_size());
496 
497   auto sample = profile.stack_sample(0);
498   ASSERT_EQ(1, sample.metadata_size());
499   EXPECT_EQ(0, sample.metadata(0).name_hash_index());
500   EXPECT_FALSE(sample.metadata(0).has_key());
501   EXPECT_EQ(10, sample.metadata(0).value());
502 }
503 
504 // A basic test of ApplyMetadataRetrospectively at the level of the
505 // CallStackProfileBuilder. The underlying implementation in
506 // CallStackProfileMetadata is tested independently.
TEST(CallStackProfileBuilderTest,ApplyMetadataRetrospectively_Basic)507 TEST(CallStackProfileBuilderTest, ApplyMetadataRetrospectively_Basic) {
508   base::MetadataRecorder metadata_recorder;
509   auto profile_builder =
510       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
511 
512   base::TestModule module;
513   base::Frame frame = {0x10, &module};
514   base::TimeTicks profile_start_time = base::TimeTicks::UnixEpoch();
515   base::TimeDelta sample_time_delta = base::Seconds(1);
516 
517   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
518       &metadata_recorder, base::PlatformThread::CurrentId()));
519   profile_builder->OnSampleCompleted({frame}, profile_start_time);
520 
521   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
522       &metadata_recorder, base::PlatformThread::CurrentId()));
523   profile_builder->OnSampleCompleted({frame},
524                                      profile_start_time + sample_time_delta);
525 
526   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
527       &metadata_recorder, base::PlatformThread::CurrentId()));
528   profile_builder->OnSampleCompleted(
529       {frame}, profile_start_time + 2 * sample_time_delta);
530 
531   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
532       &metadata_recorder, base::PlatformThread::CurrentId()));
533   profile_builder->OnSampleCompleted(
534       {frame}, profile_start_time + 3 * sample_time_delta);
535 
536   // Apply the metadata from the second through third samples.
537   profile_builder->ApplyMetadataRetrospectively(
538       profile_start_time + sample_time_delta,
539       profile_start_time + sample_time_delta * 2,
540       base::MetadataRecorder::Item(3, 30, std::nullopt, 300));
541 
542   profile_builder->OnProfileCompleted(3 * sample_time_delta, sample_time_delta);
543 
544   const SampledProfile& proto = profile_builder->test_sampled_profile();
545 
546   ASSERT_TRUE(proto.has_call_stack_profile());
547   const CallStackProfile& profile = proto.call_stack_profile();
548 
549   ASSERT_EQ(1, profile.metadata_name_hash_size());
550   EXPECT_EQ(3u, profile.metadata_name_hash(0));
551 
552   EXPECT_EQ(4, profile.stack_sample_size());
553 
554   EXPECT_EQ(0, profile.stack_sample(0).metadata_size());
555 
556   ASSERT_EQ(1, profile.stack_sample(1).metadata_size());
557   EXPECT_EQ(0, profile.stack_sample(1).metadata(0).name_hash_index());
558   EXPECT_EQ(30, profile.stack_sample(1).metadata(0).key());
559   EXPECT_EQ(300, profile.stack_sample(1).metadata(0).value());
560 
561   EXPECT_EQ(0, profile.stack_sample(2).metadata_size());
562 
563   ASSERT_EQ(1, profile.stack_sample(3).metadata_size());
564   EXPECT_EQ(0, profile.stack_sample(3).metadata(0).name_hash_index());
565   EXPECT_EQ(30, profile.stack_sample(3).metadata(0).key());
566   EXPECT_FALSE(profile.stack_sample(3).metadata(0).has_value());
567 }
568 
569 // Checks that ApplyMetadataRetrospectively doesn't apply metadata if the
570 // requested start time is before the profile start time.
TEST(CallStackProfileBuilderTest,ApplyMetadataRetrospectively_BeforeStartTime)571 TEST(CallStackProfileBuilderTest,
572      ApplyMetadataRetrospectively_BeforeStartTime) {
573   base::MetadataRecorder metadata_recorder;
574   auto profile_builder =
575       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
576 
577   base::TestModule module;
578   base::Frame frame = {0x10, &module};
579   base::TimeTicks profile_start_time = base::TimeTicks::UnixEpoch();
580   base::TimeDelta sample_time_delta = base::Seconds(1);
581 
582   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
583       &metadata_recorder, base::PlatformThread::CurrentId()));
584   profile_builder->OnSampleCompleted({frame}, profile_start_time);
585 
586   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
587       &metadata_recorder, base::PlatformThread::CurrentId()));
588   profile_builder->OnSampleCompleted({frame},
589                                      profile_start_time + sample_time_delta);
590 
591   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
592       &metadata_recorder, base::PlatformThread::CurrentId()));
593   profile_builder->OnSampleCompleted(
594       {frame}, profile_start_time + 2 * sample_time_delta);
595 
596   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
597       &metadata_recorder, base::PlatformThread::CurrentId()));
598   profile_builder->OnSampleCompleted(
599       {frame}, profile_start_time + 3 * sample_time_delta);
600 
601   profile_builder->ApplyMetadataRetrospectively(
602       profile_start_time - base::Microseconds(1),
603       profile_start_time + sample_time_delta,
604       base::MetadataRecorder::Item(3, 30, std::nullopt, 300));
605 
606   profile_builder->OnProfileCompleted(3 * sample_time_delta, sample_time_delta);
607 
608   const SampledProfile& proto = profile_builder->test_sampled_profile();
609 
610   ASSERT_TRUE(proto.has_call_stack_profile());
611   const CallStackProfile& profile = proto.call_stack_profile();
612 
613   EXPECT_EQ(0, profile.metadata_name_hash_size());
614   EXPECT_EQ(4, profile.stack_sample_size());
615 
616   for (const CallStackProfile::StackSample& sample : profile.stack_sample())
617     EXPECT_EQ(0, sample.metadata_size());
618 }
619 
620 }  // namespace metrics
621