// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/metrics/call_stack_profile_metrics_provider.h" #include #include #include "base/test/scoped_feature_list.h" #include "execution_context.pb.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h" namespace metrics { using ::testing::Eq; using ::testing::Pair; using ::testing::UnorderedElementsAre; // This test fixture enables the feature that // CallStackProfileMetricsProvider depends on to report a profile. class CallStackProfileMetricsProviderTest : public testing::Test { public: CallStackProfileMetricsProviderTest() { TestState::ResetStaticStateForTesting(); scoped_feature_list_.InitAndEnableFeature(kSamplingProfilerReporting); } CallStackProfileMetricsProviderTest( const CallStackProfileMetricsProviderTest&) = delete; CallStackProfileMetricsProviderTest& operator=( const CallStackProfileMetricsProviderTest&) = delete; protected: // Exposes the feature from the CallStackProfileMetricsProvider. class TestState : public CallStackProfileMetricsProvider { public: using CallStackProfileMetricsProvider::ResetStaticStateForTesting; }; private: base::test::ScopedFeatureList scoped_feature_list_; }; // Checks that the unserialized pending profile is encoded in the session data. TEST_F(CallStackProfileMetricsProviderTest, ProvideCurrentSessionDataUnserialized) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), profile); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(1, uma_proto.sampled_profile().size()); EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, uma_proto.sampled_profile(0).trigger_event()); } // Checks that the serialized pending profile is encoded in the session data. TEST_F(CallStackProfileMetricsProviderTest, ProvideCurrentSessionDataSerialized) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); std::string contents; { SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); profile.SerializeToString(&contents); } CallStackProfileMetricsProvider::ReceiveSerializedProfile( base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(contents)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(1, uma_proto.sampled_profile().size()); EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, uma_proto.sampled_profile(0).trigger_event()); } // Checks that both the unserialized and serialized pending profiles are // encoded in the session data. TEST_F(CallStackProfileMetricsProviderTest, ProvideCurrentSessionDataUnserializedAndSerialized) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); // Receive an unserialized profile. SampledProfile profile; profile.set_trigger_event(SampledProfile::PROCESS_STARTUP); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), std::move(profile)); // Receive a serialized profile. std::string contents; { SampledProfile serialized_profile; serialized_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); serialized_profile.SerializeToString(&contents); } CallStackProfileMetricsProvider::ReceiveSerializedProfile( base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(contents)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(2, uma_proto.sampled_profile().size()); EXPECT_EQ(SampledProfile::PROCESS_STARTUP, uma_proto.sampled_profile(0).trigger_event()); EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, uma_proto.sampled_profile(1).trigger_event()); } // Checks that the pending profiles above the total cap are dropped therefore // not encoded in the session data. TEST_F(CallStackProfileMetricsProviderTest, ProvideCurrentSessionDataExceedTotalCap) { // The value must be consistent with that in // call_stack_profile_metrics_provider.cc so that this test is meaningful. const int kMaxPendingProfiles = 1250; CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); // Receive (kMaxPendingProfiles + 1) profiles. for (int i = 0; i < kMaxPendingProfiles + 1; ++i) { SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), std::move(profile)); } ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); // Only kMaxPendingProfiles profiles are encoded, with the additional one // dropped. ASSERT_EQ(kMaxPendingProfiles, uma_proto.sampled_profile().size()); for (int i = 0; i < kMaxPendingProfiles; ++i) { EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, uma_proto.sampled_profile(i).trigger_event()); } } // Checks that the pending profile is provided to ProvideCurrentSessionData // when collected before CallStackProfileMetricsProvider is instantiated. TEST_F(CallStackProfileMetricsProviderTest, ProfileProvidedWhenCollectedBeforeInstantiation) { CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), SampledProfile()); CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(1, uma_proto.sampled_profile_size()); } // Checks that the pending profile is not provided to ProvideCurrentSessionData // while recording is disabled. TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedWhileDisabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingDisabled(); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), SampledProfile()); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } // Checks that the pending profile is not provided to ProvideCurrentSessionData // if recording is disabled while profiling. TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedAfterChangeToDisabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); base::TimeTicks profile_start_time = base::TimeTicks::Now(); provider.OnRecordingDisabled(); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, SampledProfile()); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } // Checks that the pending profile is not provided to ProvideCurrentSessionData // if recording is enabled, but then disabled and reenabled while profiling. TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedAfterChangeToDisabledThenEnabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); base::TimeTicks profile_start_time = base::TimeTicks::Now(); provider.OnRecordingDisabled(); provider.OnRecordingEnabled(); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, SampledProfile()); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } // Checks that the pending profile is provided to ProvideCurrentSessionData // if recording is disabled, but then enabled while profiling. TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedAfterChangeFromDisabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingDisabled(); base::TimeTicks profile_start_time = base::TimeTicks::Now(); provider.OnRecordingEnabled(); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, SampledProfile()); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } // Checks that a heap profile is not reported when recording is disabled. TEST_F(CallStackProfileMetricsProviderTest, HeapProfileNotProvidedWhenDisabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingDisabled(); base::TimeTicks profile_start_time = base::TimeTicks::Now(); // Unserialized profile. SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile); // Serialized profile. std::string contents; profile.SerializeToString(&contents); CallStackProfileMetricsProvider::ReceiveSerializedProfile( profile_start_time, /*is_heap_profile=*/true, std::move(contents)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } // Checks that a heap profile is provided to ProvideCurrentSessionData // if recording is enabled. TEST_F(CallStackProfileMetricsProviderTest, HeapProfileProvidedWhenEnabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); base::TimeTicks profile_start_time = base::TimeTicks::Now(); // Unserialized profile. SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile); // Serialized profile. std::string contents; profile.SerializeToString(&contents); CallStackProfileMetricsProvider::ReceiveSerializedProfile( profile_start_time, /*is_heap_profile=*/true, std::move(contents)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(2, uma_proto.sampled_profile_size()); } // Checks that heap profiles but not CPU profiles are reported when sampling CPU // Finch is disabled. TEST_F(CallStackProfileMetricsProviderTest, CpuProfileNotProvidedWithoutFinch) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndDisableFeature(kSamplingProfilerReporting); CallStackProfileMetricsProvider provider; base::TimeTicks profile_start_time = base::TimeTicks::Now(); // Unserialized profiles. SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, profile); SampledProfile heap_profile; heap_profile.set_trigger_event(SampledProfile::PERIODIC_HEAP_COLLECTION); CallStackProfileMetricsProvider::ReceiveProfile(profile_start_time, heap_profile); // Serialized profiles. std::string contents; profile.SerializeToString(&contents); CallStackProfileMetricsProvider::ReceiveSerializedProfile( profile_start_time, /*is_heap_profile=*/false, std::move(contents)); std::string heap_contents; heap_profile.SerializeToString(&heap_contents); CallStackProfileMetricsProvider::ReceiveSerializedProfile( profile_start_time, /*is_heap_profile=*/true, std::move(heap_contents)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(2, uma_proto.sampled_profile_size()); EXPECT_EQ(SampledProfile::PERIODIC_HEAP_COLLECTION, uma_proto.sampled_profile(0).trigger_event()); EXPECT_EQ(SampledProfile::PERIODIC_HEAP_COLLECTION, uma_proto.sampled_profile(1).trigger_event()); } #if BUILDFLAG(IS_CHROMEOS) namespace { // Sets |call_stack_profile| up enough to pass WasMinimallySuccessful() void MakeMinimallySuccessfulCallStackProfile( CallStackProfile* call_stack_profile) { CallStackProfile::Stack* stack = call_stack_profile->add_stack(); CallStackProfile::Location* frame = stack->add_frame(); frame->set_address(123); frame->set_module_id_index(1); frame = stack->add_frame(); frame->set_address(456); frame->set_module_id_index(0); } // Makes a minimally successful SampledProfile and sends it to ReceiveProfile. void RecieveProfile(metrics::Process process, metrics::Thread thread) { SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); profile.set_process(process); profile.set_thread(thread); MakeMinimallySuccessfulCallStackProfile(profile.mutable_call_stack_profile()); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), profile); } // Makes a minimally successful SampledProfile and sends it to // ReceiveSerializedProfile. void ReceiveSerializedProfile(metrics::Process process, metrics::Thread thread) { SampledProfile profile; profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); profile.set_process(process); profile.set_thread(thread); MakeMinimallySuccessfulCallStackProfile(profile.mutable_call_stack_profile()); std::string serialized_profile; profile.SerializeToString(&serialized_profile); CallStackProfileMetricsProvider::ReceiveSerializedProfile( base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(serialized_profile)); } } // namespace // Checks that profiles which have been received but not send out are listed // as successfully collected. TEST_F(CallStackProfileMetricsProviderTest, SuccessfullyCollectedOnReceivedNotSent) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD); ReceiveSerializedProfile(metrics::GPU_PROCESS, metrics::MAIN_THREAD); EXPECT_THAT( CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(), UnorderedElementsAre( Pair(Eq(metrics::GPU_PROCESS), UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)), Pair(Eq(metrics::MAIN_THREAD), Eq(1)))))); } // Checks that profiles which have been send out are listed as successfully // collected. TEST_F(CallStackProfileMetricsProviderTest, SuccessfullyCollectedOnSent) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD); ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::IO_THREAD); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(2, uma_proto.sampled_profile().size()); EXPECT_THAT( CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(), UnorderedElementsAre( Pair(Eq(metrics::GPU_PROCESS), UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)))), Pair(Eq(metrics::BROWSER_PROCESS), UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)))))); } // Checks that profiles which are send and profiles which are unsent are // correctly summed together. TEST_F(CallStackProfileMetricsProviderTest, SuccessfullyCollectedMixedSentUnsent) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD); ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::IO_THREAD); // Send the first 2 metrics. ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(2, uma_proto.sampled_profile().size()); RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD); ReceiveSerializedProfile(metrics::BROWSER_PROCESS, metrics::MAIN_THREAD); EXPECT_THAT( CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(), UnorderedElementsAre( Pair(Eq(metrics::GPU_PROCESS), UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(2)))), Pair(Eq(metrics::BROWSER_PROCESS), UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(1)), Pair(Eq(metrics::MAIN_THREAD), Eq(1)))))); } // Checks that "unsuccessful" profiles (profiles with 1 or no stack) are not // counted. TEST_F(CallStackProfileMetricsProviderTest, SuccessfullyCollectedIgnoresUnsuccessful) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); RecieveProfile(metrics::GPU_PROCESS, metrics::IO_THREAD); ReceiveSerializedProfile(metrics::GPU_PROCESS, metrics::IO_THREAD); { SampledProfile no_stack_profile; no_stack_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); no_stack_profile.set_process(metrics::BROWSER_PROCESS); no_stack_profile.set_thread(metrics::MAIN_THREAD); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), no_stack_profile); std::string serialized_no_stack_profile; no_stack_profile.SerializeToString(&serialized_no_stack_profile); CallStackProfileMetricsProvider::ReceiveSerializedProfile( base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(serialized_no_stack_profile)); } { SampledProfile one_frame_profile; one_frame_profile.set_trigger_event(SampledProfile::PERIODIC_COLLECTION); one_frame_profile.set_process(metrics::BROWSER_PROCESS); one_frame_profile.set_thread(metrics::MAIN_THREAD); CallStackProfile::Stack* stack = one_frame_profile.mutable_call_stack_profile()->add_stack(); CallStackProfile::Location* frame = stack->add_frame(); frame->set_address(123); frame->set_module_id_index(1); CallStackProfileMetricsProvider::ReceiveProfile(base::TimeTicks::Now(), one_frame_profile); std::string serialized_one_frame_profile; one_frame_profile.SerializeToString(&serialized_one_frame_profile); CallStackProfileMetricsProvider::ReceiveSerializedProfile( base::TimeTicks::Now(), /*is_heap_profile=*/false, std::move(serialized_one_frame_profile)); } // All the BROWSER_PROCESS profiles were unsuccessful, so only the GPU_PROCESS // profiles should be counted. EXPECT_THAT(CallStackProfileMetricsProvider::GetSuccessfullyCollectedCounts(), UnorderedElementsAre(Pair( Eq(metrics::GPU_PROCESS), UnorderedElementsAre(Pair(Eq(metrics::IO_THREAD), Eq(2)))))); } #endif // BUILDFLAG(IS_CHROMEOS) } // namespace metrics