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.
5 #include "components/metrics/call_stack_profile_builder.h"
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_stack_profile_params.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/metrics_proto/sampled_profile.pb.h"
25 constexpr CallStackProfileParams kProfileParams = {
26 CallStackProfileParams::Process::kBrowser,
27 CallStackProfileParams::Thread::kMain,
28 CallStackProfileParams::Trigger::kProcessStartup};
30 class TestingCallStackProfileBuilder : public CallStackProfileBuilder {
32 TestingCallStackProfileBuilder(
33 const CallStackProfileParams& profile_params,
34 const WorkIdRecorder* work_id_recorder = nullptr,
35 base::OnceClosure completed_callback = base::OnceClosure());
37 ~TestingCallStackProfileBuilder() override;
39 base::TimeTicks test_profile_start_time() const {
40 return test_profile_start_time_;
43 const SampledProfile& test_sampled_profile() const {
44 return test_sampled_profile_;
48 // Overridden for testing.
49 void PassProfilesToMetricsProvider(base::TimeTicks profile_start_time,
50 SampledProfile sampled_profile) override;
53 // The start time and completed profile.
54 base::TimeTicks test_profile_start_time_;
55 SampledProfile test_sampled_profile_;
58 TestingCallStackProfileBuilder::TestingCallStackProfileBuilder(
59 const CallStackProfileParams& profile_params,
60 const WorkIdRecorder* work_id_recorder,
61 base::OnceClosure completed_callback)
62 : CallStackProfileBuilder(profile_params,
64 std::move(completed_callback)) {}
66 TestingCallStackProfileBuilder::~TestingCallStackProfileBuilder() = default;
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);
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);
82 auto profile_builder = std::make_unique<TestingCallStackProfileBuilder>(
83 kProfileParams, nullptr, mock_closure.Get());
84 base::MetadataRecorder metadata_recorder;
87 uint64_t module_md5 = 0x46C3E4166659AC02ULL;
88 base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
90 uint64_t module_md5 = 0x554838A8451AC36CULL;
91 base::FilePath module_path("/some/path/to/chrome");
94 const uintptr_t module_base_address1 = 0x1000;
95 base::TestModule module1(module_base_address1);
97 module1.set_debug_basename(module_path);
98 base::Frame frame1 = {module_base_address1 + 0x10, &module1};
100 const uintptr_t module_base_address2 = 0x1100;
101 base::TestModule module2(module_base_address2);
103 module2.set_debug_basename(module_path);
104 base::Frame frame2 = {module_base_address2 + 0x10, &module2};
106 const uintptr_t module_base_address3 = 0x1010;
107 base::TestModule module3(module_base_address3);
109 module3.set_debug_basename(module_path);
110 base::Frame frame3 = {module_base_address3 + 0x10, &module3};
112 std::vector<base::Frame> frames1 = {frame1, frame2};
113 std::vector<base::Frame> frames2 = {frame3};
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));
124 const SampledProfile& proto = profile_builder->test_sampled_profile();
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());
133 ASSERT_TRUE(proto.has_call_stack_profile());
134 const CallStackProfile& profile = proto.call_stack_profile();
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());
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());
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());
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());
174 TEST(CallStackProfileBuilderTest, CustomWeightsAndCounts) {
175 auto profile_builder =
176 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
178 base::TestModule module1;
179 base::Frame frame1 = {0x10, &module1};
180 std::vector<base::Frame> frames = {frame1};
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());
187 const SampledProfile& proto = profile_builder->test_sampled_profile();
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());
202 TEST(CallStackProfileBuilderTest, StacksDeduped) {
203 auto profile_builder =
204 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
205 base::MetadataRecorder metadata_recorder;
207 base::TestModule module1;
208 base::Frame frame1 = {0x10, &module1};
210 base::TestModule module2;
211 base::Frame frame2 = {0x20, &module2};
213 std::vector<base::Frame> frames = {frame1, frame2};
215 // Two stacks are completed with the same frames therefore they are deduped
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());
224 profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
226 const SampledProfile& proto = profile_builder->test_sampled_profile();
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());
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());
243 TEST(CallStackProfileBuilderTest, StacksNotDeduped) {
244 auto profile_builder =
245 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
246 base::MetadataRecorder metadata_recorder;
248 base::TestModule module1;
249 base::Frame frame1 = {0x10, &module1};
251 base::TestModule module2;
252 base::Frame frame2 = {0x20, &module2};
254 std::vector<base::Frame> frames1 = {frame1};
255 std::vector<base::Frame> frames2 = {frame2};
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());
265 profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
267 const SampledProfile& proto = profile_builder->test_sampled_profile();
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());
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());
284 TEST(CallStackProfileBuilderTest, Modules) {
285 auto profile_builder =
286 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
287 base::MetadataRecorder metadata_recorder;
289 // A frame with no module.
290 base::Frame frame1 = {0x1010, nullptr};
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");
297 uint64_t module_md5 = 0x554838A8451AC36CULL;
298 base::FilePath module_path("/some/path/to/chrome");
300 base::TestModule module2(module_base_address2);
302 module2.set_debug_basename(module_path);
303 base::Frame frame2 = {module_base_address2 + 0x10, &module2};
305 std::vector<base::Frame> frames = {frame1, frame2};
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());
312 const SampledProfile& proto = profile_builder->test_sampled_profile();
314 ASSERT_TRUE(proto.has_call_stack_profile());
315 const CallStackProfile& profile = proto.call_stack_profile();
317 ASSERT_EQ(1, profile.stack_sample_size());
318 EXPECT_EQ(0, profile.stack_sample(0).stack_index());
320 ASSERT_EQ(1, profile.stack_size());
321 ASSERT_EQ(2, profile.stack(0).frame_size());
323 ASSERT_FALSE(profile.stack(0).frame(0).has_module_id_index());
324 ASSERT_FALSE(profile.stack(0).frame(0).has_address());
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());
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());
338 TEST(CallStackProfileBuilderTest, DedupModules) {
339 auto profile_builder =
340 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
341 base::MetadataRecorder metadata_recorder;
343 const uintptr_t module_base_address = 0x1000;
345 #if BUILDFLAG(IS_WIN)
346 uint64_t module_md5 = 0x46C3E4166659AC02ULL;
347 base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
349 uint64_t module_md5 = 0x554838A8451AC36CULL;
350 base::FilePath module_path("/some/path/to/chrome");
353 base::TestModule module(module_base_address);
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};
359 std::vector<base::Frame> frames = {frame1, frame2};
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());
366 const SampledProfile& proto = profile_builder->test_sampled_profile();
368 ASSERT_TRUE(proto.has_call_stack_profile());
369 const CallStackProfile& profile = proto.call_stack_profile();
371 ASSERT_EQ(1, profile.stack_sample_size());
372 EXPECT_EQ(0, profile.stack_sample(0).stack_index());
374 ASSERT_EQ(1, profile.stack_size());
375 ASSERT_EQ(2, profile.stack(0).frame_size());
377 // The two frames share the same module, which should be deduped in the
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());
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());
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());
396 TEST(CallStackProfileBuilderTest, WorkIds) {
397 class TestWorkIdRecorder : public WorkIdRecorder {
399 unsigned int RecordWorkId() const override { return current_id; }
401 unsigned int current_id = 0;
404 TestWorkIdRecorder work_id_recorder;
405 auto profile_builder = std::make_unique<TestingCallStackProfileBuilder>(
406 kProfileParams, &work_id_recorder);
407 base::MetadataRecorder metadata_recorder;
409 base::TestModule module;
410 base::Frame frame = {0x10, &module};
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());
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());
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());
436 profile_builder->OnProfileCompleted(base::Milliseconds(500),
437 base::Milliseconds(100));
439 const SampledProfile& proto = profile_builder->test_sampled_profile();
441 ASSERT_TRUE(proto.has_call_stack_profile());
442 const CallStackProfile& profile = proto.call_stack_profile();
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());
452 TEST(CallStackProfileBuilderTest, ProfileStartTime) {
453 auto profile_builder =
454 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams);
456 base::TestModule module;
457 const base::Frame frame = {0x10, &module};
458 const base::TimeTicks first_sample_time = base::TimeTicks::UnixEpoch();
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));
465 EXPECT_EQ(first_sample_time, profile_builder->test_profile_start_time());
468 // A basic test of RecordMetadata at the level of the
469 // CallStackProfileBuilder. The underlying implementation in
470 // CallStackProfileMetadata is tested independently.
471 TEST(CallStackProfileBuilderTest, RecordMetadata) {
472 base::MetadataRecorder metadata_recorder;
473 auto profile_builder =
474 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
476 base::TestModule module;
477 base::Frame frame = {0x10, &module};
479 metadata_recorder.Set(100, absl::nullopt, absl::nullopt, 10);
480 profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
481 &metadata_recorder, base::PlatformThread::CurrentId()));
482 profile_builder->OnSampleCompleted({frame}, base::TimeTicks());
484 profile_builder->OnProfileCompleted(base::Milliseconds(500),
485 base::Milliseconds(100));
487 const SampledProfile& proto = profile_builder->test_sampled_profile();
489 ASSERT_TRUE(proto.has_call_stack_profile());
490 const CallStackProfile& profile = proto.call_stack_profile();
492 ASSERT_EQ(1, profile.metadata_name_hash_size());
493 EXPECT_EQ(100u, profile.metadata_name_hash(0));
495 ASSERT_EQ(1, profile.stack_sample_size());
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());
504 // A basic test of ApplyMetadataRetrospectively at the level of the
505 // CallStackProfileBuilder. The underlying implementation in
506 // CallStackProfileMetadata is tested independently.
507 TEST(CallStackProfileBuilderTest, ApplyMetadataRetrospectively_Basic) {
508 base::MetadataRecorder metadata_recorder;
509 auto profile_builder =
510 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
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);
517 profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
518 &metadata_recorder, base::PlatformThread::CurrentId()));
519 profile_builder->OnSampleCompleted({frame}, profile_start_time);
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);
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);
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);
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, absl::nullopt, 300));
542 profile_builder->OnProfileCompleted(3 * sample_time_delta, sample_time_delta);
544 const SampledProfile& proto = profile_builder->test_sampled_profile();
546 ASSERT_TRUE(proto.has_call_stack_profile());
547 const CallStackProfile& profile = proto.call_stack_profile();
549 ASSERT_EQ(1, profile.metadata_name_hash_size());
550 EXPECT_EQ(3u, profile.metadata_name_hash(0));
552 EXPECT_EQ(4, profile.stack_sample_size());
554 EXPECT_EQ(0, profile.stack_sample(0).metadata_size());
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());
561 EXPECT_EQ(0, profile.stack_sample(2).metadata_size());
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());
569 // Checks that ApplyMetadataRetrospectively doesn't apply metadata if the
570 // requested start time is before the profile start time.
571 TEST(CallStackProfileBuilderTest,
572 ApplyMetadataRetrospectively_BeforeStartTime) {
573 base::MetadataRecorder metadata_recorder;
574 auto profile_builder =
575 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
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);
582 profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider(
583 &metadata_recorder, base::PlatformThread::CurrentId()));
584 profile_builder->OnSampleCompleted({frame}, profile_start_time);
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);
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);
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);
601 profile_builder->ApplyMetadataRetrospectively(
602 profile_start_time - base::Microseconds(1),
603 profile_start_time + sample_time_delta,
604 base::MetadataRecorder::Item(3, 30, absl::nullopt, 300));
606 profile_builder->OnProfileCompleted(3 * sample_time_delta, sample_time_delta);
608 const SampledProfile& proto = profile_builder->test_sampled_profile();
610 ASSERT_TRUE(proto.has_call_stack_profile());
611 const CallStackProfile& profile = proto.call_stack_profile();
613 EXPECT_EQ(0, profile.metadata_name_hash_size());
614 EXPECT_EQ(4, profile.stack_sample_size());
616 for (const CallStackProfile::StackSample& sample : profile.stack_sample())
617 EXPECT_EQ(0, sample.metadata_size());
620 // Test that timestamps are correctly attached.
621 TEST(CallStackProfileBuilderTest, AttachTimestamps) {
622 base::MetadataRecorder metadata_recorder;
623 auto profile_builder =
624 std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr);
626 base::TestModule module;
627 base::Frame frame = {0x10, &module};
629 const base::TimeTicks profile_start = base::TimeTicks::Now();
631 profile_builder->OnSampleCompleted({frame}, profile_start);
633 profile_builder->OnSampleCompleted({frame},
634 profile_start + base::Milliseconds(500));
636 profile_builder->OnProfileCompleted(base::Milliseconds(500),
637 base::Milliseconds(100));
639 const SampledProfile& proto = profile_builder->test_sampled_profile();
641 ASSERT_TRUE(proto.has_call_stack_profile());
642 const CallStackProfile& profile = proto.call_stack_profile();
643 EXPECT_EQ(profile_start.since_origin().InMilliseconds(),
644 profile.profile_time_offset_ms());
646 ASSERT_EQ(2, profile.stack_sample_size());
647 auto sample0 = profile.stack_sample(0);
648 auto sample1 = profile.stack_sample(1);
649 EXPECT_EQ(0, sample0.sample_time_offset_ms());
650 EXPECT_EQ(500, sample1.sample_time_offset_ms());
653 } // namespace metrics