1 // Copyright 2015 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_metrics_provider.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/metrics/metrics_hashes.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "execution_context.pb.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
16
17 namespace metrics {
18
19 using ::testing::Eq;
20 using ::testing::Pair;
21 using ::testing::UnorderedElementsAre;
22
23 // This test fixture enables the feature that
24 // CallStackProfileMetricsProvider depends on to report a profile.
25 class CallStackProfileMetricsProviderTest : public testing::Test {
26 public:
CallStackProfileMetricsProviderTest()27 CallStackProfileMetricsProviderTest() {
28 TestState::ResetStaticStateForTesting();
29 scoped_feature_list_.InitAndEnableFeature(kSamplingProfilerReporting);
30 }
31
32 CallStackProfileMetricsProviderTest(
33 const CallStackProfileMetricsProviderTest&) = delete;
34 CallStackProfileMetricsProviderTest& operator=(
35 const CallStackProfileMetricsProviderTest&) = delete;
36
37 protected:
38 // Exposes the feature from the CallStackProfileMetricsProvider.
39 class TestState : public CallStackProfileMetricsProvider {
40 public:
41 using CallStackProfileMetricsProvider::ResetStaticStateForTesting;
42 };
43
44 private:
45 base::test::ScopedFeatureList scoped_feature_list_;
46 };
47
48 // Checks that the unserialized pending profile is encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataUnserialized)49 TEST_F(CallStackProfileMetricsProviderTest,
50 ProvideCurrentSessionDataUnserialized) {
51 CallStackProfileMetricsProvider provider;
52 provider.OnRecordingEnabled();
53 SampledProfile profile;
54 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
55 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
56 profile);
57 ChromeUserMetricsExtension uma_proto;
58 provider.ProvideCurrentSessionData(&uma_proto);
59 ASSERT_EQ(1, uma_proto.sampled_profile().size());
60 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
61 uma_proto.sampled_profile(0).trigger_event());
62 }
63
64 // Checks that the serialized pending profile is encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataSerialized)65 TEST_F(CallStackProfileMetricsProviderTest,
66 ProvideCurrentSessionDataSerialized) {
67 CallStackProfileMetricsProvider provider;
68 provider.OnRecordingEnabled();
69 std::string contents;
70 {
71 SampledProfile profile;
72 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
73 profile.SerializeToString(&contents);
74 }
75 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
76 base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(contents));
77 ChromeUserMetricsExtension uma_proto;
78 provider.ProvideCurrentSessionData(&uma_proto);
79 ASSERT_EQ(1, uma_proto.sampled_profile().size());
80 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
81 uma_proto.sampled_profile(0).trigger_event());
82 }
83
84 // Checks that both the unserialized and serialized pending profiles are
85 // encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataUnserializedAndSerialized)86 TEST_F(CallStackProfileMetricsProviderTest,
87 ProvideCurrentSessionDataUnserializedAndSerialized) {
88 CallStackProfileMetricsProvider provider;
89 provider.OnRecordingEnabled();
90
91 // Receive an unserialized profile.
92 SampledProfile profile;
93 profile.set_trigger_event(SampledProfile::PROCESS_STARTUP);
94 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
95 std::move(profile));
96
97 // Receive a serialized profile.
98 std::string contents;
99 {
100 SampledProfile serialized_profile;
101 serialized_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
102 serialized_profile.SerializeToString(&contents);
103 }
104 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
105 base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(contents));
106
107 ChromeUserMetricsExtension uma_proto;
108 provider.ProvideCurrentSessionData(&uma_proto);
109 ASSERT_EQ(2, uma_proto.sampled_profile().size());
110 EXPECT_EQ(SampledProfile::PROCESS_STARTUP,
111 uma_proto.sampled_profile(0).trigger_event());
112 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
113 uma_proto.sampled_profile(1).trigger_event());
114 }
115
116 // Checks that the pending profiles above the total cap are dropped therefore
117 // not encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataExceedTotalCap)118 TEST_F(CallStackProfileMetricsProviderTest,
119 ProvideCurrentSessionDataExceedTotalCap) {
120 // The value must be consistent with that in
121 // call_stack_profile_metrics_provider.cc so that this test is meaningful.
122 const int kMaxPendingProfiles = 1250;
123
124 CallStackProfileMetricsProvider provider;
125 provider.OnRecordingEnabled();
126
127 // Receive (kMaxPendingProfiles + 1) profiles.
128 for (int i = 0; i < kMaxPendingProfiles + 1; ++i) {
129 SampledProfile profile;
130 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
131 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
132 std::move(profile));
133 }
134
135 ChromeUserMetricsExtension uma_proto;
136 provider.ProvideCurrentSessionData(&uma_proto);
137
138 // Only kMaxPendingProfiles profiles are encoded, with the additional one
139 // dropped.
140 ASSERT_EQ(kMaxPendingProfiles, uma_proto.sampled_profile().size());
141 for (int i = 0; i < kMaxPendingProfiles; ++i) {
142 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
143 uma_proto.sampled_profile(i).trigger_event());
144 }
145 }
146
147 // Checks that the pending profile is provided to ProvideCurrentSessionData
148 // when collected before CallStackProfileMetricsProvider is instantiated.
TEST_F(CallStackProfileMetricsProviderTest,ProfileProvidedWhenCollectedBeforeInstantiation)149 TEST_F(CallStackProfileMetricsProviderTest,
150 ProfileProvidedWhenCollectedBeforeInstantiation) {
151 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
152 SampledProfile());
153 CallStackProfileMetricsProvider provider;
154 provider.OnRecordingEnabled();
155 ChromeUserMetricsExtension uma_proto;
156 provider.ProvideCurrentSessionData(&uma_proto);
157 EXPECT_EQ(1, uma_proto.sampled_profile_size());
158 }
159
160 // Checks that the pending profile is not provided to ProvideCurrentSessionData
161 // while recording is disabled.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedWhileDisabled)162 TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedWhileDisabled) {
163 CallStackProfileMetricsProvider provider;
164 provider.OnRecordingDisabled();
165 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
166 SampledProfile());
167 ChromeUserMetricsExtension uma_proto;
168 provider.ProvideCurrentSessionData(&uma_proto);
169 EXPECT_EQ(0, uma_proto.sampled_profile_size());
170 }
171
172 // Checks that the pending profile is not provided to ProvideCurrentSessionData
173 // if recording is disabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedAfterChangeToDisabled)174 TEST_F(CallStackProfileMetricsProviderTest,
175 ProfileNotProvidedAfterChangeToDisabled) {
176 CallStackProfileMetricsProvider provider;
177 provider.OnRecordingEnabled();
178 base::TimeTicks profile_start_time = base::TimeTicks::Now();
179 provider.OnRecordingDisabled();
180 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
181 SampledProfile());
182 ChromeUserMetricsExtension uma_proto;
183 provider.ProvideCurrentSessionData(&uma_proto);
184 EXPECT_EQ(0, uma_proto.sampled_profile_size());
185 }
186
187 // Checks that the pending profile is not provided to ProvideCurrentSessionData
188 // if recording is enabled, but then disabled and reenabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedAfterChangeToDisabledThenEnabled)189 TEST_F(CallStackProfileMetricsProviderTest,
190 ProfileNotProvidedAfterChangeToDisabledThenEnabled) {
191 CallStackProfileMetricsProvider provider;
192 provider.OnRecordingEnabled();
193 base::TimeTicks profile_start_time = base::TimeTicks::Now();
194 provider.OnRecordingDisabled();
195 provider.OnRecordingEnabled();
196 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
197 SampledProfile());
198 ChromeUserMetricsExtension uma_proto;
199 provider.ProvideCurrentSessionData(&uma_proto);
200 EXPECT_EQ(0, uma_proto.sampled_profile_size());
201 }
202
203 // Checks that the pending profile is provided to ProvideCurrentSessionData
204 // if recording is disabled, but then enabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedAfterChangeFromDisabled)205 TEST_F(CallStackProfileMetricsProviderTest,
206 ProfileNotProvidedAfterChangeFromDisabled) {
207 CallStackProfileMetricsProvider provider;
208 provider.OnRecordingDisabled();
209 base::TimeTicks profile_start_time = base::TimeTicks::Now();
210 provider.OnRecordingEnabled();
211 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
212 SampledProfile());
213 ChromeUserMetricsExtension uma_proto;
214 provider.ProvideCurrentSessionData(&uma_proto);
215 EXPECT_EQ(0, uma_proto.sampled_profile_size());
216 }
217
218 // Checks that a heap profile is not reported when recording is disabled.
TEST_F(CallStackProfileMetricsProviderTest,HeapProfileNotProvidedWhenDisabled)219 TEST_F(CallStackProfileMetricsProviderTest,
220 HeapProfileNotProvidedWhenDisabled) {
221 CallStackProfileMetricsProvider provider;
222 provider.OnRecordingDisabled();
223 base::TimeTicks profile_start_time = base::TimeTicks::Now();
224
225 // Unserialized profile.
226 SampledProfile profile;
227 profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
228 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
229
230 // Serialized profile.
231 std::string contents;
232 profile.SerializeToString(&contents);
233 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
234 profile_start_time, /*is_heap_profile=*/true, std::move(contents));
235
236 ChromeUserMetricsExtension uma_proto;
237 provider.ProvideCurrentSessionData(&uma_proto);
238 EXPECT_EQ(0, uma_proto.sampled_profile_size());
239 }
240
241 // Checks that a heap profile is provided to ProvideCurrentSessionData
242 // if recording is enabled.
TEST_F(CallStackProfileMetricsProviderTest,HeapProfileProvidedWhenEnabled)243 TEST_F(CallStackProfileMetricsProviderTest, HeapProfileProvidedWhenEnabled) {
244 CallStackProfileMetricsProvider provider;
245 provider.OnRecordingEnabled();
246 base::TimeTicks profile_start_time = base::TimeTicks::Now();
247
248 // Unserialized profile.
249 SampledProfile profile;
250 profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
251 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
252
253 // Serialized profile.
254 std::string contents;
255 profile.SerializeToString(&contents);
256 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
257 profile_start_time, /*is_heap_profile=*/true, std::move(contents));
258
259 ChromeUserMetricsExtension uma_proto;
260 provider.ProvideCurrentSessionData(&uma_proto);
261 EXPECT_EQ(2, uma_proto.sampled_profile_size());
262 }
263
264 // Checks that heap profiles but not CPU profiles are reported when sampling CPU
265 // Finch is disabled.
TEST_F(CallStackProfileMetricsProviderTest,CpuProfileNotProvidedWithoutFinch)266 TEST_F(CallStackProfileMetricsProviderTest, CpuProfileNotProvidedWithoutFinch) {
267 base::test::ScopedFeatureList scoped_feature_list;
268 scoped_feature_list.InitAndDisableFeature(kSamplingProfilerReporting);
269 CallStackProfileMetricsProvider provider;
270 base::TimeTicks profile_start_time = base::TimeTicks::Now();
271
272 // Unserialized profiles.
273 SampledProfile profile;
274 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
275 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
276
277 SampledProfile heap_profile;
278 heap_profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
279 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
280 heap_profile);
281
282 // Serialized profiles.
283 std::string contents;
284 profile.SerializeToString(&contents);
285 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
286 profile_start_time, /*is_heap_profile=*/false, std::move(contents));
287
288 std::string heap_contents;
289 heap_profile.SerializeToString(&heap_contents);
290 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
291 profile_start_time, /*is_heap_profile=*/true, std::move(heap_contents));
292
293 ChromeUserMetricsExtension uma_proto;
294 provider.ProvideCurrentSessionData(&uma_proto);
295 ASSERT_EQ(2, uma_proto.sampled_profile_size());
296 EXPECT_EQ(SampledProfile::PERIODIC_HEAP_COLLECTION,
297 uma_proto.sampled_profile(0).trigger_event());
298 EXPECT_EQ(SampledProfile::PERIODIC_HEAP_COLLECTION,
299 uma_proto.sampled_profile(1).trigger_event());
300 }
301
302 namespace {
AttachProfileMetadata(SampledProfile & profile,uint32_t name_hash_index,int64_t key,int64_t value)303 void AttachProfileMetadata(SampledProfile& profile,
304 uint32_t name_hash_index,
305 int64_t key,
306 int64_t value) {
307 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
308 CallStackProfile::MetadataItem* item =
309 call_stack_profile->mutable_profile_metadata()->Add();
310 item->set_name_hash_index(name_hash_index);
311 item->set_key(key);
312 item->set_value(value);
313 }
314 } // namespace
315
316 // Checks that internal-use profile metadata is removed, when retrieving
317 // profiles from the Provider.
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataRemovedSerialized)318 TEST_F(CallStackProfileMetricsProviderTest,
319 InternalProfileMetadataRemovedSerialized) {
320 CallStackProfileMetricsProvider provider;
321 base::TimeTicks profile_start_time = base::TimeTicks::Now();
322
323 SampledProfile profile;
324 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
325 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
326 call_stack_profile->mutable_metadata_name_hash()->Add(
327 base::HashMetricName("OtherMetric0"));
328 call_stack_profile->mutable_metadata_name_hash()->Add(
329 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
330 call_stack_profile->mutable_metadata_name_hash()->Add(
331 base::HashMetricName("OtherMetric1"));
332 call_stack_profile->mutable_metadata_name_hash()->Add(
333 base::HashMetricName("Internal.LargestContentfulPaint.DocumentToken"));
334 call_stack_profile->mutable_metadata_name_hash()->Add(
335 base::HashMetricName("OtherMetric2"));
336 AttachProfileMetadata(profile, 1, 2, 12);
337 AttachProfileMetadata(profile, 3, 3, 13);
338 AttachProfileMetadata(profile, 1, 4, 14);
339 AttachProfileMetadata(profile, 3, 5, 15);
340
341 // Irrelevant profile metadata that should not be removed.
342 AttachProfileMetadata(profile, 0, 0, 0);
343 AttachProfileMetadata(profile, 2, 0, 0);
344 AttachProfileMetadata(profile, 4, 0, 0);
345
346 EXPECT_EQ(5, profile.call_stack_profile().metadata_name_hash_size());
347 EXPECT_EQ(7, profile.call_stack_profile().profile_metadata_size());
348
349 // Receive serialized profiles.
350 std::string contents;
351 profile.SerializeToString(&contents);
352 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
353 profile_start_time, /*is_heap_profile=*/false, std::move(contents));
354
355 ChromeUserMetricsExtension uma_proto;
356 provider.ProvideCurrentSessionData(&uma_proto);
357 ASSERT_EQ(1, uma_proto.sampled_profile_size());
358
359 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
360 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
361 result_profile.trigger_event());
362 ASSERT_EQ(3, result_profile.call_stack_profile().metadata_name_hash_size());
363 EXPECT_EQ(base::HashMetricName("OtherMetric0"),
364 result_profile.call_stack_profile().metadata_name_hash(0));
365 EXPECT_EQ(base::HashMetricName("OtherMetric1"),
366 result_profile.call_stack_profile().metadata_name_hash(1));
367 EXPECT_EQ(base::HashMetricName("OtherMetric2"),
368 result_profile.call_stack_profile().metadata_name_hash(2));
369
370 ASSERT_EQ(3, result_profile.call_stack_profile().profile_metadata_size());
371
372 const auto& profile_metadata =
373 result_profile.call_stack_profile().profile_metadata();
374 EXPECT_EQ(0, profile_metadata[0].name_hash_index());
375 EXPECT_EQ(1, profile_metadata[1].name_hash_index());
376 EXPECT_EQ(2, profile_metadata[2].name_hash_index());
377 }
378
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataRemovedUnserialized)379 TEST_F(CallStackProfileMetricsProviderTest,
380 InternalProfileMetadataRemovedUnserialized) {
381 CallStackProfileMetricsProvider provider;
382 base::TimeTicks profile_start_time = base::TimeTicks::Now();
383
384 // Unserialized profiles.
385 SampledProfile profile;
386 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
387 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
388 call_stack_profile->mutable_metadata_name_hash()->Add(
389 base::HashMetricName("OtherMetric0"));
390 call_stack_profile->mutable_metadata_name_hash()->Add(
391 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
392 call_stack_profile->mutable_metadata_name_hash()->Add(
393 base::HashMetricName("OtherMetric1"));
394 call_stack_profile->mutable_metadata_name_hash()->Add(
395 base::HashMetricName("Internal.LargestContentfulPaint.DocumentToken"));
396 call_stack_profile->mutable_metadata_name_hash()->Add(
397 base::HashMetricName("OtherMetric2"));
398 AttachProfileMetadata(profile, 1, 2, 12);
399 AttachProfileMetadata(profile, 3, 3, 13);
400 AttachProfileMetadata(profile, 1, 4, 14);
401 AttachProfileMetadata(profile, 3, 5, 15);
402
403 // Irrelevant profile metadata that should not be removed.
404 AttachProfileMetadata(profile, 0, 0, 0);
405 AttachProfileMetadata(profile, 2, 0, 0);
406 AttachProfileMetadata(profile, 4, 0, 0);
407
408 EXPECT_EQ(5, profile.call_stack_profile().metadata_name_hash_size());
409 EXPECT_EQ(7, profile.call_stack_profile().profile_metadata_size());
410
411 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
412
413 ChromeUserMetricsExtension uma_proto;
414 provider.ProvideCurrentSessionData(&uma_proto);
415 ASSERT_EQ(1, uma_proto.sampled_profile_size());
416
417 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
418 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
419 result_profile.trigger_event());
420 ASSERT_EQ(3, result_profile.call_stack_profile().metadata_name_hash_size());
421 EXPECT_EQ(base::HashMetricName("OtherMetric0"),
422 result_profile.call_stack_profile().metadata_name_hash(0));
423 EXPECT_EQ(base::HashMetricName("OtherMetric1"),
424 result_profile.call_stack_profile().metadata_name_hash(1));
425 EXPECT_EQ(base::HashMetricName("OtherMetric2"),
426 result_profile.call_stack_profile().metadata_name_hash(2));
427
428 ASSERT_EQ(3, result_profile.call_stack_profile().profile_metadata_size());
429 const auto& profile_metadata =
430 result_profile.call_stack_profile().profile_metadata();
431 EXPECT_EQ(0, profile_metadata[0].name_hash_index());
432 EXPECT_EQ(1, profile_metadata[1].name_hash_index());
433 EXPECT_EQ(2, profile_metadata[2].name_hash_index());
434 }
435
436 namespace {
AttachSampleWithMetadata(SampledProfile & profile,uint32_t name_hash_index,int64_t key,int64_t value)437 void AttachSampleWithMetadata(SampledProfile& profile,
438 uint32_t name_hash_index,
439 int64_t key,
440 int64_t value) {
441 auto* stack_sample =
442 profile.mutable_call_stack_profile()->mutable_stack_sample()->Add();
443 auto* item = stack_sample->mutable_metadata()->Add();
444 item->set_name_hash_index(name_hash_index);
445 item->set_key(key);
446 item->set_value(value);
447 }
448 } // namespace
449
450 // After name hash index array change, all name hash index mapping in stack
451 // sample metadata should remain correct.
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataRemovedStackSampleMetadataHashIndex)452 TEST_F(CallStackProfileMetricsProviderTest,
453 InternalProfileMetadataRemovedStackSampleMetadataHashIndex) {
454 CallStackProfileMetricsProvider provider;
455 base::TimeTicks profile_start_time = base::TimeTicks::Now();
456
457 SampledProfile profile;
458 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
459 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
460 call_stack_profile->mutable_metadata_name_hash()->Add(
461 base::HashMetricName("OtherMetric0"));
462 call_stack_profile->mutable_metadata_name_hash()->Add(
463 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
464 call_stack_profile->mutable_metadata_name_hash()->Add(
465 base::HashMetricName("OtherMetric1"));
466 call_stack_profile->mutable_metadata_name_hash()->Add(
467 base::HashMetricName("Internal.LargestContentfulPaint.DocumentToken"));
468 call_stack_profile->mutable_metadata_name_hash()->Add(
469 base::HashMetricName("OtherMetric2"));
470
471 // Metadata item on stack sample.
472 AttachSampleWithMetadata(profile, 0, 0, 0);
473 AttachSampleWithMetadata(profile, 2, 0, 0);
474 AttachSampleWithMetadata(profile, 4, 0, 0);
475
476 EXPECT_EQ(5, profile.call_stack_profile().metadata_name_hash_size());
477
478 // Receive serialized profiles.
479 std::string contents;
480 profile.SerializeToString(&contents);
481 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
482 profile_start_time, /*is_heap_profile=*/false, std::move(contents));
483
484 ChromeUserMetricsExtension uma_proto;
485 provider.ProvideCurrentSessionData(&uma_proto);
486 ASSERT_EQ(1, uma_proto.sampled_profile_size());
487
488 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
489 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
490 result_profile.trigger_event());
491 ASSERT_EQ(3, result_profile.call_stack_profile().metadata_name_hash_size());
492 EXPECT_EQ(base::HashMetricName("OtherMetric0"),
493 result_profile.call_stack_profile().metadata_name_hash(0));
494 EXPECT_EQ(base::HashMetricName("OtherMetric1"),
495 result_profile.call_stack_profile().metadata_name_hash(1));
496 EXPECT_EQ(base::HashMetricName("OtherMetric2"),
497 result_profile.call_stack_profile().metadata_name_hash(2));
498
499 ASSERT_EQ(3, result_profile.call_stack_profile().stack_sample_size());
500 EXPECT_EQ(0, result_profile.call_stack_profile()
501 .stack_sample(0)
502 .metadata(0)
503 .name_hash_index());
504 EXPECT_EQ(1, result_profile.call_stack_profile()
505 .stack_sample(1)
506 .metadata(0)
507 .name_hash_index());
508 EXPECT_EQ(2, result_profile.call_stack_profile()
509 .stack_sample(2)
510 .metadata(0)
511 .name_hash_index());
512 }
513
514 namespace {
AttachSampleWithTimestamp(SampledProfile & profile,int32_t timestamp_offset)515 void AttachSampleWithTimestamp(SampledProfile& profile,
516 int32_t timestamp_offset) {
517 auto* stack_sample =
518 profile.mutable_call_stack_profile()->mutable_stack_sample()->Add();
519 stack_sample->set_sample_time_offset_ms(timestamp_offset);
520 }
521 } // namespace
522
523 // Timestamps on each stack sample are removed.
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataRemovedTimestamps)524 TEST_F(CallStackProfileMetricsProviderTest,
525 InternalProfileMetadataRemovedTimestamps) {
526 CallStackProfileMetricsProvider provider;
527 base::TimeTicks profile_start_time = base::TimeTicks::Now();
528
529 SampledProfile profile;
530 profile.mutable_call_stack_profile()->set_profile_time_offset_ms(100);
531 AttachSampleWithTimestamp(profile, 100);
532 AttachSampleWithTimestamp(profile, 300);
533
534 // Receive serialized profiles.
535 std::string contents;
536 profile.SerializeToString(&contents);
537 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
538 profile_start_time, /*is_heap_profile=*/false, std::move(contents));
539
540 ChromeUserMetricsExtension uma_proto;
541 provider.ProvideCurrentSessionData(&uma_proto);
542 ASSERT_EQ(1, uma_proto.sampled_profile_size());
543
544 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
545 EXPECT_FALSE(
546 result_profile.call_stack_profile().has_profile_time_offset_ms());
547 ASSERT_EQ(2, result_profile.call_stack_profile().stack_sample_size());
548 for (int i = 0; i < 2; ++i) {
549 EXPECT_FALSE(result_profile.call_stack_profile()
550 .stack_sample(i)
551 .has_sample_time_offset_ms());
552 }
553 }
554
555 // Heap profiles should not be affected.
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataRemovedTimestampsHeapProfile)556 TEST_F(CallStackProfileMetricsProviderTest,
557 InternalProfileMetadataRemovedTimestampsHeapProfile) {
558 CallStackProfileMetricsProvider provider;
559 base::TimeTicks profile_start_time = base::TimeTicks::Now();
560
561 SampledProfile profile;
562 profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
563 profile.mutable_call_stack_profile()->set_profile_time_offset_ms(100);
564
565 // Receive serialized profiles.
566 std::string contents;
567 profile.SerializeToString(&contents);
568 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
569 profile_start_time, /*is_heap_profile=*/true, std::move(contents));
570
571 ChromeUserMetricsExtension uma_proto;
572 provider.ProvideCurrentSessionData(&uma_proto);
573 ASSERT_EQ(1, uma_proto.sampled_profile_size());
574
575 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
576 EXPECT_TRUE(result_profile.call_stack_profile().has_profile_time_offset_ms());
577 }
578
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataRemovedMissingHashIndex)579 TEST_F(CallStackProfileMetricsProviderTest,
580 InternalProfileMetadataRemovedMissingHashIndex) {
581 CallStackProfileMetricsProvider provider;
582 base::TimeTicks profile_start_time = base::TimeTicks::Now();
583
584 // Unserialized profiles.
585 SampledProfile profile;
586 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
587 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
588 // Hash index for "Internal.LargestContentfulPaint.DocumentToken" not present.
589 call_stack_profile->mutable_metadata_name_hash()->Add(
590 base::HashMetricName("OtherMetric0"));
591 call_stack_profile->mutable_metadata_name_hash()->Add(
592 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
593 call_stack_profile->mutable_metadata_name_hash()->Add(
594 base::HashMetricName("OtherMetric1"));
595
596 AttachProfileMetadata(profile, 1, 2, 12);
597 AttachProfileMetadata(profile, 1, 4, 14);
598
599 // Irrelevant profile metadata that should not be removed.
600 AttachProfileMetadata(profile, 0, 0, 0);
601 AttachProfileMetadata(profile, 2, 0, 0);
602
603 EXPECT_EQ(3, profile.call_stack_profile().metadata_name_hash_size());
604 EXPECT_EQ(4, profile.call_stack_profile().profile_metadata_size());
605
606 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
607
608 ChromeUserMetricsExtension uma_proto;
609 provider.ProvideCurrentSessionData(&uma_proto);
610 ASSERT_EQ(1, uma_proto.sampled_profile_size());
611
612 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
613 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
614 result_profile.trigger_event());
615 ASSERT_EQ(2, result_profile.call_stack_profile().metadata_name_hash_size());
616 EXPECT_EQ(base::HashMetricName("OtherMetric0"),
617 result_profile.call_stack_profile().metadata_name_hash(0));
618 EXPECT_EQ(base::HashMetricName("OtherMetric1"),
619 result_profile.call_stack_profile().metadata_name_hash(1));
620
621 ASSERT_EQ(2, result_profile.call_stack_profile().profile_metadata_size());
622 const auto& profile_metadata =
623 result_profile.call_stack_profile().profile_metadata();
624 EXPECT_EQ(0, profile_metadata[0].name_hash_index());
625 EXPECT_EQ(1, profile_metadata[1].name_hash_index());
626 }
627
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataInvalidDataMissingDocumentToken)628 TEST_F(CallStackProfileMetricsProviderTest,
629 InternalProfileMetadataInvalidDataMissingDocumentToken) {
630 CallStackProfileMetricsProvider provider;
631 base::TimeTicks profile_start_time = base::TimeTicks::Now();
632
633 SampledProfile profile;
634 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
635 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
636 call_stack_profile->mutable_metadata_name_hash()->Add(
637 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
638 call_stack_profile->mutable_metadata_name_hash()->Add(
639 base::HashMetricName("Internal.LargestContentfulPaint.DocumentToken"));
640 // Missing DocumentToken.
641 AttachProfileMetadata(profile, 0, 2, 12);
642 AttachProfileMetadata(profile, 0, 4, 14);
643
644 EXPECT_EQ(2, profile.call_stack_profile().metadata_name_hash_size());
645 EXPECT_EQ(2, profile.call_stack_profile().profile_metadata_size());
646 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
647
648 ChromeUserMetricsExtension uma_proto;
649 provider.ProvideCurrentSessionData(&uma_proto);
650 ASSERT_EQ(1, uma_proto.sampled_profile_size());
651
652 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
653 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
654 result_profile.trigger_event());
655 EXPECT_EQ(0, result_profile.call_stack_profile().metadata_name_hash_size());
656 EXPECT_EQ(0, result_profile.call_stack_profile().profile_metadata_size());
657 }
658
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataInvalidDataMissingNavigationStart)659 TEST_F(CallStackProfileMetricsProviderTest,
660 InternalProfileMetadataInvalidDataMissingNavigationStart) {
661 CallStackProfileMetricsProvider provider;
662 base::TimeTicks profile_start_time = base::TimeTicks::Now();
663
664 SampledProfile profile;
665 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
666 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
667 call_stack_profile->mutable_metadata_name_hash()->Add(
668 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
669 call_stack_profile->mutable_metadata_name_hash()->Add(
670 base::HashMetricName("Internal.LargestContentfulPaint.DocumentToken"));
671 // Missing NavigationStart.
672 AttachProfileMetadata(profile, 0, 2, 12);
673 AttachProfileMetadata(profile, 0, 4, 14);
674
675 EXPECT_EQ(2, profile.call_stack_profile().metadata_name_hash_size());
676 EXPECT_EQ(2, profile.call_stack_profile().profile_metadata_size());
677 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
678
679 ChromeUserMetricsExtension uma_proto;
680 provider.ProvideCurrentSessionData(&uma_proto);
681 ASSERT_EQ(1, uma_proto.sampled_profile_size());
682
683 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
684 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
685 result_profile.trigger_event());
686 EXPECT_EQ(0, result_profile.call_stack_profile().metadata_name_hash_size());
687 EXPECT_EQ(0, result_profile.call_stack_profile().profile_metadata_size());
688 }
689
690 // Should not crash when the provided name_hash_index is beyond the end of
691 // name_hash array.
TEST_F(CallStackProfileMetricsProviderTest,InternalProfileMetadataInvalidDataInvalidIndices)692 TEST_F(CallStackProfileMetricsProviderTest,
693 InternalProfileMetadataInvalidDataInvalidIndices) {
694 CallStackProfileMetricsProvider provider;
695 base::TimeTicks profile_start_time = base::TimeTicks::Now();
696
697 SampledProfile profile;
698 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
699 CallStackProfile* call_stack_profile = profile.mutable_call_stack_profile();
700 call_stack_profile->mutable_metadata_name_hash()->Add(
701 base::HashMetricName("Internal.LargestContentfulPaint.NavigationStart"));
702 call_stack_profile->mutable_metadata_name_hash()->Add(
703 base::HashMetricName("Internal.LargestContentfulPaint.DocumentToken"));
704
705 AttachProfileMetadata(profile, 0, 2, 12);
706 AttachProfileMetadata(profile, 2, 3, 13); // Invalid index
707 AttachProfileMetadata(profile, 0, 4, 14);
708 AttachProfileMetadata(profile, 1, 5, 15);
709
710 EXPECT_EQ(2, profile.call_stack_profile().metadata_name_hash_size());
711 EXPECT_EQ(4, profile.call_stack_profile().profile_metadata_size());
712 CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
713
714 ChromeUserMetricsExtension uma_proto;
715 provider.ProvideCurrentSessionData(&uma_proto);
716 ASSERT_EQ(1, uma_proto.sampled_profile_size());
717
718 const SampledProfile& result_profile = uma_proto.sampled_profile(0);
719 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
720 result_profile.trigger_event());
721 EXPECT_EQ(0, result_profile.call_stack_profile().metadata_name_hash_size());
722 // The ProfileMetadata with wrong index is not detected.
723 EXPECT_EQ(1, result_profile.call_stack_profile().profile_metadata_size());
724 }
725
726 #if BUILDFLAG(IS_CHROMEOS)
727
728 namespace {
729
730 // Sets |call_stack_profile| up enough to pass WasMinimallySuccessful()
MakeMinimallySuccessfulCallStackProfile(CallStackProfile * call_stack_profile)731 void MakeMinimallySuccessfulCallStackProfile(
732 CallStackProfile* call_stack_profile) {
733 CallStackProfile::Stack* stack = call_stack_profile->add_stack();
734 CallStackProfile::Location* frame = stack->add_frame();
735 frame->set_address(123);
736 frame->set_module_id_index(1);
737 frame = stack->add_frame();
738 frame->set_address(456);
739 frame->set_module_id_index(0);
740 }
741
742 // Makes a minimally successful SampledProfile and sends it to ReceiveProfile.
RecieveProfile(metrics::Process process,metrics::Thread thread)743 void RecieveProfile(metrics::Process process, metrics::Thread thread) {
744 SampledProfile profile;
745 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
746 profile.set_process(process);
747 profile.set_thread(thread);
748 MakeMinimallySuccessfulCallStackProfile(profile.mutable_call_stack_profile());
749 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
750 profile);
751 }
752
753 // Makes a minimally successful SampledProfile and sends it to
754 // ReceiveSerializedProfile.
ReceiveSerializedProfile(metrics::Process process,metrics::Thread thread)755 void ReceiveSerializedProfile(metrics::Process process,
756 metrics::Thread thread) {
757 SampledProfile profile;
758 profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
759 profile.set_process(process);
760 profile.set_thread(thread);
761 MakeMinimallySuccessfulCallStackProfile(profile.mutable_call_stack_profile());
762 std::string serialized_profile;
763 profile.SerializeToString(&serialized_profile);
764 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
765 base::TimeTicks::Now(), /*is_heap_profile=*/false,
766 std::move(serialized_profile));
767 }
768
769 } // namespace
770
771 // Checks that profiles which have been received but not send out are listed
772 // as successfully collected.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedOnReceivedNotSent)773 TEST_F(CallStackProfileMetricsProviderTest,
774 SuccessfullyCollectedOnReceivedNotSent) {
775 CallStackProfileMetricsProvider provider;
776 provider.OnRecordingEnabled();
777 RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
778 ReceiveSerializedProfile(metrics::GPU_PROCESS, metrics::MAIN_THREAD);
779
780 EXPECT_THAT(
781 CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
782 UnorderedElementsAre(
783 Pair(Eq(metrics::GPU_PROCESS),
784 UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)),
785 Pair(Eq(metrics::MAIN_THREAD), Eq(1))))));
786 }
787
788 // Checks that profiles which have been send out are listed as successfully
789 // collected.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedOnSent)790 TEST_F(CallStackProfileMetricsProviderTest, SuccessfullyCollectedOnSent) {
791 CallStackProfileMetricsProvider provider;
792 provider.OnRecordingEnabled();
793 RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
794 ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::IO_THREAD);
795
796 ChromeUserMetricsExtension uma_proto;
797 provider.ProvideCurrentSessionData(&uma_proto);
798 EXPECT_EQ(2, uma_proto.sampled_profile().size());
799
800 EXPECT_THAT(
801 CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
802 UnorderedElementsAre(
803 Pair(Eq(metrics::GPU_PROCESS),
804 UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)))),
805 Pair(Eq(metrics::BROWSER_PROCESS),
806 UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1))))));
807 }
808
809 // Checks that profiles which are send and profiles which are unsent are
810 // correctly summed together.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedMixedSentUnsent)811 TEST_F(CallStackProfileMetricsProviderTest,
812 SuccessfullyCollectedMixedSentUnsent) {
813 CallStackProfileMetricsProvider provider;
814 provider.OnRecordingEnabled();
815 RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
816 ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::IO_THREAD);
817
818 // Send the first 2 metrics.
819 ChromeUserMetricsExtension uma_proto;
820 provider.ProvideCurrentSessionData(&uma_proto);
821 EXPECT_EQ(2, uma_proto.sampled_profile().size());
822
823 RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
824 ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::MAIN_THREAD);
825
826 EXPECT_THAT(
827 CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
828 UnorderedElementsAre(
829 Pair(Eq(metrics::GPU_PROCESS),
830 UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(2)))),
831 Pair(Eq(metrics::BROWSER_PROCESS),
832 UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)),
833 Pair(Eq(metrics::MAIN_THREAD), Eq(1))))));
834 }
835
836 // Checks that "unsuccessful" profiles (profiles with 1 or no stack) are not
837 // counted.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedIgnoresUnsuccessful)838 TEST_F(CallStackProfileMetricsProviderTest,
839 SuccessfullyCollectedIgnoresUnsuccessful) {
840 CallStackProfileMetricsProvider provider;
841 provider.OnRecordingEnabled();
842 RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
843 ReceiveSerializedProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
844
845 {
846 SampledProfile no_stack_profile;
847 no_stack_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
848 no_stack_profile.set_process(metrics::BROWSER_PROCESS);
849 no_stack_profile.set_thread(metrics::MAIN_THREAD);
850 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
851 no_stack_profile);
852 std::string serialized_no_stack_profile;
853 no_stack_profile.SerializeToString(&serialized_no_stack_profile);
854 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
855 base::TimeTicks::Now(), /*is_heap_profile=*/false,
856 std::move(serialized_no_stack_profile));
857 }
858
859 {
860 SampledProfile one_frame_profile;
861 one_frame_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
862 one_frame_profile.set_process(metrics::BROWSER_PROCESS);
863 one_frame_profile.set_thread(metrics::MAIN_THREAD);
864 CallStackProfile::Stack* stack =
865 one_frame_profile.mutable_call_stack_profile()->add_stack();
866 CallStackProfile::Location* frame = stack->add_frame();
867 frame->set_address(123);
868 frame->set_module_id_index(1);
869 CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
870 one_frame_profile);
871 std::string serialized_one_frame_profile;
872 one_frame_profile.SerializeToString(&serialized_one_frame_profile);
873 CallStackProfileMetricsProvider::ReceiveSerializedProfile(
874 base::TimeTicks::Now(), /*is_heap_profile=*/false,
875 std::move(serialized_one_frame_profile));
876 }
877
878 // All the BROWSER_PROCESS profiles were unsuccessful, so only the GPU_PROCESS
879 // profiles should be counted.
880
881 EXPECT_THAT(CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
882 UnorderedElementsAre(Pair(
883 Eq(metrics::GPU_PROCESS),
884 UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(2))))));
885 }
886 #endif // BUILDFLAG(IS_CHROMEOS)
887
888 } // namespace metrics
889