Commit cbc9ef93 authored by Gabriel Marin's avatar Gabriel Marin Committed by Commit Bot

ProfileProvider: move more code into MetricCollector for better reuse

Move additional logic to the base class MetricCollector for reuse between
different collector implementations, e.g. writing outcomes to UMA histograms,
removal of unknown fields from protos, saving profiles to the local cache.
This change enables making two additional fields private.

Moved tests for discarding unknown fields and for saving perf data and perf
stat protos, to the metric collector unit tests.

BUG=b:110205489
TEST=Unit tests pass

Change-Id: I8877e9abc6ee8d89e6be391063d3ddf7ba231bc0
Reviewed-on: https://chromium-review.googlesource.com/c/1392352Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Commit-Queue: Gabriel Marin <gmx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#619836}
parent efd14a14
......@@ -4,7 +4,9 @@
#include "chrome/browser/metrics/perf/metric_collector.h"
#include "base/metrics/histogram_functions.h"
#include "base/rand_util.h"
#include "base/system/sys_info.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
......@@ -24,21 +26,62 @@ base::TimeDelta RandomTimeDelta(base::TimeDelta max) {
base::RandGenerator(max.InMicroseconds()));
}
// PerfDataProto is defined elsewhere with more fields than the definition in
// Chromium's copy of perf_data.proto. During deserialization, the protobuf
// data could contain fields that are defined elsewhere but not in
// perf_data.proto, resulting in some data in |unknown_fields| for the message
// types within PerfDataProto.
//
// This function deletes those dangling unknown fields if they are in messages
// containing strings. See comments in perf_data.proto describing the fields
// that have been intentionally left out. Note that all unknown fields will be
// removed from those messages, not just unknown string fields.
void RemoveUnknownFieldsFromMessagesWithStrings(PerfDataProto* proto) {
// Clean up PerfEvent::MMapEvent and PerfEvent::CommEvent.
for (PerfDataProto::PerfEvent& event : *proto->mutable_events()) {
if (event.has_comm_event())
event.mutable_comm_event()->mutable_unknown_fields()->clear();
if (event.has_mmap_event())
event.mutable_mmap_event()->mutable_unknown_fields()->clear();
}
// Clean up PerfBuildID.
for (PerfDataProto::PerfBuildID& build_id : *proto->mutable_build_ids()) {
build_id.mutable_unknown_fields()->clear();
}
// Clean up StringMetadata and StringMetadata::StringAndMd5sumPrefix.
if (proto->has_string_metadata()) {
proto->mutable_string_metadata()->mutable_unknown_fields()->clear();
if (proto->string_metadata().has_perf_command_line_whole()) {
proto->mutable_string_metadata()
->mutable_perf_command_line_whole()
->mutable_unknown_fields()
->clear();
}
}
}
} // namespace
MetricCollector::MetricCollector() {}
MetricCollector::MetricCollector(const std::string& uma_histogram)
: uma_histogram_(uma_histogram) {}
MetricCollector::MetricCollector(const CollectionParams& collection_params)
: collection_params_(collection_params) {}
MetricCollector::MetricCollector(const std::string& uma_histogram,
const CollectionParams& collection_params)
: collection_params_(collection_params), uma_histogram_(uma_histogram) {}
MetricCollector::~MetricCollector() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void MetricCollector::AddToUmaHistogram(CollectionAttemptStatus outcome) const {
base::UmaHistogramEnumeration(uma_histogram_, outcome,
CollectionAttemptStatus::NUM_OUTCOMES);
}
bool MetricCollector::GetSampledProfiles(
std::vector<SampledProfile>* sampled_profiles) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!ShouldUpload() || cached_profile_data_.empty())
if (!ShouldUpload())
return false;
sampled_profiles->insert(
......@@ -50,6 +93,12 @@ bool MetricCollector::GetSampledProfiles(
}
bool MetricCollector::ShouldUpload() const {
if (cached_profile_data_.empty()) {
AddToUmaHistogram(CollectionAttemptStatus::NOT_READY_TO_UPLOAD);
return false;
}
AddToUmaHistogram(CollectionAttemptStatus::SUCCESS);
return true;
}
......@@ -204,4 +253,56 @@ void MetricCollector::Deactivate() {
timer_.Stop();
}
void MetricCollector::SaveSerializedPerfProto(
std::unique_ptr<SampledProfile> sampled_profile,
PerfProtoType type,
const std::string& serialized_proto) {
if (serialized_proto.empty()) {
AddToUmaHistogram(CollectionAttemptStatus::ILLEGAL_DATA_RETURNED);
return;
}
switch (type) {
case PerfProtoType::PERF_TYPE_DATA: {
PerfDataProto perf_data_proto;
if (!perf_data_proto.ParseFromString(serialized_proto)) {
AddToUmaHistogram(CollectionAttemptStatus::PROTOBUF_NOT_PARSED);
return;
}
RemoveUnknownFieldsFromMessagesWithStrings(&perf_data_proto);
sampled_profile->mutable_perf_data()->Swap(&perf_data_proto);
break;
}
case PerfProtoType::PERF_TYPE_STAT: {
PerfStatProto perf_stat_proto;
if (!perf_stat_proto.ParseFromString(serialized_proto)) {
AddToUmaHistogram(CollectionAttemptStatus::PROTOBUF_NOT_PARSED);
return;
}
sampled_profile->mutable_perf_stat()->Swap(&perf_stat_proto);
break;
}
case PerfProtoType::PERF_TYPE_UNSUPPORTED:
AddToUmaHistogram(CollectionAttemptStatus::PROTOBUF_NOT_PARSED);
return;
}
sampled_profile->set_ms_after_boot(base::SysInfo::Uptime().InMilliseconds());
DCHECK(!login_time_.is_null());
sampled_profile->set_ms_after_login(
(base::TimeTicks::Now() - login_time_).InMilliseconds());
// Add the collected data to the container of collected SampledProfiles.
cached_profile_data_.resize(cached_profile_data_.size() + 1);
cached_profile_data_.back().Swap(sampled_profile.get());
}
size_t MetricCollector::cached_profile_data_size() const {
size_t data_size = 0;
for (size_t i = 0; i < cached_profile_data_.size(); ++i) {
data_size += cached_profile_data_[i].ByteSize();
}
return data_size;
}
} // namespace metrics
......@@ -25,8 +25,9 @@ class SampledProfile;
// pointer across threads safely.
class MetricCollector : public base::SupportsWeakPtr<MetricCollector> {
public:
MetricCollector();
explicit MetricCollector(const CollectionParams& collection_params);
explicit MetricCollector(const std::string& uma_histogram);
explicit MetricCollector(const std::string& uma_histogram,
const CollectionParams& collection_params);
virtual ~MetricCollector();
// Collector specific initialization.
......@@ -52,12 +53,38 @@ class MetricCollector : public base::SupportsWeakPtr<MetricCollector> {
void OnSessionRestoreDone(int num_tabs_restored);
protected:
// Perf proto type.
enum class PerfProtoType {
PERF_TYPE_DATA,
PERF_TYPE_STAT,
PERF_TYPE_UNSUPPORTED,
};
// Enumeration representing success and various failure modes for collecting
// and sending perf data.
enum class CollectionAttemptStatus {
SUCCESS,
NOT_READY_TO_UPLOAD,
NOT_READY_TO_COLLECT,
INCOGNITO_ACTIVE,
INCOGNITO_LAUNCHED,
PROTOBUF_NOT_PARSED,
ILLEGAL_DATA_RETURNED,
ALREADY_COLLECTING,
NUM_OUTCOMES
};
// Saves the given outcome to the uma histogram associated with the collector.
void AddToUmaHistogram(CollectionAttemptStatus outcome) const;
const CollectionParams& collection_params() const {
return collection_params_;
}
const base::OneShotTimer& timer() const { return timer_; }
base::TimeTicks login_time() const { return login_time_; }
// Collects perf data after a resume. |sleep_duration| is the duration the
// system was suspended before resuming. |time_after_resume_ms| is how long
// ago the system resumed.
......@@ -94,14 +121,18 @@ class MetricCollector : public base::SupportsWeakPtr<MetricCollector> {
// implementation can override this logic.
virtual bool ShouldUpload() const;
// Parameters controlling how profiles are collected.
CollectionParams collection_params_;
// Parses the given serialized perf proto of the given type (data or stat).
// If valid, it adds it to the given sampled_profile and stores it in the
// local profile data cache.
void SaveSerializedPerfProto(std::unique_ptr<SampledProfile> sampled_profile,
PerfProtoType type,
const std::string& serialized_proto);
// Vector of SampledProfile protobufs containing perf profiles.
std::vector<SampledProfile> cached_profile_data_;
// Returns the size of the cached profile data.
size_t cached_profile_data_size() const;
// Record of the last login time.
base::TimeTicks login_time_;
// Parameters controlling how profiles are collected.
CollectionParams collection_params_;
SEQUENCE_CHECKER(sequence_checker_);
......@@ -109,12 +140,22 @@ class MetricCollector : public base::SupportsWeakPtr<MetricCollector> {
// For scheduling collection of profile data.
base::OneShotTimer timer_;
// Vector of SampledProfile protobufs containing perf profiles.
std::vector<SampledProfile> cached_profile_data_;
// Record of the last login time.
base::TimeTicks login_time_;
// Record of the start of the upcoming profiling interval.
base::TimeTicks next_profiling_interval_start_;
// Tracks the last time a session restore was collected.
base::TimeTicks last_session_restore_collection_time_;
// Name of the histogram that represents the success and various failure modes
// for a collector.
std::string uma_histogram_;
DISALLOW_COPY_AND_ASSIGN(MetricCollector);
};
......
......@@ -16,14 +16,14 @@
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "third_party/protobuf/src/google/protobuf/wire_format_lite_inl.h"
namespace metrics {
namespace {
const long kMsAfterBoot = 10000;
const long kMsAfterLogin = 2000;
// Returns an example PerfDataProto. The contents don't have to make sense. They
// just need to constitute a semantically valid protobuf.
// |proto| is an output parameter that will contain the created protobuf.
......@@ -53,27 +53,66 @@ PerfDataProto GetExamplePerfDataProto() {
return proto;
}
// Returns an example PerfStatProto. The contents don't have to make sense. They
// just need to constitute a semantically valid protobuf.
// |result| is an output parameter that will contain the created protobuf.
PerfStatProto GetExamplePerfStatProto() {
PerfStatProto proto;
proto.set_command_line(
"perf stat -a -e cycles -e instructions -e branches -- sleep 2");
PerfStatProto_PerfStatLine* line1 = proto.add_line();
line1->set_time_ms(1000);
line1->set_count(2000);
line1->set_event_name("cycles");
PerfStatProto_PerfStatLine* line2 = proto.add_line();
line2->set_time_ms(2000);
line2->set_count(5678);
line2->set_event_name("instructions");
PerfStatProto_PerfStatLine* line3 = proto.add_line();
line3->set_time_ms(3000);
line3->set_count(9999);
line3->set_event_name("branches");
return proto;
}
// Creates a serialized data stream containing a string with a field tag number.
std::string SerializeStringFieldWithTag(int field, const std::string& value) {
std::string result;
google::protobuf::io::StringOutputStream string_stream(&result);
google::protobuf::io::CodedOutputStream output(&string_stream);
using google::protobuf::internal::WireFormatLite;
WireFormatLite::WriteTag(field, WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
&output);
output.WriteVarint32(value.size());
output.WriteString(value);
return result;
}
// Allows access to some private methods for testing.
class TestMetricCollector : public MetricCollector {
public:
TestMetricCollector() {}
TestMetricCollector() : MetricCollector("UMA.CWP.TestData") {}
explicit TestMetricCollector(const CollectionParams& collection_params)
: MetricCollector(collection_params) {}
: MetricCollector("UMA.CWP.TestData", collection_params) {}
void CollectProfile(
std::unique_ptr<SampledProfile> sampled_profile) override {
PerfDataProto perf_data_proto = GetExamplePerfDataProto();
sampled_profile->set_ms_after_boot(kMsAfterBoot);
sampled_profile->set_ms_after_login(kMsAfterLogin);
sampled_profile->mutable_perf_data()->Swap(&perf_data_proto);
// Add the collected data to the container of collected SampledProfiles.
cached_profile_data_.resize(cached_profile_data_.size() + 1);
cached_profile_data_.back().Swap(sampled_profile.get());
SaveSerializedPerfProto(std::move(sampled_profile),
PerfProtoType::PERF_TYPE_DATA,
perf_data_proto.SerializeAsString());
}
using MetricCollector::collection_params;
using MetricCollector::login_time_;
using MetricCollector::login_time;
using MetricCollector::PerfProtoType;
using MetricCollector::SaveSerializedPerfProto;
using MetricCollector::ScheduleIntervalCollection;
using MetricCollector::timer;
......@@ -90,7 +129,8 @@ class MetricCollectorTest : public testing::Test {
MetricCollectorTest()
: task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
task_runner_handle_(task_runner_),
perf_data_proto_(GetExamplePerfDataProto()) {}
perf_data_proto_(GetExamplePerfDataProto()),
perf_stat_proto_(GetExamplePerfStatProto()) {}
void SetUp() override {
CollectionParams test_params;
......@@ -101,6 +141,9 @@ class MetricCollectorTest : public testing::Test {
metric_collector_ = std::make_unique<TestMetricCollector>(test_params);
metric_collector_->Init();
// MetricCollector requires the user to be logged in.
metric_collector_->OnUserLoggedIn();
}
void TearDown() override { metric_collector_.reset(); }
......@@ -111,19 +154,22 @@ class MetricCollectorTest : public testing::Test {
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
// Store a sample perf data protobuf for testing.
// Store sample perf data/stat protobufs for testing.
PerfDataProto perf_data_proto_;
PerfStatProto perf_stat_proto_;
DISALLOW_COPY_AND_ASSIGN(MetricCollectorTest);
};
TEST_F(MetricCollectorTest, CheckSetup) {
EXPECT_GT(perf_data_proto_.ByteSize(), 0);
EXPECT_GT(perf_stat_proto_.ByteSize(), 0);
// Timer is not active before user logs in.
EXPECT_FALSE(metric_collector_->timer().IsRunning());
EXPECT_TRUE(metric_collector_->login_time_.is_null());
// Timer is active after user logs in.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time().is_null());
// There are no cached profiles at start.
std::vector<SampledProfile> stored_profiles;
EXPECT_FALSE(metric_collector_->GetSampledProfiles(&stored_profiles));
EXPECT_TRUE(stored_profiles.empty());
......@@ -132,7 +178,256 @@ TEST_F(MetricCollectorTest, CheckSetup) {
TEST_F(MetricCollectorTest, EnabledOnLogin) {
metric_collector_->OnUserLoggedIn();
EXPECT_TRUE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time_.is_null());
EXPECT_FALSE(metric_collector_->login_time().is_null());
}
TEST_F(MetricCollectorTest, EmptyProtosAreNotSaved) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA, std::string());
std::vector<SampledProfile> stored_profiles;
EXPECT_FALSE(metric_collector_->GetSampledProfiles(&stored_profiles));
}
TEST_F(MetricCollectorTest, PerfDataProto) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
TEST_F(MetricCollectorTest, PerfDataProto_UnknownFieldsDiscarded) {
// First add some unknown fields to MMapEvent, CommEvent, PerfBuildID, and
// StringAndMd5sumPrefix. The known field values don't have to make sense for
// perf data. They are just padding to avoid having an otherwise empty proto.
// The unknown field string contents don't have to make sense as serialized
// data as the test is to discard them.
// MMapEvent
PerfDataProto_PerfEvent* event1 = perf_data_proto_.add_events();
event1->mutable_header()->set_type(1);
event1->mutable_mmap_event()->set_pid(1234);
event1->mutable_mmap_event()->set_filename_md5_prefix(0xdeadbeef);
// Missing field |MMapEvent::filename| has tag=6.
*event1->mutable_mmap_event()->mutable_unknown_fields() =
SerializeStringFieldWithTag(6, "/opt/google/chrome/chrome");
// CommEvent
PerfDataProto_PerfEvent* event2 = perf_data_proto_.add_events();
event2->mutable_header()->set_type(2);
event2->mutable_comm_event()->set_pid(5678);
event2->mutable_comm_event()->set_comm_md5_prefix(0x900df00d);
// Missing field |CommEvent::comm| has tag=3.
*event2->mutable_comm_event()->mutable_unknown_fields() =
SerializeStringFieldWithTag(3, "chrome");
// PerfBuildID
PerfDataProto_PerfBuildID* build_id = perf_data_proto_.add_build_ids();
build_id->set_misc(3);
build_id->set_pid(1337);
build_id->set_filename_md5_prefix(0x9876543210);
// Missing field |PerfBuildID::filename| has tag=4.
*build_id->mutable_unknown_fields() =
SerializeStringFieldWithTag(4, "/opt/google/chrome/chrome");
// StringAndMd5sumPrefix
PerfDataProto_StringMetadata* metadata =
perf_data_proto_.mutable_string_metadata();
metadata->mutable_perf_command_line_whole()->set_value_md5_prefix(
0x123456789);
// Missing field |StringAndMd5sumPrefix::value| has tag=1.
*metadata->mutable_perf_command_line_whole()->mutable_unknown_fields() =
SerializeStringFieldWithTag(1, "perf record -a -- sleep 1");
// Serialize to string and make sure it can be deserialized.
std::string perf_data_string = perf_data_proto_.SerializeAsString();
PerfDataProto temp_proto;
EXPECT_TRUE(temp_proto.ParseFromString(perf_data_string));
// Now pass it to |metric_collector_|.
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA, perf_data_string);
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_perf_data());
// The serialized form should be different because the unknown fields have
// have been removed.
EXPECT_NE(perf_data_string, profile.perf_data().SerializeAsString());
// Check contents of stored protobuf.
const PerfDataProto& stored_proto = profile.perf_data();
ASSERT_EQ(2, stored_proto.events_size());
// MMapEvent
const PerfDataProto_PerfEvent& stored_event1 = stored_proto.events(0);
EXPECT_EQ(1U, stored_event1.header().type());
EXPECT_EQ(1234U, stored_event1.mmap_event().pid());
EXPECT_EQ(0xdeadbeef, stored_event1.mmap_event().filename_md5_prefix());
EXPECT_EQ(0U, stored_event1.mmap_event().unknown_fields().size());
// CommEvent
const PerfDataProto_PerfEvent& stored_event2 = stored_proto.events(1);
EXPECT_EQ(2U, stored_event2.header().type());
EXPECT_EQ(5678U, stored_event2.comm_event().pid());
EXPECT_EQ(0x900df00d, stored_event2.comm_event().comm_md5_prefix());
EXPECT_EQ(0U, stored_event2.comm_event().unknown_fields().size());
// PerfBuildID
ASSERT_EQ(1, stored_proto.build_ids_size());
const PerfDataProto_PerfBuildID& stored_build_id = stored_proto.build_ids(0);
EXPECT_EQ(3U, stored_build_id.misc());
EXPECT_EQ(1337U, stored_build_id.pid());
EXPECT_EQ(0x9876543210U, stored_build_id.filename_md5_prefix());
EXPECT_EQ(0U, stored_build_id.unknown_fields().size());
// StringAndMd5sumPrefix
const PerfDataProto_StringMetadata& stored_metadata =
stored_proto.string_metadata();
EXPECT_EQ(0x123456789U,
stored_metadata.perf_command_line_whole().value_md5_prefix());
EXPECT_EQ(0U,
stored_metadata.perf_command_line_whole().unknown_fields().size());
}
TEST_F(MetricCollectorTest, PerfStatProto) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(perf_stat_proto_.SerializeAsString(),
profile.perf_stat().SerializeAsString());
}
// Change |sampled_profile| between calls to SaveSerializedPerfProto().
TEST_F(MetricCollectorTest, MultipleCalls) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION);
sampled_profile->set_ms_after_restore(3000);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND);
sampled_profile->set_suspend_duration_ms(60000);
sampled_profile->set_ms_after_resume(1500);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(4U, stored_profiles.size());
{
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
{
const SampledProfile& profile = stored_profiles[1];
EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_EQ(3000, profile.ms_after_restore());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(perf_stat_proto_.SerializeAsString(),
profile.perf_stat().SerializeAsString());
}
{
const SampledProfile& profile = stored_profiles[2];
EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_EQ(60000, profile.suspend_duration_ms());
EXPECT_EQ(1500, profile.ms_after_resume());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
{
const SampledProfile& profile = stored_profiles[3];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(perf_stat_proto_.SerializeAsString(),
profile.perf_stat().SerializeAsString());
}
}
TEST_F(MetricCollectorTest, Deactivate) {
......@@ -143,12 +438,12 @@ TEST_F(MetricCollectorTest, Deactivate) {
metric_collector_->OnUserLoggedIn();
EXPECT_TRUE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time_.is_null());
EXPECT_FALSE(metric_collector_->login_time().is_null());
// Timer is stopped by Deactivate(), but login time and cached profiles stay.
metric_collector_->Deactivate();
EXPECT_FALSE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time_.is_null());
EXPECT_FALSE(metric_collector_->login_time().is_null());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
......@@ -157,11 +452,9 @@ TEST_F(MetricCollectorTest, Deactivate) {
TEST_F(MetricCollectorTest, SuspendDone) {
const auto kSuspendDuration = base::TimeDelta::FromMinutes(3);
// Timer is not active before user logs in.
EXPECT_FALSE(metric_collector_->timer().IsRunning());
metric_collector_->SuspendDone(kSuspendDuration);
// Timer is activated by the SuspendDone call.
// Timer is active after the SuspendDone call.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
// Run all pending tasks. This will run all the tasks already queued, but not
......@@ -182,8 +475,8 @@ TEST_F(MetricCollectorTest, SuspendDone) {
EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile.trigger_event());
EXPECT_EQ(kSuspendDuration.InMilliseconds(), profile.suspend_duration_ms());
EXPECT_TRUE(profile.has_ms_after_resume());
EXPECT_EQ(kMsAfterLogin, profile.ms_after_login());
EXPECT_EQ(kMsAfterBoot, profile.ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_TRUE(profile.has_ms_after_boot());
// Run all new pending tasks. This will run a periodic collection that was
// scheduled by the previous collection event.
......@@ -199,11 +492,9 @@ TEST_F(MetricCollectorTest, SuspendDone) {
TEST_F(MetricCollectorTest, OnSessionRestoreDone) {
const int kRestoredTabs = 7;
// Timer is not active before user logs in.
EXPECT_FALSE(metric_collector_->timer().IsRunning());
metric_collector_->OnSessionRestoreDone(kRestoredTabs);
// Timer is activated by the OnSessionRestoreDone call.
// Timer is active after the OnSessionRestoreDone call.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
// Run all pending tasks.
......@@ -220,8 +511,8 @@ TEST_F(MetricCollectorTest, OnSessionRestoreDone) {
EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile.trigger_event());
EXPECT_EQ(kRestoredTabs, profile.num_tabs_restored());
EXPECT_FALSE(profile.has_ms_after_resume());
EXPECT_EQ(kMsAfterLogin, profile.ms_after_login());
EXPECT_EQ(kMsAfterBoot, profile.ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_TRUE(profile.has_ms_after_boot());
// A second SessionRestoreDone call is throttled.
metric_collector_->OnSessionRestoreDone(1);
......@@ -247,11 +538,7 @@ TEST_F(MetricCollectorTest, OnSessionRestoreDone) {
}
TEST_F(MetricCollectorTest, ScheduleIntervalCollection) {
// Timer is not active before user logs in.
EXPECT_FALSE(metric_collector_->timer().IsRunning());
metric_collector_->ScheduleIntervalCollection();
// Timer is activated by the ScheduleIntervalCollection call.
// Timer is active after login and a periodic collection is scheduled.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
// Run all pending tasks.
......@@ -268,8 +555,8 @@ TEST_F(MetricCollectorTest, ScheduleIntervalCollection) {
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_FALSE(profile.has_suspend_duration_ms());
EXPECT_FALSE(profile.has_ms_after_resume());
EXPECT_EQ(kMsAfterLogin, profile.ms_after_login());
EXPECT_EQ(kMsAfterBoot, profile.ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_TRUE(profile.has_ms_after_boot());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
......
......@@ -4,11 +4,9 @@
#include "chrome/browser/metrics/perf/perf_events_collector.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/system/sys_info.h"
#include "chrome/browser/metrics/perf/cpu_identity.h"
#include "chrome/browser/metrics/perf/perf_output.h"
#include "chrome/browser/metrics/perf/windowed_incognito_observer.h"
......@@ -27,28 +25,9 @@ const char kCWPFieldTrialName[] = "ChromeOSWideProfilingCollection";
// collecting further perf data. The current value is 4 MB.
const size_t kCachedPerfDataProtobufSizeThreshold = 4 * 1024 * 1024;
// Enumeration representing success and various failure modes for collecting and
// sending perf data.
enum GetPerfDataOutcome {
SUCCESS,
NOT_READY_TO_UPLOAD,
NOT_READY_TO_COLLECT,
INCOGNITO_ACTIVE,
INCOGNITO_LAUNCHED,
PROTOBUF_NOT_PARSED,
ILLEGAL_DATA_RETURNED,
ALREADY_COLLECTING,
NUM_OUTCOMES
};
// Name of the histogram that represents the success and various failure modes
// for collecting and sending perf data.
const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData";
void AddToPerfHistogram(GetPerfDataOutcome outcome) {
UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram, outcome,
NUM_OUTCOMES);
}
// for the perf collector.
const char kPerfCollectorOutcomeHistogram[] = "UMA.Perf.GetData";
// Gets parameter named by |key| from the map. If it is present and is an
// integer, stores the result in |out| and return true. Otherwise return false.
......@@ -193,40 +172,6 @@ const std::vector<RandomSelector::WeightAndValue> GetDefaultCommands_x86_64(
return cmds;
}
// PerfDataProto is defined elsewhere with more fields than the definition in
// Chromium's copy of perf_data.proto. During deserialization, the protobuf
// data could contain fields that are defined elsewhere but not in
// perf_data.proto, resulting in some data in |unknown_fields| for the message
// types within PerfDataProto.
//
// This function deletes those dangling unknown fields if they are in messages
// containing strings. See comments in perf_data.proto describing the fields
// that have been intentionally left out. Note that all unknown fields will be
// removed from those messages, not just unknown string fields.
void RemoveUnknownFieldsFromMessagesWithStrings(PerfDataProto* proto) {
// Clean up PerfEvent::MMapEvent and PerfEvent::CommEvent.
for (PerfDataProto::PerfEvent& event : *proto->mutable_events()) {
if (event.has_comm_event())
event.mutable_comm_event()->mutable_unknown_fields()->clear();
if (event.has_mmap_event())
event.mutable_mmap_event()->mutable_unknown_fields()->clear();
}
// Clean up PerfBuildID.
for (PerfDataProto::PerfBuildID& build_id : *proto->mutable_build_ids()) {
build_id.mutable_unknown_fields()->clear();
}
// Clean up StringMetadata and StringMetadata::StringAndMd5sumPrefix.
if (proto->has_string_metadata()) {
proto->mutable_string_metadata()->mutable_unknown_fields()->clear();
if (proto->string_metadata().has_perf_command_line_whole()) {
proto->mutable_string_metadata()
->mutable_perf_command_line_whole()
->mutable_unknown_fields()
->clear();
}
}
}
} // namespace
namespace internal {
......@@ -253,7 +198,8 @@ std::vector<RandomSelector::WeightAndValue> GetDefaultCommandsForCpu(
} // namespace internal
PerfCollector::PerfCollector() {}
PerfCollector::PerfCollector()
: MetricCollector(kPerfCollectorOutcomeHistogram) {}
PerfCollector::~PerfCollector() {}
......@@ -267,16 +213,6 @@ void PerfCollector::Init() {
MetricCollector::Init();
}
bool PerfCollector::ShouldUpload() const {
if (cached_profile_data_.empty()) {
AddToPerfHistogram(NOT_READY_TO_UPLOAD);
return false;
}
AddToPerfHistogram(SUCCESS);
return true;
}
namespace internal {
std::string FindBestCpuSpecifierFromParams(
......@@ -387,24 +323,22 @@ void PerfCollector::SetCollectionParamsFromVariationParams(
command_selector_.SetOdds(commands);
}
PerfCollector::PerfSubcommand PerfCollector::GetPerfSubcommandType(
MetricCollector::PerfProtoType PerfCollector::GetPerfProtoType(
const std::vector<std::string>& args) {
if (args.size() > 1 && args[0] == "perf") {
if (args[1] == "record")
return PerfSubcommand::PERF_COMMAND_RECORD;
if (args[1] == "record" || args[1] == "mem")
return PerfProtoType::PERF_TYPE_DATA;
if (args[1] == "stat")
return PerfSubcommand::PERF_COMMAND_STAT;
if (args[1] == "mem")
return PerfSubcommand::PERF_COMMAND_MEM;
return PerfProtoType::PERF_TYPE_STAT;
}
return PerfSubcommand::PERF_COMMAND_UNSUPPORTED;
return PerfProtoType::PERF_TYPE_UNSUPPORTED;
}
void PerfCollector::ParseOutputProtoIfValid(
std::unique_ptr<WindowedIncognitoObserver> incognito_observer,
std::unique_ptr<SampledProfile> sampled_profile,
PerfSubcommand subcommand,
PerfProtoType type,
const std::string& perf_stdout) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -413,74 +347,30 @@ void PerfCollector::ParseOutputProtoIfValid(
std::unique_ptr<PerfOutputCall> call_deleter(std::move(perf_output_call_));
if (incognito_observer->incognito_launched()) {
AddToPerfHistogram(INCOGNITO_LAUNCHED);
return;
}
if (perf_stdout.empty()) {
AddToPerfHistogram(ILLEGAL_DATA_RETURNED);
AddToUmaHistogram(CollectionAttemptStatus::INCOGNITO_LAUNCHED);
return;
}
switch (subcommand) {
case PerfSubcommand::PERF_COMMAND_RECORD:
case PerfSubcommand::PERF_COMMAND_MEM: {
PerfDataProto perf_data_proto;
if (!perf_data_proto.ParseFromString(perf_stdout)) {
AddToPerfHistogram(PROTOBUF_NOT_PARSED);
return;
}
RemoveUnknownFieldsFromMessagesWithStrings(&perf_data_proto);
sampled_profile->set_ms_after_boot(
base::SysInfo::Uptime().InMilliseconds());
sampled_profile->mutable_perf_data()->Swap(&perf_data_proto);
break;
}
case PerfSubcommand::PERF_COMMAND_STAT: {
PerfStatProto perf_stat_proto;
if (!perf_stat_proto.ParseFromString(perf_stdout)) {
AddToPerfHistogram(PROTOBUF_NOT_PARSED);
return;
}
sampled_profile->mutable_perf_stat()->Swap(&perf_stat_proto);
break;
}
case PerfSubcommand::PERF_COMMAND_UNSUPPORTED:
AddToPerfHistogram(PROTOBUF_NOT_PARSED);
return;
}
DCHECK(!login_time_.is_null());
sampled_profile->set_ms_after_login(
(base::TimeTicks::Now() - login_time_).InMilliseconds());
// Add the collected data to the container of collected SampledProfiles.
cached_profile_data_.resize(cached_profile_data_.size() + 1);
cached_profile_data_.back().Swap(sampled_profile.get());
SaveSerializedPerfProto(std::move(sampled_profile), type, perf_stdout);
}
bool PerfCollector::ShouldCollect() const {
// Only allow one active collection.
if (perf_output_call_) {
AddToPerfHistogram(ALREADY_COLLECTING);
AddToUmaHistogram(CollectionAttemptStatus::ALREADY_COLLECTING);
return false;
}
// Do not collect further data if we've already collected a substantial amount
// of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|.
size_t cached_perf_data_size = 0;
for (size_t i = 0; i < cached_profile_data_.size(); ++i) {
cached_perf_data_size += cached_profile_data_[i].ByteSize();
}
if (cached_perf_data_size >= kCachedPerfDataProtobufSizeThreshold) {
AddToPerfHistogram(NOT_READY_TO_COLLECT);
if (cached_profile_data_size() >= kCachedPerfDataProtobufSizeThreshold) {
AddToUmaHistogram(CollectionAttemptStatus::NOT_READY_TO_COLLECT);
return false;
}
// For privacy reasons, Chrome should only collect perf data if there is no
// incognito session active (or gets spawned during the collection).
if (BrowserList::IsIncognitoSessionActive()) {
AddToPerfHistogram(INCOGNITO_ACTIVE);
AddToUmaHistogram(CollectionAttemptStatus::INCOGNITO_ACTIVE);
return false;
}
......@@ -495,14 +385,14 @@ void PerfCollector::CollectProfile(
std::vector<std::string> command =
base::SplitString(command_selector_.Select(), kPerfCommandDelimiter,
base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
PerfSubcommand subcommand = GetPerfSubcommandType(command);
PerfProtoType type = GetPerfProtoType(command);
perf_output_call_ = std::make_unique<PerfOutputCall>(
collection_params_.collection_duration, command,
base::BindOnce(&PerfCollector::ParseOutputProtoIfValid,
base::AsWeakPtr<PerfCollector>(this),
base::Passed(&incognito_observer),
base::Passed(&sampled_profile), subcommand));
base::Passed(&sampled_profile), type));
}
} // namespace metrics
......@@ -27,18 +27,9 @@ class PerfCollector : public MetricCollector {
void Init() override;
protected:
// Perf events collection subcommands.
enum PerfSubcommand {
PERF_COMMAND_RECORD,
PERF_COMMAND_STAT,
PERF_COMMAND_MEM,
PERF_COMMAND_UNSUPPORTED,
};
// Returns one of the above enums given an vector of perf arguments, starting
// with "perf" itself in |args[0]|.
static PerfSubcommand GetPerfSubcommandType(
const std::vector<std::string>& args);
// Returns the perf proto type associated with the given vector of perf
// arguments, starting with "perf" itself in |args[0]|.
static PerfProtoType GetPerfProtoType(const std::vector<std::string>& args);
// Parses a PerfDataProto or PerfStatProto from serialized data |perf_stdout|,
// if non-empty. Which proto to use depends on |subcommand|. If |perf_stdout|
......@@ -48,11 +39,10 @@ class PerfCollector : public MetricCollector {
void ParseOutputProtoIfValid(
std::unique_ptr<WindowedIncognitoObserver> incognito_observer,
std::unique_ptr<SampledProfile> sampled_profile,
PerfSubcommand subcommand,
PerfProtoType type,
const std::string& perf_stdout);
// MetricCollector:
bool ShouldUpload() const override;
bool ShouldCollect() const override;
void CollectProfile(std::unique_ptr<SampledProfile> sampled_profile) override;
......
......@@ -22,9 +22,6 @@
#include "components/variations/variations_associated_data.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "third_party/protobuf/src/google/protobuf/wire_format_lite_inl.h"
namespace metrics {
......@@ -104,21 +101,6 @@ PerfStatProto GetExamplePerfStatProto() {
return proto;
}
// Creates a serialized data stream containing a string with a field tag number.
std::string SerializeStringFieldWithTag(int field, const std::string& value) {
std::string result;
google::protobuf::io::StringOutputStream string_stream(&result);
google::protobuf::io::CodedOutputStream output(&string_stream);
using google::protobuf::internal::WireFormatLite;
WireFormatLite::WriteTag(field, WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
&output);
output.WriteVarint32(value.size());
output.WriteString(value);
return result;
}
// Allows testing of PerfCollector behavior when an incognito window is opened.
class TestIncognitoObserver : public WindowedIncognitoObserver {
public:
......@@ -144,13 +126,10 @@ class TestPerfCollector : public PerfCollector {
public:
TestPerfCollector() {}
using MetricCollector::PerfProtoType;
using PerfCollector::collection_params;
using PerfCollector::command_selector;
using PerfCollector::Deactivate;
using PerfCollector::OnSessionRestoreDone;
using PerfCollector::ParseOutputProtoIfValid;
using PerfCollector::PerfSubcommand;
using PerfCollector::timer;
private:
DISALLOW_COPY_AND_ASSIGN(TestPerfCollector);
......@@ -162,9 +141,7 @@ class PerfCollectorTest : public testing::Test {
public:
PerfCollectorTest()
: task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
task_runner_handle_(task_runner_),
perf_data_proto_(GetExamplePerfDataProto()),
perf_stat_proto_(GetExamplePerfStatProto()) {}
task_runner_handle_(task_runner_) {}
void SetUp() override {
// PerfCollector requires chromeos::LoginState and
......@@ -191,17 +168,10 @@ class PerfCollectorTest : public testing::Test {
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
// These store example perf data/stat protobufs for testing.
PerfDataProto perf_data_proto_;
PerfStatProto perf_stat_proto_;
DISALLOW_COPY_AND_ASSIGN(PerfCollectorTest);
};
TEST_F(PerfCollectorTest, CheckSetup) {
EXPECT_GT(perf_data_proto_.ByteSize(), 0);
EXPECT_GT(perf_stat_proto_.ByteSize(), 0);
std::vector<SampledProfile> stored_profiles;
EXPECT_FALSE(perf_collector_->GetSampledProfiles(&stored_profiles));
EXPECT_TRUE(stored_profiles.empty());
......@@ -212,261 +182,22 @@ TEST_F(PerfCollectorTest, CheckSetup) {
->incognito_launched());
}
// If quipper fails, or the DBus call fails, no data will be returned.
TEST_F(PerfCollectorTest, NoPerfData) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD, std::string());
std::vector<SampledProfile> stored_profiles;
EXPECT_FALSE(perf_collector_->GetSampledProfiles(&stored_profiles));
}
TEST_F(PerfCollectorTest, PerfDataProto) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD,
perf_data_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_data_proto_),
SerializeMessageToVector(profile.perf_data()));
}
TEST_F(PerfCollectorTest, PerfDataProto_UnknownFieldsDiscarded) {
// First add some unknown fields to MMapEvent, CommEvent, PerfBuildID, and
// StringAndMd5sumPrefix. The known field values don't have to make sense for
// perf data. They are just padding to avoid having an otherwise empty proto.
// The unknown field string contents don't have to make sense as serialized
// data as the test is to discard them.
// MMapEvent
PerfDataProto_PerfEvent* event1 = perf_data_proto_.add_events();
event1->mutable_header()->set_type(1);
event1->mutable_mmap_event()->set_pid(1234);
event1->mutable_mmap_event()->set_filename_md5_prefix(0xdeadbeef);
// Missing field |MMapEvent::filename| has tag=6.
*event1->mutable_mmap_event()->mutable_unknown_fields() =
SerializeStringFieldWithTag(6, "/opt/google/chrome/chrome");
// CommEvent
PerfDataProto_PerfEvent* event2 = perf_data_proto_.add_events();
event2->mutable_header()->set_type(2);
event2->mutable_comm_event()->set_pid(5678);
event2->mutable_comm_event()->set_comm_md5_prefix(0x900df00d);
// Missing field |CommEvent::comm| has tag=3.
*event2->mutable_comm_event()->mutable_unknown_fields() =
SerializeStringFieldWithTag(3, "chrome");
// PerfBuildID
PerfDataProto_PerfBuildID* build_id = perf_data_proto_.add_build_ids();
build_id->set_misc(3);
build_id->set_pid(1337);
build_id->set_filename_md5_prefix(0x9876543210);
// Missing field |PerfBuildID::filename| has tag=4.
*build_id->mutable_unknown_fields() =
SerializeStringFieldWithTag(4, "/opt/google/chrome/chrome");
// StringAndMd5sumPrefix
PerfDataProto_StringMetadata* metadata =
perf_data_proto_.mutable_string_metadata();
metadata->mutable_perf_command_line_whole()->set_value_md5_prefix(
0x123456789);
// Missing field |StringAndMd5sumPrefix::value| has tag=1.
*metadata->mutable_perf_command_line_whole()->mutable_unknown_fields() =
SerializeStringFieldWithTag(1, "perf record -a -- sleep 1");
// Serialize to string and make sure it can be deserialized.
std::string perf_data_string = perf_data_proto_.SerializeAsString();
PerfDataProto temp_proto;
EXPECT_TRUE(temp_proto.ParseFromString(perf_data_string));
// Now pass it to |perf_collector_|.
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD, perf_data_string);
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_perf_data());
// The serialized form should be different because the unknown fields have
// have been removed.
EXPECT_NE(perf_data_string, profile.perf_data().SerializeAsString());
// Check contents of stored protobuf.
const PerfDataProto& stored_proto = profile.perf_data();
ASSERT_EQ(2, stored_proto.events_size());
// MMapEvent
const PerfDataProto_PerfEvent& stored_event1 = stored_proto.events(0);
EXPECT_EQ(1U, stored_event1.header().type());
EXPECT_EQ(1234U, stored_event1.mmap_event().pid());
EXPECT_EQ(0xdeadbeef, stored_event1.mmap_event().filename_md5_prefix());
EXPECT_EQ(0U, stored_event1.mmap_event().unknown_fields().size());
// CommEvent
const PerfDataProto_PerfEvent& stored_event2 = stored_proto.events(1);
EXPECT_EQ(2U, stored_event2.header().type());
EXPECT_EQ(5678U, stored_event2.comm_event().pid());
EXPECT_EQ(0x900df00d, stored_event2.comm_event().comm_md5_prefix());
EXPECT_EQ(0U, stored_event2.comm_event().unknown_fields().size());
// PerfBuildID
ASSERT_EQ(1, stored_proto.build_ids_size());
const PerfDataProto_PerfBuildID& stored_build_id = stored_proto.build_ids(0);
EXPECT_EQ(3U, stored_build_id.misc());
EXPECT_EQ(1337U, stored_build_id.pid());
EXPECT_EQ(0x9876543210U, stored_build_id.filename_md5_prefix());
EXPECT_EQ(0U, stored_build_id.unknown_fields().size());
// StringAndMd5sumPrefix
const PerfDataProto_StringMetadata& stored_metadata =
stored_proto.string_metadata();
EXPECT_EQ(0x123456789U,
stored_metadata.perf_command_line_whole().value_md5_prefix());
EXPECT_EQ(0U,
stored_metadata.perf_command_line_whole().unknown_fields().size());
}
TEST_F(PerfCollectorTest, PerfStatProto) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_STAT,
perf_stat_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_stat_proto_),
SerializeMessageToVector(profile.perf_stat()));
}
// Change |sampled_profile| between calls to ParseOutputProtoIfValid().
TEST_F(PerfCollectorTest, MultipleCalls) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD,
perf_data_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION);
sampled_profile->set_ms_after_restore(3000);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_STAT,
perf_stat_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND);
sampled_profile->set_suspend_duration_ms(60000);
sampled_profile->set_ms_after_resume(1500);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD,
perf_data_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_STAT,
perf_stat_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(4U, stored_profiles.size());
const SampledProfile& profile1 = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile1.trigger_event());
EXPECT_TRUE(profile1.has_ms_after_login());
ASSERT_TRUE(profile1.has_perf_data());
EXPECT_FALSE(profile1.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_data_proto_),
SerializeMessageToVector(profile1.perf_data()));
const SampledProfile& profile2 = stored_profiles[1];
EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile2.trigger_event());
EXPECT_TRUE(profile2.has_ms_after_login());
EXPECT_EQ(3000, profile2.ms_after_restore());
EXPECT_FALSE(profile2.has_perf_data());
ASSERT_TRUE(profile2.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_stat_proto_),
SerializeMessageToVector(profile2.perf_stat()));
const SampledProfile& profile3 = stored_profiles[2];
EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile3.trigger_event());
EXPECT_TRUE(profile3.has_ms_after_login());
EXPECT_EQ(60000, profile3.suspend_duration_ms());
EXPECT_EQ(1500, profile3.ms_after_resume());
ASSERT_TRUE(profile3.has_perf_data());
EXPECT_FALSE(profile3.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_data_proto_),
SerializeMessageToVector(profile3.perf_data()));
const SampledProfile& profile4 = stored_profiles[3];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile4.trigger_event());
EXPECT_TRUE(profile4.has_ms_after_login());
EXPECT_FALSE(profile4.has_perf_data());
ASSERT_TRUE(profile4.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_stat_proto_),
SerializeMessageToVector(profile4.perf_stat()));
}
// Simulate opening and closing of incognito window in between calls to
// ParseOutputProtoIfValid().
TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
PerfDataProto perf_data_proto = GetExamplePerfDataProto();
PerfStatProto perf_stat_proto = GetExamplePerfStatProto();
EXPECT_GT(perf_data_proto.ByteSize(), 0);
EXPECT_GT(perf_stat_proto.ByteSize(), 0);
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD,
perf_data_proto_.SerializeAsString());
TestPerfCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto.SerializeAsString());
std::vector<SampledProfile> stored_profiles1;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles1));
......@@ -477,7 +208,7 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
EXPECT_TRUE(profile1.has_ms_after_login());
ASSERT_TRUE(profile1.has_perf_data());
EXPECT_FALSE(profile1.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_data_proto_),
EXPECT_EQ(SerializeMessageToVector(perf_data_proto),
SerializeMessageToVector(profile1.perf_data()));
sampled_profile = std::make_unique<SampledProfile>();
......@@ -486,8 +217,8 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_STAT,
perf_stat_proto_.SerializeAsString());
TestPerfCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto.SerializeAsString());
std::vector<SampledProfile> stored_profiles2;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles2));
......@@ -499,7 +230,7 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
EXPECT_EQ(3000, profile2.ms_after_restore());
EXPECT_FALSE(profile2.has_perf_data());
ASSERT_TRUE(profile2.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_stat_proto_),
EXPECT_EQ(SerializeMessageToVector(perf_stat_proto),
SerializeMessageToVector(profile2.perf_stat()));
sampled_profile = std::make_unique<SampledProfile>();
......@@ -508,8 +239,8 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(true),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD,
perf_data_proto_.SerializeAsString());
TestPerfCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto.SerializeAsString());
std::vector<SampledProfile> stored_profiles_empty;
EXPECT_FALSE(perf_collector_->GetSampledProfiles(&stored_profiles_empty));
......@@ -520,8 +251,8 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(true),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_STAT,
perf_stat_proto_.SerializeAsString());
TestPerfCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto.SerializeAsString());
EXPECT_FALSE(perf_collector_->GetSampledProfiles(&stored_profiles_empty));
......@@ -533,8 +264,8 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
perf_collector_->ParseOutputProtoIfValid(
TestIncognitoObserver::CreateWithIncognitoLaunched(false),
std::move(sampled_profile),
TestPerfCollector::PerfSubcommand::PERF_COMMAND_RECORD,
perf_data_proto_.SerializeAsString());
TestPerfCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto.SerializeAsString());
std::vector<SampledProfile> stored_profiles3;
EXPECT_TRUE(perf_collector_->GetSampledProfiles(&stored_profiles3));
......@@ -547,7 +278,7 @@ TEST_F(PerfCollectorTest, IncognitoWindowOpened) {
EXPECT_EQ(1500, profile3.ms_after_resume());
ASSERT_TRUE(profile3.has_perf_data());
EXPECT_FALSE(profile3.has_perf_stat());
EXPECT_EQ(SerializeMessageToVector(perf_data_proto_),
EXPECT_EQ(SerializeMessageToVector(perf_data_proto),
SerializeMessageToVector(profile3.perf_data()));
}
......
......@@ -24,9 +24,6 @@ namespace metrics {
namespace {
const long kMsAfterBoot = 10000;
const long kMsAfterLogin = 2000;
// Returns sample PerfDataProtos with custom timestamps. The contents don't have
// to make sense. They just need to constitute a semantically valid protobuf.
// |proto| is an output parameter that will contain the created protobuf.
......@@ -62,18 +59,14 @@ class TestMetricCollector : public MetricCollector {
public:
TestMetricCollector() {}
explicit TestMetricCollector(const CollectionParams& collection_params)
: MetricCollector(collection_params) {}
: MetricCollector("UMA.CWP.TestData", collection_params) {}
void CollectProfile(
std::unique_ptr<SampledProfile> sampled_profile) override {
PerfDataProto perf_data_proto = GetExamplePerfDataProto(TSTAMP);
sampled_profile->set_ms_after_boot(kMsAfterBoot);
sampled_profile->set_ms_after_login(kMsAfterLogin);
sampled_profile->mutable_perf_data()->Swap(&perf_data_proto);
// Add the collected data to the container of collected SampledProfiles.
cached_profile_data_.resize(cached_profile_data_.size() + 1);
cached_profile_data_.back().Swap(sampled_profile.get());
SaveSerializedPerfProto(std::move(sampled_profile),
PerfProtoType::PERF_TYPE_DATA,
perf_data_proto.SerializeAsString());
}
private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment