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/test/scoped_feature_list.h"
11 #include "execution_context.pb.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
15 
16 namespace metrics {
17 
18 using ::testing::Eq;
19 using ::testing::Pair;
20 using ::testing::UnorderedElementsAre;
21 
22 // This test fixture enables the feature that
23 // CallStackProfileMetricsProvider depends on to report a profile.
24 class CallStackProfileMetricsProviderTest : public testing::Test {
25  public:
CallStackProfileMetricsProviderTest()26   CallStackProfileMetricsProviderTest() {
27     TestState::ResetStaticStateForTesting();
28     scoped_feature_list_.InitAndEnableFeature(kSamplingProfilerReporting);
29   }
30 
31   CallStackProfileMetricsProviderTest(
32       const CallStackProfileMetricsProviderTest&) = delete;
33   CallStackProfileMetricsProviderTest& operator=(
34       const CallStackProfileMetricsProviderTest&) = delete;
35 
36  protected:
37   // Exposes the feature from the CallStackProfileMetricsProvider.
38   class TestState : public CallStackProfileMetricsProvider {
39    public:
40     using CallStackProfileMetricsProvider::ResetStaticStateForTesting;
41   };
42 
43  private:
44   base::test::ScopedFeatureList scoped_feature_list_;
45 };
46 
47 // Checks that the unserialized pending profile is encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataUnserialized)48 TEST_F(CallStackProfileMetricsProviderTest,
49        ProvideCurrentSessionDataUnserialized) {
50   CallStackProfileMetricsProvider provider;
51   provider.OnRecordingEnabled();
52   SampledProfile profile;
53   profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
54   CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
55                                                   profile);
56   ChromeUserMetricsExtension uma_proto;
57   provider.ProvideCurrentSessionData(&uma_proto);
58   ASSERT_EQ(1, uma_proto.sampled_profile().size());
59   EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
60             uma_proto.sampled_profile(0).trigger_event());
61 }
62 
63 // Checks that the serialized pending profile is encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataSerialized)64 TEST_F(CallStackProfileMetricsProviderTest,
65        ProvideCurrentSessionDataSerialized) {
66   CallStackProfileMetricsProvider provider;
67   provider.OnRecordingEnabled();
68   std::string contents;
69   {
70     SampledProfile profile;
71     profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
72     profile.SerializeToString(&contents);
73   }
74   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
75       base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(contents));
76   ChromeUserMetricsExtension uma_proto;
77   provider.ProvideCurrentSessionData(&uma_proto);
78   ASSERT_EQ(1, uma_proto.sampled_profile().size());
79   EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
80             uma_proto.sampled_profile(0).trigger_event());
81 }
82 
83 // Checks that both the unserialized and serialized pending profiles are
84 // encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataUnserializedAndSerialized)85 TEST_F(CallStackProfileMetricsProviderTest,
86        ProvideCurrentSessionDataUnserializedAndSerialized) {
87   CallStackProfileMetricsProvider provider;
88   provider.OnRecordingEnabled();
89 
90   // Receive an unserialized profile.
91   SampledProfile profile;
92   profile.set_trigger_event(SampledProfile::PROCESS_STARTUP);
93   CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
94                                                   std::move(profile));
95 
96   // Receive a serialized profile.
97   std::string contents;
98   {
99     SampledProfile serialized_profile;
100     serialized_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
101     serialized_profile.SerializeToString(&contents);
102   }
103   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
104       base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(contents));
105 
106   ChromeUserMetricsExtension uma_proto;
107   provider.ProvideCurrentSessionData(&uma_proto);
108   ASSERT_EQ(2, uma_proto.sampled_profile().size());
109   EXPECT_EQ(SampledProfile::PROCESS_STARTUP,
110             uma_proto.sampled_profile(0).trigger_event());
111   EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
112             uma_proto.sampled_profile(1).trigger_event());
113 }
114 
115 // Checks that the pending profiles above the total cap are dropped therefore
116 // not encoded in the session data.
TEST_F(CallStackProfileMetricsProviderTest,ProvideCurrentSessionDataExceedTotalCap)117 TEST_F(CallStackProfileMetricsProviderTest,
118        ProvideCurrentSessionDataExceedTotalCap) {
119   // The value must be consistent with that in
120   // call_stack_profile_metrics_provider.cc so that this test is meaningful.
121   const int kMaxPendingProfiles = 1250;
122 
123   CallStackProfileMetricsProvider provider;
124   provider.OnRecordingEnabled();
125 
126   // Receive (kMaxPendingProfiles + 1) profiles.
127   for (int i = 0; i < kMaxPendingProfiles + 1; ++i) {
128     SampledProfile profile;
129     profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
130     CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
131                                                     std::move(profile));
132   }
133 
134   ChromeUserMetricsExtension uma_proto;
135   provider.ProvideCurrentSessionData(&uma_proto);
136 
137   // Only kMaxPendingProfiles profiles are encoded, with the additional one
138   // dropped.
139   ASSERT_EQ(kMaxPendingProfiles, uma_proto.sampled_profile().size());
140   for (int i = 0; i < kMaxPendingProfiles; ++i) {
141     EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
142               uma_proto.sampled_profile(i).trigger_event());
143   }
144 }
145 
146 // Checks that the pending profile is provided to ProvideCurrentSessionData
147 // when collected before CallStackProfileMetricsProvider is instantiated.
TEST_F(CallStackProfileMetricsProviderTest,ProfileProvidedWhenCollectedBeforeInstantiation)148 TEST_F(CallStackProfileMetricsProviderTest,
149        ProfileProvidedWhenCollectedBeforeInstantiation) {
150   CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
151                                                   SampledProfile());
152   CallStackProfileMetricsProvider provider;
153   provider.OnRecordingEnabled();
154   ChromeUserMetricsExtension uma_proto;
155   provider.ProvideCurrentSessionData(&uma_proto);
156   EXPECT_EQ(1, uma_proto.sampled_profile_size());
157 }
158 
159 // Checks that the pending profile is not provided to ProvideCurrentSessionData
160 // while recording is disabled.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedWhileDisabled)161 TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedWhileDisabled) {
162   CallStackProfileMetricsProvider provider;
163   provider.OnRecordingDisabled();
164   CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
165                                                   SampledProfile());
166   ChromeUserMetricsExtension uma_proto;
167   provider.ProvideCurrentSessionData(&uma_proto);
168   EXPECT_EQ(0, uma_proto.sampled_profile_size());
169 }
170 
171 // Checks that the pending profile is not provided to ProvideCurrentSessionData
172 // if recording is disabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedAfterChangeToDisabled)173 TEST_F(CallStackProfileMetricsProviderTest,
174        ProfileNotProvidedAfterChangeToDisabled) {
175   CallStackProfileMetricsProvider provider;
176   provider.OnRecordingEnabled();
177   base::TimeTicks profile_start_time = base::TimeTicks::Now();
178   provider.OnRecordingDisabled();
179   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
180                                                   SampledProfile());
181   ChromeUserMetricsExtension uma_proto;
182   provider.ProvideCurrentSessionData(&uma_proto);
183   EXPECT_EQ(0, uma_proto.sampled_profile_size());
184 }
185 
186 // Checks that the pending profile is not provided to ProvideCurrentSessionData
187 // if recording is enabled, but then disabled and reenabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedAfterChangeToDisabledThenEnabled)188 TEST_F(CallStackProfileMetricsProviderTest,
189        ProfileNotProvidedAfterChangeToDisabledThenEnabled) {
190   CallStackProfileMetricsProvider provider;
191   provider.OnRecordingEnabled();
192   base::TimeTicks profile_start_time = base::TimeTicks::Now();
193   provider.OnRecordingDisabled();
194   provider.OnRecordingEnabled();
195   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
196                                                   SampledProfile());
197   ChromeUserMetricsExtension uma_proto;
198   provider.ProvideCurrentSessionData(&uma_proto);
199   EXPECT_EQ(0, uma_proto.sampled_profile_size());
200 }
201 
202 // Checks that the pending profile is provided to ProvideCurrentSessionData
203 // if recording is disabled, but then enabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,ProfileNotProvidedAfterChangeFromDisabled)204 TEST_F(CallStackProfileMetricsProviderTest,
205        ProfileNotProvidedAfterChangeFromDisabled) {
206   CallStackProfileMetricsProvider provider;
207   provider.OnRecordingDisabled();
208   base::TimeTicks profile_start_time = base::TimeTicks::Now();
209   provider.OnRecordingEnabled();
210   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
211                                                   SampledProfile());
212   ChromeUserMetricsExtension uma_proto;
213   provider.ProvideCurrentSessionData(&uma_proto);
214   EXPECT_EQ(0, uma_proto.sampled_profile_size());
215 }
216 
217 // Checks that a heap profile is not reported when recording is disabled.
TEST_F(CallStackProfileMetricsProviderTest,HeapProfileNotProvidedWhenDisabled)218 TEST_F(CallStackProfileMetricsProviderTest,
219        HeapProfileNotProvidedWhenDisabled) {
220   CallStackProfileMetricsProvider provider;
221   provider.OnRecordingDisabled();
222   base::TimeTicks profile_start_time = base::TimeTicks::Now();
223 
224   // Unserialized profile.
225   SampledProfile profile;
226   profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
227   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
228 
229   // Serialized profile.
230   std::string contents;
231   profile.SerializeToString(&contents);
232   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
233       profile_start_time, /*is_heap_profile=*/true, std::move(contents));
234 
235   ChromeUserMetricsExtension uma_proto;
236   provider.ProvideCurrentSessionData(&uma_proto);
237   EXPECT_EQ(0, uma_proto.sampled_profile_size());
238 }
239 
240 // Checks that a heap profile is provided to ProvideCurrentSessionData
241 // if recording is enabled.
TEST_F(CallStackProfileMetricsProviderTest,HeapProfileProvidedWhenEnabled)242 TEST_F(CallStackProfileMetricsProviderTest, HeapProfileProvidedWhenEnabled) {
243   CallStackProfileMetricsProvider provider;
244   provider.OnRecordingEnabled();
245   base::TimeTicks profile_start_time = base::TimeTicks::Now();
246 
247   // Unserialized profile.
248   SampledProfile profile;
249   profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
250   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
251 
252   // Serialized profile.
253   std::string contents;
254   profile.SerializeToString(&contents);
255   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
256       profile_start_time, /*is_heap_profile=*/true, std::move(contents));
257 
258   ChromeUserMetricsExtension uma_proto;
259   provider.ProvideCurrentSessionData(&uma_proto);
260   EXPECT_EQ(2, uma_proto.sampled_profile_size());
261 }
262 
263 // Checks that heap profiles but not CPU profiles are reported when sampling CPU
264 // Finch is disabled.
TEST_F(CallStackProfileMetricsProviderTest,CpuProfileNotProvidedWithoutFinch)265 TEST_F(CallStackProfileMetricsProviderTest, CpuProfileNotProvidedWithoutFinch) {
266   base::test::ScopedFeatureList scoped_feature_list;
267   scoped_feature_list.InitAndDisableFeature(kSamplingProfilerReporting);
268   CallStackProfileMetricsProvider provider;
269   base::TimeTicks profile_start_time = base::TimeTicks::Now();
270 
271   // Unserialized profiles.
272   SampledProfile profile;
273   profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
274   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile);
275 
276   SampledProfile heap_profile;
277   heap_profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION);
278   CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time,
279                                                   heap_profile);
280 
281   // Serialized profiles.
282   std::string contents;
283   profile.SerializeToString(&contents);
284   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
285       profile_start_time, /*is_heap_profile=*/false, std::move(contents));
286 
287   std::string heap_contents;
288   heap_profile.SerializeToString(&heap_contents);
289   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
290       profile_start_time, /*is_heap_profile=*/true, std::move(heap_contents));
291 
292   ChromeUserMetricsExtension uma_proto;
293   provider.ProvideCurrentSessionData(&uma_proto);
294   ASSERT_EQ(2, uma_proto.sampled_profile_size());
295   EXPECT_EQ(SampledProfile::PERIODIC_HEAP_COLLECTION,
296             uma_proto.sampled_profile(0).trigger_event());
297   EXPECT_EQ(SampledProfile::PERIODIC_HEAP_COLLECTION,
298             uma_proto.sampled_profile(1).trigger_event());
299 }
300 
301 #if BUILDFLAG(IS_CHROMEOS)
302 
303 namespace {
304 
305 // Sets |call_stack_profile| up enough to pass WasMinimallySuccessful()
MakeMinimallySuccessfulCallStackProfile(CallStackProfile * call_stack_profile)306 void MakeMinimallySuccessfulCallStackProfile(
307     CallStackProfile* call_stack_profile) {
308   CallStackProfile::Stack* stack = call_stack_profile->add_stack();
309   CallStackProfile::Location* frame = stack->add_frame();
310   frame->set_address(123);
311   frame->set_module_id_index(1);
312   frame = stack->add_frame();
313   frame->set_address(456);
314   frame->set_module_id_index(0);
315 }
316 
317 // Makes a minimally successful SampledProfile and sends it to ReceiveProfile.
RecieveProfile(metrics::Process process,metrics::Thread thread)318 void RecieveProfile(metrics::Process process, metrics::Thread thread) {
319   SampledProfile profile;
320   profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
321   profile.set_process(process);
322   profile.set_thread(thread);
323   MakeMinimallySuccessfulCallStackProfile(profile.mutable_call_stack_profile());
324   CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
325                                                   profile);
326 }
327 
328 // Makes a minimally successful SampledProfile and sends it to
329 // ReceiveSerializedProfile.
ReceiveSerializedProfile(metrics::Process process,metrics::Thread thread)330 void ReceiveSerializedProfile(metrics::Process process,
331                               metrics::Thread thread) {
332   SampledProfile profile;
333   profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
334   profile.set_process(process);
335   profile.set_thread(thread);
336   MakeMinimallySuccessfulCallStackProfile(profile.mutable_call_stack_profile());
337   std::string serialized_profile;
338   profile.SerializeToString(&serialized_profile);
339   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
340       base::TimeTicks::Now(), /*is_heap_profile=*/false,
341       std::move(serialized_profile));
342 }
343 
344 }  // namespace
345 
346 // Checks that profiles which have been received but not send out are listed
347 // as successfully collected.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedOnReceivedNotSent)348 TEST_F(CallStackProfileMetricsProviderTest,
349        SuccessfullyCollectedOnReceivedNotSent) {
350   CallStackProfileMetricsProvider provider;
351   provider.OnRecordingEnabled();
352   RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
353   ReceiveSerializedProfile(metrics::GPU_PROCESS, metrics::MAIN_THREAD);
354 
355   EXPECT_THAT(
356       CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
357       UnorderedElementsAre(
358           Pair(Eq(metrics::GPU_PROCESS),
359                UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)),
360                                     Pair(Eq(metrics::MAIN_THREAD), Eq(1))))));
361 }
362 
363 // Checks that profiles which have been send out are listed as successfully
364 // collected.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedOnSent)365 TEST_F(CallStackProfileMetricsProviderTest, SuccessfullyCollectedOnSent) {
366   CallStackProfileMetricsProvider provider;
367   provider.OnRecordingEnabled();
368   RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
369   ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::IO_THREAD);
370 
371   ChromeUserMetricsExtension uma_proto;
372   provider.ProvideCurrentSessionData(&uma_proto);
373   EXPECT_EQ(2, uma_proto.sampled_profile().size());
374 
375   EXPECT_THAT(
376       CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
377       UnorderedElementsAre(
378           Pair(Eq(metrics::GPU_PROCESS),
379                UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)))),
380           Pair(Eq(metrics::BROWSER_PROCESS),
381                UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1))))));
382 }
383 
384 // Checks that profiles which are send and profiles which are unsent are
385 // correctly summed together.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedMixedSentUnsent)386 TEST_F(CallStackProfileMetricsProviderTest,
387        SuccessfullyCollectedMixedSentUnsent) {
388   CallStackProfileMetricsProvider provider;
389   provider.OnRecordingEnabled();
390   RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
391   ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::IO_THREAD);
392 
393   // Send the first 2 metrics.
394   ChromeUserMetricsExtension uma_proto;
395   provider.ProvideCurrentSessionData(&uma_proto);
396   EXPECT_EQ(2, uma_proto.sampled_profile().size());
397 
398   RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
399   ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::MAIN_THREAD);
400 
401   EXPECT_THAT(
402       CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
403       UnorderedElementsAre(
404           Pair(Eq(metrics::GPU_PROCESS),
405                UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(2)))),
406           Pair(Eq(metrics::BROWSER_PROCESS),
407                UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)),
408                                     Pair(Eq(metrics::MAIN_THREAD), Eq(1))))));
409 }
410 
411 // Checks that "unsuccessful" profiles (profiles with 1 or no stack) are not
412 // counted.
TEST_F(CallStackProfileMetricsProviderTest,SuccessfullyCollectedIgnoresUnsuccessful)413 TEST_F(CallStackProfileMetricsProviderTest,
414        SuccessfullyCollectedIgnoresUnsuccessful) {
415   CallStackProfileMetricsProvider provider;
416   provider.OnRecordingEnabled();
417   RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
418   ReceiveSerializedProfile(metrics::GPU_PROCESS, metrics::IO_THREAD);
419 
420   {
421     SampledProfile no_stack_profile;
422     no_stack_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
423     no_stack_profile.set_process(metrics::BROWSER_PROCESS);
424     no_stack_profile.set_thread(metrics::MAIN_THREAD);
425     CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
426                                                     no_stack_profile);
427     std::string serialized_no_stack_profile;
428     no_stack_profile.SerializeToString(&serialized_no_stack_profile);
429     CallStackProfileMetricsProvider::ReceiveSerializedProfile(
430         base::TimeTicks::Now(), /*is_heap_profile=*/false,
431         std::move(serialized_no_stack_profile));
432   }
433 
434   {
435     SampledProfile one_frame_profile;
436     one_frame_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
437     one_frame_profile.set_process(metrics::BROWSER_PROCESS);
438     one_frame_profile.set_thread(metrics::MAIN_THREAD);
439     CallStackProfile::Stack* stack =
440         one_frame_profile.mutable_call_stack_profile()->add_stack();
441     CallStackProfile::Location* frame = stack->add_frame();
442     frame->set_address(123);
443     frame->set_module_id_index(1);
444     CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(),
445                                                     one_frame_profile);
446     std::string serialized_one_frame_profile;
447     one_frame_profile.SerializeToString(&serialized_one_frame_profile);
448     CallStackProfileMetricsProvider::ReceiveSerializedProfile(
449         base::TimeTicks::Now(), /*is_heap_profile=*/false,
450         std::move(serialized_one_frame_profile));
451   }
452 
453   // All the BROWSER_PROCESS profiles were unsuccessful, so only the GPU_PROCESS
454   // profiles should be counted.
455 
456   EXPECT_THAT(CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(),
457               UnorderedElementsAre(Pair(
458                   Eq(metrics::GPU_PROCESS),
459                   UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(2))))));
460 }
461 #endif  // BUILDFLAG(IS_CHROMEOS)
462 
463 }  // namespace metrics
464