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