• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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