blob: ec9090b8c41afd94e1999ac25019bb55f8473734 [file] [log] [blame]
// 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 <string>
#include <utility>
#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