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