summaryrefslogtreecommitdiffstats
path: root/components/metrics
diff options
context:
space:
mode:
authorbsimonnet@chromium.org <bsimonnet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-02 00:59:06 +0000
committerbsimonnet@chromium.org <bsimonnet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-02 00:59:06 +0000
commit064107ef1030b102ee805d17d1de9b9b24a0f1e9 (patch)
treecef90be3817689a11946c330f824cb6e8d9acf01 /components/metrics
parent8e05f0f285002c8468046dabe4956fc90b66c328 (diff)
downloadchromium_src-064107ef1030b102ee805d17d1de9b9b24a0f1e9.zip
chromium_src-064107ef1030b102ee805d17d1de9b9b24a0f1e9.tar.gz
chromium_src-064107ef1030b102ee805d17d1de9b9b24a0f1e9.tar.bz2
Move part of metrics from chrome/common to components
Chrome OS needs to have access to the metrics aggretion and serialization logic to build a metric sender service replacing chrome. Protobuf definitions are moved to components too. The code remaining will be extrated in components/metrics and components/variation during a refactoring later this year. BUG=chromium:360183 TEST=run unittests TBR=sky@chromium.org, benwells@chromium.org Review URL: https://codereview.chromium.org/239093004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@267709 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/metrics')
-rw-r--r--components/metrics/DEPS4
-rw-r--r--components/metrics/README22
-rw-r--r--components/metrics/metrics_log_base.cc142
-rw-r--r--components/metrics/metrics_log_base.h110
-rw-r--r--components/metrics/metrics_log_base_unittest.cc125
-rw-r--r--components/metrics/metrics_log_manager.cc208
-rw-r--r--components/metrics/metrics_log_manager.h213
-rw-r--r--components/metrics/metrics_log_manager_unittest.cc427
-rw-r--r--components/metrics/proto/chrome_user_metrics_extension.proto58
-rw-r--r--components/metrics/proto/histogram_event.proto45
-rw-r--r--components/metrics/proto/omnibox_event.proto257
-rw-r--r--components/metrics/proto/perf_data.proto388
-rw-r--r--components/metrics/proto/profiler_event.proto92
-rw-r--r--components/metrics/proto/system_profile.proto509
-rw-r--r--components/metrics/proto/user_action_event.proto21
15 files changed, 2621 insertions, 0 deletions
diff --git a/components/metrics/DEPS b/components/metrics/DEPS
new file mode 100644
index 0000000..ccaaef4
--- /dev/null
+++ b/components/metrics/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "-content",
+ "-net",
+]
diff --git a/components/metrics/README b/components/metrics/README
new file mode 100644
index 0000000..3461dea
--- /dev/null
+++ b/components/metrics/README
@@ -0,0 +1,22 @@
+This component contains the base classes for the metrics service and only
+depends on //base. It is used by ChromeOS as the base for a standalone service
+that will upload the metrics when ChromeOS is not installed (headless install).
+
+This is the first step towards the componentization of metrics that will happen
+later this spring.
+
+A proposed structure for the metrics component is:
+//components/metrics/base,
+ Depends on base only. Contains the protobuf definitions.
+//components/metrics/core
+ Depends on everything iOS depends on
+//components/metrics/content
+ Depends on content
+
+Ideally, the component would abstract the network stack and have a clean
+separation between the metrics upload logic (protbuf generation, retry, etc...),
+the chrome part (gathering histogram from all the threads, populating the
+log with hardware characteristics, plugin state, etc.).
+
+It is a plus if the component stays in a single directory as it would be easier
+for ChromeOS to pull it :).
diff --git a/components/metrics/metrics_log_base.cc b/components/metrics/metrics_log_base.cc
new file mode 100644
index 0000000..99e4edb
--- /dev/null
+++ b/components/metrics/metrics_log_base.cc
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/metrics_log_base.h"
+
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "components/metrics/metrics_hashes.h"
+#include "components/metrics/proto/histogram_event.pb.h"
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/metrics/proto/user_action_event.pb.h"
+
+using base::Histogram;
+using base::HistogramBase;
+using base::HistogramSamples;
+using base::SampleCountIterator;
+using base::Time;
+using base::TimeDelta;
+using metrics::HistogramEventProto;
+using metrics::SystemProfileProto;
+using metrics::UserActionEventProto;
+
+namespace metrics {
+namespace {
+
+// Any id less than 16 bytes is considered to be a testing id.
+bool IsTestingID(const std::string& id) {
+ return id.size() < 16;
+}
+
+} // namespace
+
+MetricsLogBase::MetricsLogBase(const std::string& client_id,
+ int session_id,
+ LogType log_type,
+ const std::string& version_string)
+ : num_events_(0),
+ locked_(false),
+ log_type_(log_type) {
+ DCHECK_NE(NO_LOG, log_type);
+ if (IsTestingID(client_id))
+ uma_proto_.set_client_id(0);
+ else
+ uma_proto_.set_client_id(Hash(client_id));
+
+ uma_proto_.set_session_id(session_id);
+ uma_proto_.mutable_system_profile()->set_build_timestamp(GetBuildTime());
+ uma_proto_.mutable_system_profile()->set_app_version(version_string);
+}
+
+MetricsLogBase::~MetricsLogBase() {}
+
+// static
+uint64 MetricsLogBase::Hash(const std::string& value) {
+ uint64 hash = metrics::HashMetricName(value);
+
+ // The following log is VERY helpful when folks add some named histogram into
+ // the code, but forgot to update the descriptive list of histograms. When
+ // that happens, all we get to see (server side) is a hash of the histogram
+ // name. We can then use this logging to find out what histogram name was
+ // being hashed to a given MD5 value by just running the version of Chromium
+ // in question with --enable-logging.
+ DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]";
+
+ return hash;
+}
+
+// static
+int64 MetricsLogBase::GetBuildTime() {
+ static int64 integral_build_time = 0;
+ if (!integral_build_time) {
+ Time time;
+ const char* kDateTime = __DATE__ " " __TIME__ " GMT";
+ bool result = Time::FromString(kDateTime, &time);
+ DCHECK(result);
+ integral_build_time = static_cast<int64>(time.ToTimeT());
+ }
+ return integral_build_time;
+}
+
+// static
+int64 MetricsLogBase::GetCurrentTime() {
+ return (base::TimeTicks::Now() - base::TimeTicks()).InSeconds();
+}
+
+void MetricsLogBase::CloseLog() {
+ DCHECK(!locked_);
+ locked_ = true;
+}
+
+void MetricsLogBase::GetEncodedLog(std::string* encoded_log) {
+ DCHECK(locked_);
+ uma_proto_.SerializeToString(encoded_log);
+}
+
+void MetricsLogBase::RecordUserAction(const std::string& key) {
+ DCHECK(!locked_);
+
+ UserActionEventProto* user_action = uma_proto_.add_user_action_event();
+ user_action->set_name_hash(Hash(key));
+ user_action->set_time(GetCurrentTime());
+
+ ++num_events_;
+}
+
+void MetricsLogBase::RecordHistogramDelta(const std::string& histogram_name,
+ const HistogramSamples& snapshot) {
+ DCHECK(!locked_);
+ DCHECK_NE(0, snapshot.TotalCount());
+
+ // We will ignore the MAX_INT/infinite value in the last element of range[].
+
+ HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event();
+ histogram_proto->set_name_hash(Hash(histogram_name));
+ histogram_proto->set_sum(snapshot.sum());
+
+ for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator(); !it->Done();
+ it->Next()) {
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ it->Get(&min, &max, &count);
+ HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
+ bucket->set_min(min);
+ bucket->set_max(max);
+ bucket->set_count(count);
+ }
+
+ // Omit fields to save space (see rules in histogram_event.proto comments).
+ for (int i = 0; i < histogram_proto->bucket_size(); ++i) {
+ HistogramEventProto::Bucket* bucket = histogram_proto->mutable_bucket(i);
+ if (i + 1 < histogram_proto->bucket_size() &&
+ bucket->max() == histogram_proto->bucket(i + 1).min()) {
+ bucket->clear_max();
+ } else if (bucket->max() == bucket->min() + 1) {
+ bucket->clear_min();
+ }
+ }
+}
+
+} // namespace metrics
diff --git a/components/metrics/metrics_log_base.h b/components/metrics/metrics_log_base.h
new file mode 100644
index 0000000..25a6bd0
--- /dev/null
+++ b/components/metrics/metrics_log_base.h
@@ -0,0 +1,110 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines a set of user experience metrics data recorded by
+// the MetricsService. This is the unit of data that is sent to the server.
+
+#ifndef COMPONENTS_METRICS_METRICS_LOG_BASE_H_
+#define COMPONENTS_METRICS_METRICS_LOG_BASE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
+
+namespace base {
+class HistogramSamples;
+} // namespace base
+
+namespace metrics {
+
+// This class provides base functionality for logging metrics data.
+class MetricsLogBase {
+ public:
+ // TODO(asvitkine): Remove the NO_LOG value.
+ enum LogType {
+ INITIAL_STABILITY_LOG, // The initial log containing stability stats.
+ ONGOING_LOG, // Subsequent logs in a session.
+ NO_LOG, // Placeholder value for when there is no log.
+ };
+
+ // Creates a new metrics log of the specified type.
+ // client_id is the identifier for this profile on this installation
+ // session_id is an integer that's incremented on each application launch
+ MetricsLogBase(const std::string& client_id,
+ int session_id,
+ LogType log_type,
+ const std::string& version_string);
+ virtual ~MetricsLogBase();
+
+ // Computes the MD5 hash of the given string, and returns the first 8 bytes of
+ // the hash.
+ static uint64 Hash(const std::string& value);
+
+ // Get the GMT buildtime for the current binary, expressed in seconds since
+ // January 1, 1970 GMT.
+ // The value is used to identify when a new build is run, so that previous
+ // reliability stats, from other builds, can be abandoned.
+ static int64 GetBuildTime();
+
+ // Convenience function to return the current time at a resolution in seconds.
+ // This wraps base::TimeTicks, and hence provides an abstract time that is
+ // always incrementing for use in measuring time durations.
+ static int64 GetCurrentTime();
+
+ // Records a user-initiated action.
+ void RecordUserAction(const std::string& key);
+
+ // Record any changes in a given histogram for transmission.
+ void RecordHistogramDelta(const std::string& histogram_name,
+ const base::HistogramSamples& snapshot);
+
+ // Stop writing to this record and generate the encoded representation.
+ // None of the Record* methods can be called after this is called.
+ void CloseLog();
+
+ // Fills |encoded_log| with the serialized protobuf representation of the
+ // record. Must only be called after CloseLog() has been called.
+ void GetEncodedLog(std::string* encoded_log);
+
+ int num_events() { return num_events_; }
+
+ void set_hardware_class(const std::string& hardware_class) {
+ uma_proto_.mutable_system_profile()->mutable_hardware()->set_hardware_class(
+ hardware_class);
+ }
+
+ LogType log_type() const { return log_type_; }
+
+ protected:
+ bool locked() const { return locked_; }
+
+ metrics::ChromeUserMetricsExtension* uma_proto() { return &uma_proto_; }
+ const metrics::ChromeUserMetricsExtension* uma_proto() const {
+ return &uma_proto_;
+ }
+
+ // TODO(isherman): Remove this once the XML pipeline is outta here.
+ int num_events_; // the number of events recorded in this log
+
+ private:
+ // locked_ is true when record has been packed up for sending, and should
+ // no longer be written to. It is only used for sanity checking and is
+ // not a real lock.
+ bool locked_;
+
+ // The type of the log, i.e. initial or ongoing.
+ const LogType log_type_;
+
+ // Stores the protocol buffer representation for this log.
+ metrics::ChromeUserMetricsExtension uma_proto_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsLogBase);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_METRICS_LOG_BASE_H_
diff --git a/components/metrics/metrics_log_base_unittest.cc b/components/metrics/metrics_log_base_unittest.cc
new file mode 100644
index 0000000..cc7a173
--- /dev/null
+++ b/components/metrics/metrics_log_base_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/metrics_log_base.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/sample_vector.h"
+#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+namespace {
+
+class TestMetricsLogBase : public MetricsLogBase {
+ public:
+ TestMetricsLogBase()
+ : MetricsLogBase("client_id", 1, MetricsLogBase::ONGOING_LOG, "1.2.3.4") {
+ }
+ virtual ~TestMetricsLogBase() {}
+
+ using MetricsLogBase::uma_proto;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestMetricsLogBase);
+};
+
+} // namespace
+
+TEST(MetricsLogBaseTest, LogType) {
+ MetricsLogBase log1("id", 0, MetricsLogBase::ONGOING_LOG, "1.2.3");
+ EXPECT_EQ(MetricsLogBase::ONGOING_LOG, log1.log_type());
+
+ MetricsLogBase log2("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "1.2.3");
+ EXPECT_EQ(MetricsLogBase::INITIAL_STABILITY_LOG, log2.log_type());
+}
+
+TEST(MetricsLogBaseTest, EmptyRecord) {
+ MetricsLogBase log("totally bogus client ID", 137,
+ MetricsLogBase::ONGOING_LOG, "bogus version");
+ log.set_hardware_class("sample-class");
+ log.CloseLog();
+
+ std::string encoded;
+ log.GetEncodedLog(&encoded);
+
+ // A couple of fields are hard to mock, so these will be copied over directly
+ // for the expected output.
+ metrics::ChromeUserMetricsExtension parsed;
+ ASSERT_TRUE(parsed.ParseFromString(encoded));
+
+ metrics::ChromeUserMetricsExtension expected;
+ expected.set_client_id(5217101509553811875); // Hashed bogus client ID
+ expected.set_session_id(137);
+ expected.mutable_system_profile()->set_build_timestamp(
+ parsed.system_profile().build_timestamp());
+ expected.mutable_system_profile()->set_app_version("bogus version");
+ expected.mutable_system_profile()->mutable_hardware()->set_hardware_class(
+ "sample-class");
+
+ EXPECT_EQ(expected.SerializeAsString(), encoded);
+}
+
+TEST(MetricsLogBaseTest, HistogramBucketFields) {
+ // Create buckets: 1-5, 5-7, 7-8, 8-9, 9-10, 10-11, 11-12.
+ base::BucketRanges ranges(8);
+ ranges.set_range(0, 1);
+ ranges.set_range(1, 5);
+ ranges.set_range(2, 7);
+ ranges.set_range(3, 8);
+ ranges.set_range(4, 9);
+ ranges.set_range(5, 10);
+ ranges.set_range(6, 11);
+ ranges.set_range(7, 12);
+
+ base::SampleVector samples(&ranges);
+ samples.Accumulate(3, 1); // Bucket 1-5.
+ samples.Accumulate(6, 1); // Bucket 5-7.
+ samples.Accumulate(8, 1); // Bucket 8-9. (7-8 skipped)
+ samples.Accumulate(10, 1); // Bucket 10-11. (9-10 skipped)
+ samples.Accumulate(11, 1); // Bucket 11-12.
+
+ TestMetricsLogBase log;
+ log.RecordHistogramDelta("Test", samples);
+
+ const metrics::ChromeUserMetricsExtension* uma_proto = log.uma_proto();
+ const metrics::HistogramEventProto& histogram_proto =
+ uma_proto->histogram_event(uma_proto->histogram_event_size() - 1);
+
+ // Buckets with samples: 1-5, 5-7, 8-9, 10-11, 11-12.
+ // Should become: 1-/, 5-7, /-9, 10-/, /-12.
+ ASSERT_EQ(5, histogram_proto.bucket_size());
+
+ // 1-5 becomes 1-/ (max is same as next min).
+ EXPECT_TRUE(histogram_proto.bucket(0).has_min());
+ EXPECT_FALSE(histogram_proto.bucket(0).has_max());
+ EXPECT_EQ(1, histogram_proto.bucket(0).min());
+
+ // 5-7 stays 5-7 (no optimization possible).
+ EXPECT_TRUE(histogram_proto.bucket(1).has_min());
+ EXPECT_TRUE(histogram_proto.bucket(1).has_max());
+ EXPECT_EQ(5, histogram_proto.bucket(1).min());
+ EXPECT_EQ(7, histogram_proto.bucket(1).max());
+
+ // 8-9 becomes /-9 (min is same as max - 1).
+ EXPECT_FALSE(histogram_proto.bucket(2).has_min());
+ EXPECT_TRUE(histogram_proto.bucket(2).has_max());
+ EXPECT_EQ(9, histogram_proto.bucket(2).max());
+
+ // 10-11 becomes 10-/ (both optimizations apply, omit max is prioritized).
+ EXPECT_TRUE(histogram_proto.bucket(3).has_min());
+ EXPECT_FALSE(histogram_proto.bucket(3).has_max());
+ EXPECT_EQ(10, histogram_proto.bucket(3).min());
+
+ // 11-12 becomes /-12 (last record must keep max, min is same as max - 1).
+ EXPECT_FALSE(histogram_proto.bucket(4).has_min());
+ EXPECT_TRUE(histogram_proto.bucket(4).has_max());
+ EXPECT_EQ(12, histogram_proto.bucket(4).max());
+}
+
+} // namespace metrics
diff --git a/components/metrics/metrics_log_manager.cc b/components/metrics/metrics_log_manager.cc
new file mode 100644
index 0000000..e537eef
--- /dev/null
+++ b/components/metrics/metrics_log_manager.cc
@@ -0,0 +1,208 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/metrics_log_manager.h"
+
+#include <algorithm>
+
+#include "base/metrics/histogram.h"
+#include "base/sha1.h"
+#include "base/strings/string_util.h"
+#include "base/timer/elapsed_timer.h"
+#include "components/metrics/metrics_log_base.h"
+
+namespace metrics {
+
+MetricsLogManager::SerializedLog::SerializedLog() {}
+MetricsLogManager::SerializedLog::~SerializedLog() {}
+
+bool MetricsLogManager::SerializedLog::IsEmpty() const {
+ return log_text_.empty();
+}
+
+void MetricsLogManager::SerializedLog::SwapLogText(std::string* log_text) {
+ log_text_.swap(*log_text);
+ if (log_text_.empty())
+ log_hash_.clear();
+ else
+ log_hash_ = base::SHA1HashString(log_text_);
+}
+
+void MetricsLogManager::SerializedLog::Clear() {
+ log_text_.clear();
+ log_hash_.clear();
+}
+
+void MetricsLogManager::SerializedLog::Swap(
+ MetricsLogManager::SerializedLog* other) {
+ log_text_.swap(other->log_text_);
+ log_hash_.swap(other->log_hash_);
+}
+
+MetricsLogManager::MetricsLogManager()
+ : unsent_logs_loaded_(false),
+ staged_log_type_(MetricsLogBase::NO_LOG),
+ max_ongoing_log_store_size_(0),
+ last_provisional_store_index_(-1),
+ last_provisional_store_type_(MetricsLogBase::INITIAL_STABILITY_LOG) {}
+
+MetricsLogManager::~MetricsLogManager() {}
+
+void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log) {
+ DCHECK(!current_log_.get());
+ current_log_.reset(log);
+}
+
+void MetricsLogManager::FinishCurrentLog() {
+ DCHECK(current_log_.get());
+ current_log_->CloseLog();
+ SerializedLog compressed_log;
+ CompressCurrentLog(&compressed_log);
+ if (!compressed_log.IsEmpty())
+ StoreLog(&compressed_log, current_log_->log_type(), NORMAL_STORE);
+ current_log_.reset();
+}
+
+void MetricsLogManager::StageNextLogForUpload() {
+ // Prioritize initial logs for uploading.
+ std::vector<SerializedLog>* source_list =
+ unsent_initial_logs_.empty() ? &unsent_ongoing_logs_
+ : &unsent_initial_logs_;
+ LogType source_type = (source_list == &unsent_ongoing_logs_) ?
+ MetricsLogBase::ONGOING_LOG : MetricsLogBase::INITIAL_STABILITY_LOG;
+ // CHECK, rather than DCHECK, because swap()ing with an empty list causes
+ // hard-to-identify crashes much later.
+ CHECK(!source_list->empty());
+ DCHECK(staged_log_.IsEmpty());
+ DCHECK_EQ(MetricsLogBase::NO_LOG, staged_log_type_);
+ staged_log_.Swap(&source_list->back());
+ staged_log_type_ = source_type;
+ source_list->pop_back();
+
+ // If the staged log was the last provisional store, clear that.
+ if (last_provisional_store_index_ != -1) {
+ if (source_type == last_provisional_store_type_ &&
+ static_cast<unsigned int>(last_provisional_store_index_) ==
+ source_list->size()) {
+ last_provisional_store_index_ = -1;
+ }
+ }
+}
+
+bool MetricsLogManager::has_staged_log() const {
+ return !staged_log_.IsEmpty();
+}
+
+void MetricsLogManager::DiscardStagedLog() {
+ staged_log_.Clear();
+ staged_log_type_ = MetricsLogBase::NO_LOG;
+}
+
+void MetricsLogManager::DiscardCurrentLog() {
+ current_log_->CloseLog();
+ current_log_.reset();
+}
+
+void MetricsLogManager::PauseCurrentLog() {
+ DCHECK(!paused_log_.get());
+ paused_log_.reset(current_log_.release());
+}
+
+void MetricsLogManager::ResumePausedLog() {
+ DCHECK(!current_log_.get());
+ current_log_.reset(paused_log_.release());
+}
+
+void MetricsLogManager::StoreStagedLogAsUnsent(StoreType store_type) {
+ DCHECK(has_staged_log());
+
+ // If compressing the log failed, there's nothing to store.
+ if (staged_log_.IsEmpty())
+ return;
+
+ StoreLog(&staged_log_, staged_log_type_, store_type);
+ DiscardStagedLog();
+}
+
+void MetricsLogManager::StoreLog(SerializedLog* log,
+ LogType log_type,
+ StoreType store_type) {
+ DCHECK_NE(MetricsLogBase::NO_LOG, log_type);
+ std::vector<SerializedLog>* destination_list =
+ (log_type == MetricsLogBase::INITIAL_STABILITY_LOG) ?
+ &unsent_initial_logs_ : &unsent_ongoing_logs_;
+ destination_list->push_back(SerializedLog());
+ destination_list->back().Swap(log);
+
+ if (store_type == PROVISIONAL_STORE) {
+ last_provisional_store_index_ = destination_list->size() - 1;
+ last_provisional_store_type_ = log_type;
+ }
+}
+
+void MetricsLogManager::DiscardLastProvisionalStore() {
+ if (last_provisional_store_index_ == -1)
+ return;
+ std::vector<SerializedLog>* source_list =
+ (last_provisional_store_type_ == MetricsLogBase::ONGOING_LOG)
+ ? &unsent_ongoing_logs_
+ : &unsent_initial_logs_;
+ DCHECK_LT(static_cast<unsigned int>(last_provisional_store_index_),
+ source_list->size());
+ source_list->erase(source_list->begin() + last_provisional_store_index_);
+ last_provisional_store_index_ = -1;
+}
+
+void MetricsLogManager::PersistUnsentLogs() {
+ DCHECK(log_serializer_.get());
+ if (!log_serializer_.get())
+ return;
+ DCHECK(unsent_logs_loaded_);
+ if (!unsent_logs_loaded_)
+ return;
+
+ base::ElapsedTimer timer;
+ // Remove any ongoing logs that are over the serialization size limit.
+ if (max_ongoing_log_store_size_) {
+ for (std::vector<SerializedLog>::iterator it = unsent_ongoing_logs_.begin();
+ it != unsent_ongoing_logs_.end();) {
+ size_t log_size = it->log_text().length();
+ if (log_size > max_ongoing_log_store_size_) {
+ UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
+ static_cast<int>(log_size));
+ it = unsent_ongoing_logs_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ log_serializer_->SerializeLogs(unsent_initial_logs_,
+ MetricsLogBase::INITIAL_STABILITY_LOG);
+ log_serializer_->SerializeLogs(unsent_ongoing_logs_,
+ MetricsLogBase::ONGOING_LOG);
+ UMA_HISTOGRAM_TIMES("UMA.StoreLogsTime", timer.Elapsed());
+}
+
+void MetricsLogManager::LoadPersistedUnsentLogs() {
+ DCHECK(log_serializer_.get());
+ if (!log_serializer_.get())
+ return;
+
+ base::ElapsedTimer timer;
+ log_serializer_->DeserializeLogs(MetricsLogBase::INITIAL_STABILITY_LOG,
+ &unsent_initial_logs_);
+ log_serializer_->DeserializeLogs(MetricsLogBase::ONGOING_LOG,
+ &unsent_ongoing_logs_);
+ UMA_HISTOGRAM_TIMES("UMA.LoadLogsTime", timer.Elapsed());
+
+ unsent_logs_loaded_ = true;
+}
+
+void MetricsLogManager::CompressCurrentLog(SerializedLog* compressed_log) {
+ std::string log_text;
+ current_log_->GetEncodedLog(&log_text);
+ compressed_log->SwapLogText(&log_text);
+}
+
+} // namespace metrics
diff --git a/components/metrics/metrics_log_manager.h b/components/metrics/metrics_log_manager.h
new file mode 100644
index 0000000..006d4ed
--- /dev/null
+++ b/components/metrics/metrics_log_manager.h
@@ -0,0 +1,213 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_METRICS_LOG_MANAGER_H_
+#define COMPONENTS_METRICS_METRICS_LOG_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/metrics/metrics_log_base.h"
+
+namespace metrics {
+
+// Manages all the log objects used by a MetricsService implementation. Keeps
+// track of both an in progress log and a log that is staged for uploading as
+// text, as well as saving logs to, and loading logs from, persistent storage.
+class MetricsLogManager {
+ public:
+ typedef MetricsLogBase::LogType LogType;
+
+ MetricsLogManager();
+ ~MetricsLogManager();
+
+ class SerializedLog {
+ public:
+ SerializedLog();
+ ~SerializedLog();
+
+ const std::string& log_text() const { return log_text_; }
+ const std::string& log_hash() const { return log_hash_; }
+
+ // Returns true if the log is empty.
+ bool IsEmpty() const;
+
+ // Swaps log text with |log_text| and updates the hash. This is more
+ // performant than a regular setter as it avoids doing a large string copy.
+ void SwapLogText(std::string* log_text);
+
+ // Clears the log.
+ void Clear();
+
+ // Swaps log contents with |other|.
+ void Swap(SerializedLog* other);
+
+ private:
+ // Non-human readable log text (serialized proto).
+ std::string log_text_;
+
+ // Non-human readable SHA1 of |log_text| or empty if |log_text| is empty.
+ std::string log_hash_;
+
+ // Intentionally omits DISALLOW_COPY_AND_ASSIGN() so that it can be used
+ // in std::vector<SerializedLog>.
+ };
+
+ enum StoreType {
+ NORMAL_STORE, // A standard store operation.
+ PROVISIONAL_STORE, // A store operation that can be easily reverted later.
+ };
+
+ // Takes ownership of |log| and makes it the current_log. This should only be
+ // called if there is not a current log.
+ void BeginLoggingWithLog(MetricsLogBase* log);
+
+ // Returns the in-progress log.
+ MetricsLogBase* current_log() { return current_log_.get(); }
+
+ // Closes current_log(), compresses it, and stores the compressed log for
+ // later, leaving current_log() NULL.
+ void FinishCurrentLog();
+
+ // Returns true if there are any logs waiting to be uploaded.
+ bool has_unsent_logs() const {
+ return !unsent_initial_logs_.empty() || !unsent_ongoing_logs_.empty();
+ }
+
+ // Populates staged_log_text() with the next stored log to send.
+ // Should only be called if has_unsent_logs() is true.
+ void StageNextLogForUpload();
+
+ // Returns true if there is a log that needs to be, or is being, uploaded.
+ bool has_staged_log() const;
+
+ // The text of the staged log, as a serialized protobuf. Empty if there is no
+ // staged log, or if compression of the staged log failed.
+ const std::string& staged_log_text() const { return staged_log_.log_text(); }
+
+ // The SHA1 hash (non-human readable) of the staged log or empty if there is
+ // no staged log.
+ const std::string& staged_log_hash() const { return staged_log_.log_hash(); }
+
+ // Discards the staged log.
+ void DiscardStagedLog();
+
+ // Closes and discards |current_log|.
+ void DiscardCurrentLog();
+
+ // Sets current_log to NULL, but saves the current log for future use with
+ // ResumePausedLog(). Only one log may be paused at a time.
+ // TODO(stuartmorgan): Pause/resume support is really a workaround for a
+ // design issue in initial log writing; that should be fixed, and pause/resume
+ // removed.
+ void PauseCurrentLog();
+
+ // Restores the previously paused log (if any) to current_log().
+ // This should only be called if there is not a current log.
+ void ResumePausedLog();
+
+ // Saves the staged log, then clears staged_log().
+ // If |store_type| is PROVISIONAL_STORE, it can be dropped from storage with
+ // a later call to DiscardLastProvisionalStore (if it hasn't already been
+ // staged again).
+ // This is intended to be used when logs are being saved while an upload is in
+ // progress, in case the upload later succeeds.
+ // This can only be called if has_staged_log() is true.
+ void StoreStagedLogAsUnsent(StoreType store_type);
+
+ // Discards the last log stored with StoreStagedLogAsUnsent with |store_type|
+ // set to PROVISIONAL_STORE, as long as it hasn't already been re-staged. If
+ // the log is no longer present, this is a no-op.
+ void DiscardLastProvisionalStore();
+
+ // Sets the threshold for how large an onging log can be and still be written
+ // to persistant storage. Ongoing logs larger than this will be discarded
+ // before persisting. 0 is interpreted as no limit.
+ void set_max_ongoing_log_store_size(size_t max_size) {
+ max_ongoing_log_store_size_ = max_size;
+ }
+
+ // Interface for a utility class to serialize and deserialize logs for
+ // persistent storage.
+ class LogSerializer {
+ public:
+ virtual ~LogSerializer() {}
+
+ // Serializes |logs| to persistent storage, replacing any previously
+ // serialized logs of the same type.
+ virtual void SerializeLogs(const std::vector<SerializedLog>& logs,
+ LogType log_type) = 0;
+
+ // Populates |logs| with logs of type |log_type| deserialized from
+ // persistent storage.
+ virtual void DeserializeLogs(LogType log_type,
+ std::vector<SerializedLog>* logs) = 0;
+ };
+
+ // Sets the serializer to use for persisting and loading logs; takes ownership
+ // of |serializer|.
+ void set_log_serializer(LogSerializer* serializer) {
+ log_serializer_.reset(serializer);
+ }
+
+ // Saves any unsent logs to persistent storage using the current log
+ // serializer. Can only be called after set_log_serializer.
+ void PersistUnsentLogs();
+
+ // Loads any unsent logs from persistent storage using the current log
+ // serializer. Can only be called after set_log_serializer.
+ void LoadPersistedUnsentLogs();
+
+ private:
+ // Saves |log| as the given type (or discards it in accordance with
+ // |max_ongoing_log_store_size_|).
+ // NOTE: This clears the contents of |log| (to avoid an expensive copy),
+ // so the log should be discarded after this call.
+ void StoreLog(SerializedLog* log, LogType log_type, StoreType store_type);
+
+ // Compresses |current_log_| into |compressed_log|.
+ void CompressCurrentLog(SerializedLog* compressed_log);
+
+ // Tracks whether unsent logs (if any) have been loaded from the serializer.
+ bool unsent_logs_loaded_;
+
+ // The log that we are still appending to.
+ scoped_ptr<MetricsLogBase> current_log_;
+
+ // A paused, previously-current log.
+ scoped_ptr<MetricsLogBase> paused_log_;
+
+ // Helper class to handle serialization/deserialization of logs for persistent
+ // storage. May be NULL.
+ scoped_ptr<LogSerializer> log_serializer_;
+
+ // The current staged log, ready for upload to the server.
+ SerializedLog staged_log_;
+ LogType staged_log_type_;
+
+ // Logs from a previous session that have not yet been sent.
+ // Note that the vector has the oldest logs listed first (early in the
+ // vector), and we'll discard old logs if we have gathered too many logs.
+ std::vector<SerializedLog> unsent_initial_logs_;
+ std::vector<SerializedLog> unsent_ongoing_logs_;
+
+ size_t max_ongoing_log_store_size_;
+
+ // The index and type of the last provisional store. If nothing has been
+ // provisionally stored, or the last provisional store has already been
+ // re-staged, the index will be -1;
+ // This is necessary because during an upload there are two logs (staged
+ // and current) and a client might store them in either order, so it's
+ // not necessarily the case that the provisional store is the last store.
+ int last_provisional_store_index_;
+ LogType last_provisional_store_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsLogManager);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_METRICS_LOG_MANAGER_H_
diff --git a/components/metrics/metrics_log_manager_unittest.cc b/components/metrics/metrics_log_manager_unittest.cc
new file mode 100644
index 0000000..ec0ca38f
--- /dev/null
+++ b/components/metrics/metrics_log_manager_unittest.cc
@@ -0,0 +1,427 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/metrics_log_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/sha1.h"
+#include "components/metrics/metrics_log_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+namespace {
+
+// Dummy serializer that just stores logs in memory.
+class DummyLogSerializer : public MetricsLogManager::LogSerializer {
+ public:
+ virtual void SerializeLogs(
+ const std::vector<MetricsLogManager::SerializedLog>& logs,
+ MetricsLogManager::LogType log_type) OVERRIDE {
+ persisted_logs_[log_type] = logs;
+ }
+
+ virtual void DeserializeLogs(
+ MetricsLogManager::LogType log_type,
+ std::vector<MetricsLogManager::SerializedLog>* logs) OVERRIDE {
+ ASSERT_NE(static_cast<void*>(NULL), logs);
+ *logs = persisted_logs_[log_type];
+ }
+
+ // Returns the number of logs of the given type.
+ size_t TypeCount(MetricsLogManager::LogType log_type) {
+ return persisted_logs_[log_type].size();
+ }
+
+ // In-memory "persitent storage".
+ std::vector<MetricsLogManager::SerializedLog> persisted_logs_[2];
+};
+
+} // namespace
+
+TEST(MetricsLogManagerTest, StandardFlow) {
+ MetricsLogManager log_manager;
+
+ // Make sure a new manager has a clean slate.
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+
+ // Check that the normal flow works.
+ MetricsLogBase* initial_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+ log_manager.BeginLoggingWithLog(initial_log);
+ EXPECT_EQ(initial_log, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+
+ log_manager.FinishCurrentLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+ EXPECT_FALSE(log_manager.has_staged_log());
+
+ MetricsLogBase* second_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "version");
+ log_manager.BeginLoggingWithLog(second_log);
+ EXPECT_EQ(second_log, log_manager.current_log());
+
+ log_manager.StageNextLogForUpload();
+ EXPECT_TRUE(log_manager.has_staged_log());
+ EXPECT_FALSE(log_manager.staged_log_text().empty());
+
+ log_manager.DiscardStagedLog();
+ EXPECT_EQ(second_log, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+ EXPECT_TRUE(log_manager.staged_log_text().empty());
+
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+}
+
+TEST(MetricsLogManagerTest, AbandonedLog) {
+ MetricsLogManager log_manager;
+
+ MetricsLogBase* dummy_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+ log_manager.BeginLoggingWithLog(dummy_log);
+ EXPECT_EQ(dummy_log, log_manager.current_log());
+
+ log_manager.DiscardCurrentLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+ EXPECT_FALSE(log_manager.has_staged_log());
+}
+
+TEST(MetricsLogManagerTest, InterjectedLog) {
+ MetricsLogManager log_manager;
+
+ MetricsLogBase* ongoing_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "v");
+ MetricsLogBase* temp_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+
+ log_manager.BeginLoggingWithLog(ongoing_log);
+ EXPECT_EQ(ongoing_log, log_manager.current_log());
+
+ log_manager.PauseCurrentLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+
+ log_manager.BeginLoggingWithLog(temp_log);
+ EXPECT_EQ(temp_log, log_manager.current_log());
+ log_manager.FinishCurrentLog();
+ EXPECT_EQ(NULL, log_manager.current_log());
+
+ log_manager.ResumePausedLog();
+ EXPECT_EQ(ongoing_log, log_manager.current_log());
+
+ EXPECT_FALSE(log_manager.has_staged_log());
+ log_manager.StageNextLogForUpload();
+ log_manager.DiscardStagedLog();
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+}
+
+TEST(MetricsLogManagerTest, InterjectedLogPreservesType) {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ MetricsLogBase* ongoing_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "v");
+ MetricsLogBase* temp_log =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+
+ log_manager.BeginLoggingWithLog(ongoing_log);
+ log_manager.PauseCurrentLog();
+ log_manager.BeginLoggingWithLog(temp_log);
+ log_manager.FinishCurrentLog();
+ log_manager.ResumePausedLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.DiscardStagedLog();
+
+ // Verify that the remaining log (which is the original ongoing log) still
+ // has the right type.
+ log_manager.FinishCurrentLog();
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+}
+
+TEST(MetricsLogManagerTest, StoreAndLoad) {
+ std::vector<MetricsLogManager::SerializedLog> initial_logs;
+ std::vector<MetricsLogManager::SerializedLog> ongoing_logs;
+
+ // Set up some in-progress logging in a scoped log manager simulating the
+ // leadup to quitting, then persist as would be done on quit.
+ {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ // Simulate a log having already been unsent from a previous session.
+ MetricsLogManager::SerializedLog log;
+ std::string text = "proto";
+ log.SwapLogText(&text);
+ serializer->persisted_logs_[MetricsLogBase::ONGOING_LOG].push_back(log);
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+ log_manager.LoadPersistedUnsentLogs();
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+
+ MetricsLogBase* log1 =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+ MetricsLogBase* log2 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "v");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.FinishCurrentLog();
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.FinishCurrentLog();
+
+ // Nothing should be written out until PersistUnsentLogs is called.
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(2U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+
+ // Save the logs to transfer over to a new serializer (since log_manager
+ // owns |serializer|, so it's about to go away.
+ initial_logs =
+ serializer->persisted_logs_[MetricsLogBase::INITIAL_STABILITY_LOG];
+ ongoing_logs = serializer->persisted_logs_[MetricsLogBase::ONGOING_LOG];
+ }
+
+ // Now simulate the relaunch, ensure that the log manager restores
+ // everything correctly, and verify that once the are handled they are not
+ // re-persisted.
+ {
+ MetricsLogManager log_manager;
+
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ serializer->persisted_logs_[MetricsLogBase::INITIAL_STABILITY_LOG] =
+ initial_logs;
+ serializer->persisted_logs_[MetricsLogBase::ONGOING_LOG] = ongoing_logs;
+
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+
+ log_manager.StageNextLogForUpload();
+ log_manager.DiscardStagedLog();
+ // The initial log should be sent first; update the persisted storage to
+ // verify.
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(2U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+
+ // Handle the first ongoing log.
+ log_manager.StageNextLogForUpload();
+ log_manager.DiscardStagedLog();
+ EXPECT_TRUE(log_manager.has_unsent_logs());
+
+ // Handle the last log.
+ log_manager.StageNextLogForUpload();
+ log_manager.DiscardStagedLog();
+ EXPECT_FALSE(log_manager.has_unsent_logs());
+
+ // Nothing should have changed "on disk" since PersistUnsentLogs hasn't been
+ // called again.
+ EXPECT_EQ(2U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ // Persist, and make sure nothing is left.
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ }
+}
+
+TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
+ // Ensure that types are preserved when storing staged logs.
+ {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ MetricsLogBase* log =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "version");
+ log_manager.BeginLoggingWithLog(log);
+ log_manager.FinishCurrentLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.PersistUnsentLogs();
+
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ }
+
+ {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ MetricsLogBase* log =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+ log_manager.BeginLoggingWithLog(log);
+ log_manager.FinishCurrentLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.PersistUnsentLogs();
+
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ }
+}
+
+TEST(MetricsLogManagerTest, LargeLogDiscarding) {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ // Set the size threshold very low, to verify that it's honored.
+ log_manager.set_max_ongoing_log_store_size(1);
+
+ MetricsLogBase* log1 =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+ MetricsLogBase* log2 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "v");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.FinishCurrentLog();
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.FinishCurrentLog();
+
+ // Only the ongoing log should be written out, due to the threshold.
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+}
+
+TEST(MetricsLogManagerTest, ProvisionalStoreStandardFlow) {
+ // Ensure that provisional store works, and discards the correct log.
+ {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ MetricsLogBase* log1 =
+ new MetricsLogBase("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "v");
+ MetricsLogBase* log2 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "v");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.FinishCurrentLog();
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
+ log_manager.FinishCurrentLog();
+ log_manager.DiscardLastProvisionalStore();
+
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(0U, serializer->TypeCount(MetricsLogBase::INITIAL_STABILITY_LOG));
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ }
+}
+
+TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
+ // Ensure that trying to drop a sent log is a no-op, even if another log has
+ // since been staged.
+ {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ MetricsLogBase* log1 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "version");
+ MetricsLogBase* log2 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "version");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.FinishCurrentLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
+ log_manager.StageNextLogForUpload();
+ log_manager.DiscardStagedLog();
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.FinishCurrentLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.DiscardLastProvisionalStore();
+
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ }
+
+ // Ensure that trying to drop more than once is a no-op
+ {
+ MetricsLogManager log_manager;
+ DummyLogSerializer* serializer = new DummyLogSerializer;
+ log_manager.set_log_serializer(serializer);
+ log_manager.LoadPersistedUnsentLogs();
+
+ MetricsLogBase* log1 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "version");
+ MetricsLogBase* log2 =
+ new MetricsLogBase("id", 0, MetricsLogBase::ONGOING_LOG, "version");
+ log_manager.BeginLoggingWithLog(log1);
+ log_manager.FinishCurrentLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
+ log_manager.BeginLoggingWithLog(log2);
+ log_manager.FinishCurrentLog();
+ log_manager.StageNextLogForUpload();
+ log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
+ log_manager.DiscardLastProvisionalStore();
+ log_manager.DiscardLastProvisionalStore();
+
+ log_manager.PersistUnsentLogs();
+ EXPECT_EQ(1U, serializer->TypeCount(MetricsLogBase::ONGOING_LOG));
+ }
+}
+
+TEST(MetricsLogManagerTest, SerializedLog) {
+ const char kFooText[] = "foo";
+ const std::string foo_hash = base::SHA1HashString(kFooText);
+ const char kBarText[] = "bar";
+ const std::string bar_hash = base::SHA1HashString(kBarText);
+
+ MetricsLogManager::SerializedLog log;
+ EXPECT_TRUE(log.log_text().empty());
+ EXPECT_TRUE(log.log_hash().empty());
+
+ std::string foo = kFooText;
+ log.SwapLogText(&foo);
+ EXPECT_TRUE(foo.empty());
+ EXPECT_FALSE(log.IsEmpty());
+ EXPECT_EQ(kFooText, log.log_text());
+ EXPECT_EQ(foo_hash, log.log_hash());
+
+ std::string bar = kBarText;
+ log.SwapLogText(&bar);
+ EXPECT_EQ(kFooText, bar);
+ EXPECT_FALSE(log.IsEmpty());
+ EXPECT_EQ(kBarText, log.log_text());
+ EXPECT_EQ(bar_hash, log.log_hash());
+
+ log.Clear();
+ EXPECT_TRUE(log.IsEmpty());
+ EXPECT_TRUE(log.log_text().empty());
+ EXPECT_TRUE(log.log_hash().empty());
+
+ MetricsLogManager::SerializedLog log2;
+ foo = kFooText;
+ log2.SwapLogText(&foo);
+ log.Swap(&log2);
+ EXPECT_FALSE(log.IsEmpty());
+ EXPECT_EQ(kFooText, log.log_text());
+ EXPECT_EQ(foo_hash, log.log_hash());
+ EXPECT_TRUE(log2.IsEmpty());
+ EXPECT_TRUE(log2.log_text().empty());
+ EXPECT_TRUE(log2.log_hash().empty());
+}
+
+} // namespace metrics
diff --git a/components/metrics/proto/chrome_user_metrics_extension.proto b/components/metrics/proto/chrome_user_metrics_extension.proto
new file mode 100644
index 0000000..80f3499
--- /dev/null
+++ b/components/metrics/proto/chrome_user_metrics_extension.proto
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Protocol buffer for Chrome UMA (User Metrics Analysis).
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+import "histogram_event.proto";
+import "omnibox_event.proto";
+import "profiler_event.proto";
+import "system_profile.proto";
+import "user_action_event.proto";
+import "perf_data.proto";
+
+// Next tag: 11
+message ChromeUserMetricsExtension {
+ // The product (i.e. end user application) for a given UMA log.
+ enum Product {
+ // Google Chrome product family.
+ CHROME = 0;
+ }
+ // The product corresponding to this log. Note: The default value is Chrome,
+ // so Chrome products will not transmit this field.
+ optional Product product = 10 [default = CHROME];
+
+ // The id of the client install that generated these events.
+ //
+ // For Chrome clients, this id is unique to a top-level (one level above the
+ // "Default" directory) Chrome user data directory [1], and so is shared among
+ // all Chrome user profiles contained in this user data directory.
+ // An id of 0 is reserved for test data (monitoring and internal testing) and
+ // should normally be ignored in analysis of the data.
+ // [1] http://www.chromium.org/user-experience/user-data-directory
+ optional fixed64 client_id = 1;
+
+ // The session id for this user.
+ // Values such as tab ids are only meaningful within a particular session.
+ // The client keeps track of the session id and sends it with each event.
+ // The session id is simply an integer that is incremented each time the user
+ // relaunches Chrome.
+ optional int32 session_id = 2;
+
+ // Information about the user's browser and system configuration.
+ optional SystemProfileProto system_profile = 3;
+
+ // This message will log one or more of the following event types:
+ repeated UserActionEventProto user_action_event = 4;
+ repeated OmniboxEventProto omnibox_event = 5;
+ repeated HistogramEventProto histogram_event = 6;
+ repeated ProfilerEventProto profiler_event = 7;
+
+ repeated PerfDataProto perf_data = 8;
+}
diff --git a/components/metrics/proto/histogram_event.proto b/components/metrics/proto/histogram_event.proto
new file mode 100644
index 0000000..7cb0e8c
--- /dev/null
+++ b/components/metrics/proto/histogram_event.proto
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Histogram-collected metrics.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+// Next tag: 4
+message HistogramEventProto {
+ // The name of the histogram, hashed.
+ optional fixed64 name_hash = 1;
+
+ // The sum of all the sample values.
+ // Together with the total count of the sample values, this allows us to
+ // compute the average value. The count of all sample values is just the sum
+ // of the counts of all the buckets.
+ optional int64 sum = 2;
+
+ // The per-bucket data.
+ message Bucket {
+ // Each bucket's range is bounded by min <= x < max.
+ // It is valid to omit one of these two fields in a bucket, but not both.
+ // If the min field is omitted, its value is assumed to be equal to max - 1.
+ // If the max field is omitted, its value is assumed to be equal to the next
+ // bucket's min value (possibly computed per above). The last bucket in a
+ // histogram should always include the max field.
+ optional int64 min = 1;
+ optional int64 max = 2;
+
+ // The bucket's index in the list of buckets, sorted in ascending order.
+ // This field was intended to provide extra redundancy to detect corrupted
+ // records, but was never used. As of M31, it is no longer sent by Chrome
+ // clients to reduce the UMA upload size.
+ optional int32 bucket_index = 3 [deprecated = true];
+
+ // The number of entries in this bucket.
+ optional int64 count = 4;
+ }
+ repeated Bucket bucket = 3;
+}
diff --git a/components/metrics/proto/omnibox_event.proto b/components/metrics/proto/omnibox_event.proto
new file mode 100644
index 0000000..6e3f91c
--- /dev/null
+++ b/components/metrics/proto/omnibox_event.proto
@@ -0,0 +1,257 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Stores information about an omnibox interaction.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+// Next tag: 17
+message OmniboxEventProto {
+ // The timestamp for the event, in seconds since the epoch.
+ optional int64 time = 1;
+
+ // The id of the originating tab for this omnibox interaction.
+ // This is the current tab *unless* the user opened the target in a new tab.
+ // In those cases, this is unset. Tab ids are unique for a given session_id
+ // (in the containing protocol buffer ChromeUserMetricsExtensionProto).
+ optional int32 tab_id = 2;
+
+ // The number of characters the user had typed before autocompleting.
+ optional int32 typed_length = 3;
+
+ // Whether the user deleted text immediately before selecting an omnibox
+ // suggestion. This is usually the result of pressing backspace or delete.
+ optional bool just_deleted_text = 11;
+
+ // The number of terms that the user typed in the omnibox.
+ optional int32 num_typed_terms = 4;
+
+ // The index of the item that the user selected in the omnibox popup list.
+ // This corresponds the index of the |suggestion| below.
+ optional int32 selected_index = 5;
+
+ // Whether or not the top match was hidden in the omnibox suggestions
+ // dropdown.
+ optional bool is_top_result_hidden_in_dropdown = 14;
+
+ // Whether the omnibox popup is open. It can be closed if, for instance,
+ // the user clicks in the omnibox and hits return to reload the same page.
+ // If the popup is closed, the suggestion list will contain only one item
+ // and selected_index will be 0 (pointing to that single item). Because
+ // paste-and-search/paste-and-go actions ignore the current content of the
+ // omnibox dropdown (if it is open) when they happen, we pretend the
+ // dropdown is closed when logging these.
+ optional bool is_popup_open = 15;
+
+ // True if this is a paste-and-search or paste-and-go action. (The codebase
+ // refers to both these types as paste-and-go.)
+ optional bool is_paste_and_go = 16;
+
+ // The length of the inline autocomplete text in the omnibox.
+ // The sum |typed_length| + |completed_length| gives the full length of the
+ // user-visible text in the omnibox.
+ // This field is only set for inlineable suggestions selected at position 0
+ // (|selected_index| = 0) and will be omitted otherwise.
+ optional int32 completed_length = 6;
+
+ // The amount of time, in milliseconds, since the user first began modifying
+ // the text in the omnibox. If at some point after modifying the text, the
+ // user reverts the modifications (thus seeing the current web page's URL
+ // again), then writes in the omnibox again, this elapsed time should start
+ // from the time of the second series of modification.
+ optional int64 typing_duration_ms = 7;
+
+ // The amount of time, in milliseconds, since the last time the default
+ // (inline) match changed. This may be longer than the time since the
+ // last keystroke. (The last keystroke may not have changed the default
+ // match.) It may also be shorter than the time since the last keystroke
+ // because the default match might have come from an asynchronous
+ // provider. Regardless, it should always be less than or equal to
+ // the field |typing_duration_ms|.
+ optional int64 duration_since_last_default_match_update_ms = 13;
+
+ // The type of page currently displayed when the user used the omnibox.
+ enum PageClassification {
+ // An invalid URL; shouldn't happen.
+ INVALID_SPEC = 0;
+
+ // chrome://newtab/. This can be either the built-in version or a
+ // replacement new tab page from an extension. Note that when Instant
+ // Extended is enabled, the new tab page will be reported as either
+ // INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS or
+ // INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS below,
+ // unless an extension is replacing the new tab page, in which case
+ // it will still be reported as NTP.
+ NTP = 1;
+
+ // about:blank.
+ BLANK = 2;
+
+ // The user's home page. Note that if the home page is set to any
+ // of the new tab page versions or to about:blank, then we'll
+ // classify the page into those categories, not HOME_PAGE.
+ HOME_PAGE = 3;
+
+ // The catch-all entry of everything not included somewhere else
+ // on this list.
+ OTHER = 4;
+
+ // The instant new tab page enum value was deprecated on August 2, 2013.
+ OBSOLETE_INSTANT_NTP = 5;
+
+ // The user is on a search result page that's doing search term
+ // replacement, meaning the search terms should've appeared in the omnibox
+ // before the user started editing it, not the URL of the page.
+ SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT = 6;
+
+ // The new tab page in which this omnibox interaction first started
+ // with the user having focus in the omnibox.
+ INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS = 7;
+
+ // The new tab page in which this omnibox interaction first started
+ // with the user having focus in the fakebox.
+ INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS = 8;
+
+ // The user is on a search result page that's not doing search term
+ // replacement, meaning the URL of the page should've appeared in the
+ // omnibox before the user started editing it, not the search terms.
+ SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT = 9;
+
+ // When adding new classifications, please consider adding them in
+ // chrome/browser/resources/omnibox/omnibox.html
+ // so that these new options are displayed on about:omnibox.
+ }
+ optional PageClassification current_page_classification = 10;
+
+ // What kind of input the user provided.
+ enum InputType {
+ INVALID = 0; // Empty input (should not reach here)
+ UNKNOWN = 1; // Valid input whose type cannot be determined
+ REQUESTED_URL = 2; // DEPRECATED. Input autodetected as UNKNOWN, which the
+ // user wants to treat as an URL by specifying a
+ // desired_tld
+ URL = 3; // Input autodetected as a URL
+ QUERY = 4; // Input autodetected as a query
+ FORCED_QUERY = 5; // Input forced to be a query by an initial '?'
+ }
+ optional InputType input_type = 8;
+
+ // An enum used in multiple places below.
+ enum ProviderType {
+ UNKNOWN_PROVIDER = 0; // Unknown provider (should not reach here)
+ HISTORY_URL = 1; // URLs in history, or user-typed URLs
+ HISTORY_CONTENTS = 2; // Matches for page contents of pages in history
+ HISTORY_QUICK = 3; // Matches for recently or frequently visited pages
+ // in history
+ SEARCH = 4; // Search suggestions for the default search engine
+ KEYWORD = 5; // Keyword-triggered searches
+ BUILTIN = 6; // Built-in URLs, such as chrome://version
+ SHORTCUTS = 7; // Recently selected omnibox suggestions
+ EXTENSION_APPS = 8; // Custom suggestions from extensions and/or apps
+ CONTACT = 9; // DEPRECATED. The user's contacts
+ BOOKMARK = 10; // The user's bookmarks
+ ZERO_SUGGEST = 11; // Suggestions based on the current page
+ // This enum value is currently only used by Android GSA. It represents
+ // a suggestion from the phone.
+ ON_DEVICE = 12;
+ }
+
+ // The result set displayed on the completion popup
+ // Next tag: 6
+ message Suggestion {
+ // Where does this result come from?
+ optional ProviderType provider = 1;
+
+ // What kind of result this is.
+ // This corresponds to the AutocompleteMatch::Type enumeration in
+ // chrome/browser/autocomplete/autocomplete_match.h (except for Android
+ // GSA result types).
+ enum ResultType {
+ UNKNOWN_RESULT_TYPE = 0; // Unknown type (should not reach here)
+ URL_WHAT_YOU_TYPED = 1; // The input as a URL
+ HISTORY_URL = 2; // A past page whose URL contains the input
+ HISTORY_TITLE = 3; // A past page whose title contains the input
+ HISTORY_BODY = 4; // A past page whose body contains the input
+ HISTORY_KEYWORD = 5; // A past page whose keyword contains the
+ // input
+ NAVSUGGEST = 6; // A suggested URL
+ SEARCH_WHAT_YOU_TYPED = 7; // The input as a search query (with the
+ // default engine)
+ SEARCH_HISTORY = 8; // A past search (with the default engine)
+ // containing the input
+ SEARCH_SUGGEST = 9; // A suggested search (with the default
+ // engine) query that doesn't fall into one of
+ // the more specific suggestion categories
+ // below.
+ SEARCH_OTHER_ENGINE = 10; // A search with a non-default engine
+ EXTENSION_APP = 11; // An Extension App with a title/url that
+ // contains the input
+ CONTACT = 12; // DEPRECATED. One of the user's contacts
+ BOOKMARK_TITLE = 13; // A bookmark whose title contains the input.
+ SEARCH_SUGGEST_ENTITY = 14; // A suggested search for an entity.
+ SEARCH_SUGGEST_INFINITE = 15; // A suggested search to complete the tail
+ // of the query.
+ SEARCH_SUGGEST_PERSONALIZED = 16; // A personalized suggested search.
+ SEARCH_SUGGEST_PROFILE = 17; // A personalized suggested search for a
+ // Google+ profile.
+ APP_RESULT = 18; // Result from an installed app
+ // (eg: a gmail email).
+ // Used by Android GSA for on-device
+ // suggestion logging.
+ APP = 19; // An app result (eg: the gmail app).
+ // Used by Android GSA for on-device
+ // suggestion logging.
+ }
+ optional ResultType result_type = 2;
+
+ // The relevance score for this suggestion.
+ optional int32 relevance = 3;
+
+ // How many times this result was typed in / selected from the omnibox.
+ // Only set for some providers and result_types. At the time of
+ // writing this comment, it is only set for HistoryURL and
+ // HistoryQuickProvider matches.
+ optional int32 typed_count = 5;
+
+ // Whether this item is starred (bookmarked) or not.
+ optional bool is_starred = 4;
+ }
+ repeated Suggestion suggestion = 9;
+
+ // A data structure that holds per-provider information, general information
+ // not associated with a particular result.
+ // Next tag: 5
+ message ProviderInfo {
+ // Which provider generated this ProviderInfo entry.
+ optional ProviderType provider = 1;
+
+ // The provider's done() value, i.e., whether it's completed processing
+ // the query. Providers which don't do any asynchronous processing
+ // will always be done.
+ optional bool provider_done = 2;
+
+ // The set of field trials that have triggered in the most recent query,
+ // possibly affecting the shown suggestions. Each element is a hash
+ // of the corresponding field trial name.
+ // See chrome/browser/autocomplete/search_provider.cc for a specific usage
+ // example.
+ repeated fixed32 field_trial_triggered = 3;
+
+ // Same as above except that the set of field trials is a union of all field
+ // trials that have triggered within the current omnibox session including
+ // the most recent query.
+ // See AutocompleteController::ResetSession() for more details on the
+ // definition of a session.
+ // See chrome/browser/autocomplete/search_provider.cc for a specific usage
+ // example.
+ repeated fixed32 field_trial_triggered_in_session = 4;
+ }
+ // A list of diagnostic information about each provider. Providers
+ // will appear at most once in this list.
+ repeated ProviderInfo provider_info = 12;
+}
diff --git a/components/metrics/proto/perf_data.proto b/components/metrics/proto/perf_data.proto
new file mode 100644
index 0000000..6278649
--- /dev/null
+++ b/components/metrics/proto/perf_data.proto
@@ -0,0 +1,388 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+// Stores information from a perf session generated via running:
+// "perf record"
+//
+// See $kernel/tools/perf/design.txt for more details.
+
+// Please do not modify this protobuf directly, except to mirror the upstream
+// version found here:
+// https://chromium.googlesource.com/chromiumos/platform/chromiumos-wide-profiling/+/master/perf_data.proto
+// with some fields omitted for privacy reasons. Because it is a read-only copy
+// of the upstream protobuf, "Next tag:" comments are also absent.
+
+message PerfDataProto {
+
+ // Perf event attribute. Stores the event description.
+ // This data structure is defined in the linux kernel:
+ // $kernel/tools/perf/util/event.h.
+ message PerfEventAttr {
+ // Type of the event. Type is an enumeration and can be:
+ // IP: an instruction-pointer was stored in the event.
+ // MMAP: a DLL was loaded.
+ // FORK: a process was forked.
+ // etc.
+ optional uint32 type = 1;
+
+ // Size of the event data in bytes.
+ optional uint32 size = 2;
+
+ // The config stores the CPU-specific counter information.
+ optional uint64 config = 3;
+
+ // Sample period of the event. Indicates how often the event is
+ // triggered in terms of # of events. After |sample_period| events, an event
+ // will be recorded and stored.
+ optional uint64 sample_period = 4;
+
+ // Sample frequency of the event. Indicates how often the event is
+ // triggered in terms of # per second. The kernel will try to record
+ // |sample_freq| events per second.
+ optional uint64 sample_freq = 5;
+
+ // Sample type is a bitfield that records attributes of the sample. Example,
+ // whether an entire callchain was recorded, etc.
+ optional uint64 sample_type = 6;
+
+ // Bitfield that indicates whether reads on the counter will return the
+ // total time enabled and total time running.
+ optional uint64 read_format = 7;
+
+ // Indicates whether the counter starts off disabled.
+ optional bool disabled = 8;
+
+ // Indicates whether child processes inherit the counter.
+ optional bool inherit = 9;
+
+ // Indicates whether the counter is pinned to a particular CPU.
+ optional bool pinned = 10;
+
+ // Indicates whether this counter's group has exclusive access to the CPU's
+ // counters.
+ optional bool exclusive = 11;
+
+ // The following bits restrict events to be counted when the CPU is in user,
+ // kernel, hypervisor or idle modes.
+ optional bool exclude_user = 12;
+ optional bool exclude_kernel = 13;
+ optional bool exclude_hv = 14;
+ optional bool exclude_idle = 15;
+
+ // Indicates whether mmap events should be recorded.
+ optional bool mmap = 16;
+
+ // Indicates whether process comm information should be recorded upon
+ // process creation.
+ optional bool comm = 17;
+
+ // Indicates that we are in frequency mode, not period mode.
+ optional bool freq = 18;
+
+ // Indicates whether we have per-task counts.
+ optional bool inherit_stat = 19;
+
+ // Indicates whether we enable perf events after an exec() function call.
+ optional bool enable_on_exec = 20;
+
+ // Indicates whether we trace fork/exit.
+ optional bool task = 21;
+
+ // Indicates whether we are using a watermark to wake up.
+ optional bool watermark = 22;
+
+ // CPUs often "skid" when recording events. That means the instruction
+ // pointer may not be the same as the one that caused the counter overflow.
+ // Indicates the capabilities of the CPU in terms of recording precise
+ // instruction pointer.
+ optional uint32 precise_ip = 23;
+
+ // Indicates whether we have non-exec mmap data.
+ optional bool mmap_data = 24;
+
+ // If set, all the event types will have the same sample_type.
+ optional bool sample_id_all = 25;
+
+ // Indicates whether we are counting events from the host (when running a
+ // VM).
+ optional bool exclude_host = 26;
+
+ // Exclude events that happen on a guest OS.
+ optional bool exclude_guest = 27;
+
+ // Contains the number of events after which we wake up.
+ optional uint32 wakeup_events = 28;
+
+ // Contains the number of bytes after which we wake up.
+ optional uint32 wakeup_watermark = 29;
+
+ // Information about the type of the breakpoint.
+ optional uint32 bp_type = 30;
+
+ // Contains the breakpoint address.
+ optional uint64 bp_addr = 31;
+
+ // This is an extension of config (see above).
+ optional uint64 config1 = 32;
+
+ // The length of the breakpoint data in bytes.
+ optional uint64 bp_len = 33;
+
+ // This is an extension of config (see above).
+ optional uint64 config2 = 34;
+
+ // Contains the type of branch, example: user, kernel, call, return, etc.
+ optional uint64 branch_sample_type = 35;
+ }
+
+ // Describes a perf.data file attribute.
+ message PerfFileAttr {
+ optional PerfEventAttr attr = 1;
+
+ // List of perf file attribute ids. Each id describes an event.
+ repeated uint64 ids = 2;
+ }
+
+ // This message contains information about a perf sample itself, as opposed to
+ // a perf event captured by a sample.
+ message SampleInfo {
+ // Process ID / thread ID from which this sample was taken.
+ optional uint32 pid = 1;
+ optional uint32 tid = 2;
+
+ // Time this sample was taken (NOT the same as an event time).
+ // It is the number of nanoseconds since bootup.
+ optional uint64 sample_time_ns = 3;
+
+ // The ID of the sample's event type (cycles, instructions, etc).
+ // The event type IDs are defined in PerfFileAttr.
+ optional uint64 id = 4;
+
+ // The CPU on which this sample was taken.
+ optional uint32 cpu = 5;
+ }
+
+ message CommEvent {
+ // Process id.
+ optional uint32 pid = 1;
+
+ // Thread id.
+ optional uint32 tid = 2;
+
+ // Comm string's md5 prefix.
+ // The comm string was field 3 and has been intentionally left out.
+ optional uint64 comm_md5_prefix = 4;
+
+ // Time the sample was taken.
+ // Deprecated, use |sample_info| instead.
+ optional uint64 sample_time = 5 [deprecated=true];
+
+ // Info about the perf sample containing this event.
+ optional SampleInfo sample_info = 6;
+ }
+
+ message MMapEvent {
+ // Process id.
+ optional uint32 pid = 1;
+
+ // Thread id.
+ optional uint32 tid = 2;
+
+ // Start address.
+ optional uint64 start = 3;
+
+ // Length.
+ optional uint64 len = 4;
+
+ // PG Offset.
+ optional uint64 pgoff = 5;
+
+ // Filename's md5 prefix.
+ // The filename was field 6 and has been intentionally left out.
+ optional uint64 filename_md5_prefix = 7;
+
+ // Info about the perf sample containing this event.
+ optional SampleInfo sample_info = 8;
+ }
+
+ message BranchStackEntry {
+ // Branch source address.
+ optional uint64 from_ip = 1;
+
+ // Branch destination address.
+ optional uint64 to_ip = 2;
+
+ // Indicates a mispredicted branch.
+ optional bool mispredicted = 3;
+ }
+
+ message SampleEvent {
+ // Instruction pointer.
+ optional uint64 ip = 1;
+
+ // Process id.
+ optional uint32 pid = 2;
+
+ // Thread id.
+ optional uint32 tid = 3;
+
+ // The time after boot when the sample was recorded, in nanoseconds.
+ optional uint64 sample_time_ns = 4;
+
+ // The address of the sample.
+ optional uint64 addr = 5;
+
+ // The id of the sample.
+ optional uint64 id = 6;
+
+ // The stream id of the sample.
+ optional uint64 stream_id = 7;
+
+ // The period of the sample.
+ optional uint64 period = 8;
+
+ // The CPU where the event was recorded.
+ optional uint32 cpu = 9;
+
+ // The raw size of the event in bytes.
+ optional uint32 raw_size = 10;
+
+ // Sample callchain info.
+ repeated uint64 callchain = 11;
+
+ // Branch stack info.
+ repeated BranchStackEntry branch_stack = 12;
+ }
+
+ // ForkEvent is used for both FORK and EXIT events, which have the same data
+ // format. We don't want to call this "ForkOrExitEvent", in case a separate
+ // exit event is introduced in the future.
+ message ForkEvent {
+ // Forked process ID.
+ optional uint32 pid = 1;
+
+ // Parent process ID.
+ optional uint32 ppid = 2;
+
+ // Forked process thread ID.
+ optional uint32 tid = 3;
+
+ // Parent process thread ID.
+ optional uint32 ptid = 4;
+
+ // Time of fork event in nanoseconds since bootup.
+ optional uint64 fork_time_ns = 5;
+
+ // Info about the perf sample containing this event.
+ optional SampleInfo sample_info = 11;
+ }
+
+ message EventHeader {
+ // Type of event.
+ optional uint32 type = 1;
+ optional uint32 misc = 2;
+ // Size of event.
+ optional uint32 size = 3;
+ }
+
+ message PerfEvent {
+ optional EventHeader header = 1;
+
+ optional MMapEvent mmap_event = 2;
+ optional SampleEvent sample_event = 3;
+ optional CommEvent comm_event = 4;
+ optional ForkEvent fork_event = 5;
+ }
+
+ message PerfEventStats {
+ // Total number of events read from perf data.
+ optional uint32 num_events_read = 1;
+
+ // Total number of various types of events.
+ optional uint32 num_sample_events = 2;
+ optional uint32 num_mmap_events = 3;
+ optional uint32 num_fork_events = 4;
+ optional uint32 num_exit_events = 5;
+
+ // Number of sample events that were successfully mapped by the address
+ // mapper, a quipper module that is used to obscure addresses and convert
+ // them to DSO name + offset. Sometimes it fails to process sample events.
+ // This field allows us to track the success rate of the address mapper.
+ optional uint32 num_sample_events_mapped = 6;
+
+ // Whether address remapping was enabled.
+ optional bool did_remap = 7;
+ }
+
+ message PerfBuildID {
+ // Misc field in perf_event_header.
+ // Indicates whether the file is mapped in kernel mode or user mode.
+ optional uint32 misc = 1;
+
+ // Process ID.
+ optional uint32 pid = 2;
+
+ // Build id. Should always contain kBuildIDArraySize bytes of data.
+ // perf_reader.h in Chrome OS defines kBuildIDArraySize = 20.
+ optional bytes build_hash = 3;
+
+ // Filename Md5sum prefix.
+ // The filename was field 4 and has been intentionally left out.
+ optional uint64 filename_md5_prefix = 5;
+ }
+
+ repeated PerfFileAttr file_attrs = 1;
+ repeated PerfEvent events = 2;
+
+ // Time when quipper generated this perf data / protobuf, given as seconds
+ // since the epoch.
+ optional uint64 timestamp_sec = 3;
+
+ // Records some stats about the serialized perf events.
+ optional PerfEventStats stats = 4;
+
+ // Not added from original: repeated uint64 metadata_mask = 5;
+
+ // Build ID metadata.
+ repeated PerfBuildID build_ids = 7;
+
+ // Not added from original: repeated PerfUint32Metadata uint32_metadata = 8;
+ // Not added from original: repeated PerfUint64Metadata uint64_metadata = 9;
+ // Not added from original:
+ // optional PerfCPUTopologyMetadata cpu_topology = 11;
+ // Not added from original:
+ // repeated PerfNodeTopologyMetadata numa_topology = 12;
+
+ message StringMetadata {
+ message StringAndMd5sumPrefix {
+ // The string value was field 1 and has been intentionally left out.
+
+ // The string value's md5sum prefix.
+ optional uint64 value_md5_prefix = 2;
+ }
+
+ // Not added from original: optional StringAndMd5sumPrefix hostname = 1;
+ // Not added from original:
+ // optional StringAndMd5sumPrefix kernel_version =2;
+ // Not added from original: optional StringAndMd5sumPrefix perf_version = 3;
+ // Not added from original: optional StringAndMd5sumPrefix architecture = 4;
+ // Not added from original:
+ // optional StringAndMd5sumPrefix cpu_description = 5;
+ // Not added from original: optional StringAndMd5sumPrefix cpu_id = 6;
+ // Not added from original:
+ // repeated StringAndMd5sumPrefix perf_command_line_token = 7;
+
+ // The command line stored as a single string.
+ optional StringAndMd5sumPrefix perf_command_line_whole = 8;
+ }
+
+ // All the string metadata from the perf data file.
+ optional StringMetadata string_metadata = 13;
+}
diff --git a/components/metrics/proto/profiler_event.proto b/components/metrics/proto/profiler_event.proto
new file mode 100644
index 0000000..1f18b2a
--- /dev/null
+++ b/components/metrics/proto/profiler_event.proto
@@ -0,0 +1,92 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Performance metrics collected via Chrome's built-in profiler.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+
+// Next tag: 4
+message ProfilerEventProto {
+ // The type of this profile.
+ enum ProfileType {
+ UNKNOWN_PROFILE = 0; // Unknown type (should not reach here).
+ STARTUP_PROFILE = 1; // Startup profile, logged approximately 60 seconds
+ // after launch.
+ }
+ optional ProfileType profile_type = 1;
+
+ // The source based upon which "time" measurements are made.
+ // We currently only measure wall clock time; but we are exploring other
+ // measurement sources as well, such as CPU time or TCMalloc statistics.
+ enum TimeSource {
+ UNKNOWN_TIME_SOURCE = 0; // Unknown source (should not reach here).
+ WALL_CLOCK_TIME = 1; // Total time elapsed between the start and end of
+ // the task's execution.
+ }
+ optional TimeSource time_source = 2;
+
+ // Data for a single tracked object (typically, a Task).
+ message TrackedObject {
+ // The name of the thread from which this task was posted, hashed.
+ optional fixed64 birth_thread_name_hash = 1;
+
+ // The name of the thread on which this task was executed, hashed.
+ optional fixed64 exec_thread_name_hash = 2;
+
+ // The source file name from which this task was posted, hashed.
+ optional fixed64 source_file_name_hash = 3;
+
+ // Function name from which this task was posted, hashed.
+ optional fixed64 source_function_name_hash = 4;
+
+ // The line number within the source file from which this task was posted.
+ optional int32 source_line_number = 5;
+
+ // The number of times this task was executed.
+ optional int32 exec_count = 6;
+
+ // The total execution time for instances this task.
+ optional int32 exec_time_total = 7;
+
+ // The execution time for a uniformly randomly sampled instance of this
+ // task.
+ optional int32 exec_time_sampled = 8;
+
+ // The total time instances this task spent waiting (e.g. in a message loop)
+ // before they were run.
+ optional int32 queue_time_total = 9;
+
+ // The time that a uniformly randomly sampled instance of this task spent
+ // waiting (e.g. in a message loop) before it was run.
+ optional int32 queue_time_sampled = 10;
+
+ // The type of process within which this task was executed.
+ enum ProcessType {
+ UNKNOWN = 0; // Should not reach here
+ BROWSER = 1;
+ RENDERER = 2;
+ PLUGIN = 3;
+ WORKER = 4;
+ NACL_LOADER = 5;
+ UTILITY = 6;
+ PROFILE_IMPORT = 7;
+ ZYGOTE = 8;
+ SANDBOX_HELPER = 9;
+ NACL_BROKER = 10;
+ GPU = 11;
+ PPAPI_PLUGIN = 12;
+ PPAPI_BROKER = 13;
+ }
+ optional ProcessType process_type = 11;
+
+ // The local PID for the process within which this task was executed.
+ optional uint32 process_id = 12;
+ }
+ repeated TrackedObject tracked_object = 3;
+}
diff --git a/components/metrics/proto/system_profile.proto b/components/metrics/proto/system_profile.proto
new file mode 100644
index 0000000..98a0f5d
--- /dev/null
+++ b/components/metrics/proto/system_profile.proto
@@ -0,0 +1,509 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Stores information about the user's brower and system configuration.
+// The system configuration fields are recorded once per client session.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+// Next tag: 19
+message SystemProfileProto {
+ // The time when the client was compiled/linked, in seconds since the epoch.
+ optional int64 build_timestamp = 1;
+
+ // A version number string for the application.
+ // Most commonly this is the browser version number found in a user agent
+ // string, and is typically a 4-tuple of numbers separated by periods. In
+ // cases where the user agent version might be ambiguous (example: Linux 64-
+ // bit build, rather than 32-bit build, or a Windows version used in some
+ // special context, such as ChromeFrame running in IE), then this may include
+ // some additional postfix to provide clarification not available in the UA
+ // string.
+ //
+ // An example of a browser version 4-tuple is "5.0.322.0". Currently used
+ // postfixes are:
+ //
+ // "-64": a 64-bit build
+ // "-F": Chrome is running under control of ChromeFrame
+ // "-devel": this is not an official build of Chrome
+ //
+ // A full version number string could look similar to:
+ // "5.0.322.0-F-devel".
+ //
+ // This value, when available, is more trustworthy than the UA string
+ // associated with the request; and including the postfix, may be more
+ // specific.
+ optional string app_version = 2;
+
+ // The brand code or distribution tag assigned to a partner, if available.
+ // Brand codes are only available on Windows. Not every Windows install
+ // though will have a brand code.
+ optional string brand_code = 12;
+
+ // The possible channels for an installation, from least to most stable.
+ enum Channel {
+ CHANNEL_UNKNOWN = 0; // Unknown channel -- perhaps an unofficial build?
+ CHANNEL_CANARY = 1;
+ CHANNEL_DEV = 2;
+ CHANNEL_BETA = 3;
+ CHANNEL_STABLE = 4;
+ }
+ optional Channel channel = 10;
+
+ // The date the user enabled UMA, in seconds since the epoch.
+ // If the user has toggled the UMA enabled state multiple times, this will
+ // be the most recent date on which UMA was enabled.
+ // For privacy, this is rounded to the nearest hour.
+ optional int64 uma_enabled_date = 3;
+
+ // The time when the client was installed, in seconds since the epoch.
+ // For privacy, this is rounded to the nearest hour.
+ optional int64 install_date = 16;
+
+ // The user's selected application locale, i.e. the user interface language.
+ // The locale includes a language code and, possibly, also a country code,
+ // e.g. "en-US".
+ optional string application_locale = 4;
+
+ // Information on the user's operating system.
+ message OS {
+ // The user's operating system.
+ optional string name = 1;
+
+ // The version of the OS. The meaning of this field is OS-dependent.
+ optional string version = 2;
+
+ // The fingerprint of the build. This field is used only on Android.
+ optional string fingerprint = 3;
+ }
+ optional OS os = 5;
+
+ // Next tag for Hardware: 16
+ // Information on the user's hardware.
+ message Hardware {
+ // The CPU architecture (x86, PowerPC, x86_64, ...)
+ optional string cpu_architecture = 1;
+
+ // The amount of RAM present on the system, in megabytes.
+ optional int64 system_ram_mb = 2;
+
+ // The base memory address that chrome.dll was loaded at.
+ // (Logged only on Windows.)
+ optional int64 dll_base = 3;
+
+ // The Chrome OS device hardware class ID is a unique string associated with
+ // each Chrome OS device product revision generally assigned at hardware
+ // qualification time. The hardware class effectively identifies the
+ // configured system components such as CPU, WiFi adapter, etc.
+ //
+ // An example of such a hardware class is "IEC MARIO PONY 6101". An
+ // internal database associates this hardware class with the qualified
+ // device specifications including OEM information, schematics, hardware
+ // qualification reports, test device tags, etc.
+ optional string hardware_class = 4;
+
+ // The number of physical screens.
+ optional int32 screen_count = 5;
+
+ // The screen dimensions of the primary screen, in pixels.
+ optional int32 primary_screen_width = 6;
+ optional int32 primary_screen_height = 7;
+
+ // The device scale factor of the primary screen.
+ optional float primary_screen_scale_factor = 12;
+
+ // Max DPI for any attached screen. (Windows only)
+ optional float max_dpi_x = 9;
+ optional float max_dpi_y = 10;
+
+ // Information on the CPU obtained by CPUID.
+ message CPU {
+ // A 12 character string naming the vendor, e.g. "GeniuneIntel".
+ optional string vendor_name = 1;
+
+ // The signature reported by CPUID (from EAX).
+ optional uint32 signature = 2;
+ }
+ optional CPU cpu = 13;
+
+ // Information on the GPU
+ message Graphics {
+ // The GPU manufacturer's vendor id.
+ optional uint32 vendor_id = 1;
+
+ // The GPU manufacturer's device id for the chip set.
+ optional uint32 device_id = 2;
+
+ // The driver version on the GPU.
+ optional string driver_version = 3;
+
+ // The driver date on the GPU.
+ optional string driver_date = 4;
+
+ // The GPU performance statistics.
+ // See http://src.chromium.org/viewvc/chrome/trunk/src/content/public/common/gpu_performance_stats.h?view=markup
+ // for details. Currently logged only on Windows.
+ message PerformanceStatistics {
+ optional float graphics_score = 1;
+ optional float gaming_score = 2;
+ optional float overall_score = 3;
+ }
+ optional PerformanceStatistics performance_statistics = 5;
+
+ // The GL_VENDOR string. An example of a gl_vendor string is
+ // "Imagination Technologies". "" if we are not using OpenGL.
+ optional string gl_vendor = 6;
+
+ // The GL_RENDERER string. An example of a gl_renderer string is
+ // "PowerVR SGX 540". "" if we are not using OpenGL.
+ optional string gl_renderer = 7;
+ }
+ optional Graphics gpu = 8;
+
+ // Information about Bluetooth devices paired with the system.
+ message Bluetooth {
+ // Whether Bluetooth is present on this system.
+ optional bool is_present = 1;
+
+ // Whether Bluetooth is enabled on this system.
+ optional bool is_enabled = 2;
+
+ // Describes a paired device.
+ message PairedDevice {
+ // Assigned class of the device. This is a bitfield according to the
+ // Bluetooth specification available at the following URL:
+ // https://www.bluetooth.org/en-us/specification/assigned-numbers-overview/baseband
+ optional uint32 bluetooth_class = 1;
+
+ // Decoded device type.
+ enum Type {
+ DEVICE_UNKNOWN = 0;
+ DEVICE_COMPUTER = 1;
+ DEVICE_PHONE = 2;
+ DEVICE_MODEM = 3;
+ DEVICE_AUDIO = 4;
+ DEVICE_CAR_AUDIO = 5;
+ DEVICE_VIDEO = 6;
+ DEVICE_PERIPHERAL = 7;
+ DEVICE_JOYSTICK = 8;
+ DEVICE_GAMEPAD = 9;
+ DEVICE_KEYBOARD = 10;
+ DEVICE_MOUSE = 11;
+ DEVICE_TABLET = 12;
+ DEVICE_KEYBOARD_MOUSE_COMBO = 13;
+ }
+ optional Type type = 2;
+
+ // Vendor prefix of the Bluetooth address, these are OUI registered by
+ // the IEEE and are encoded with the first byte in bits 16-23, the
+ // second byte in bits 8-15 and the third byte in bits 0-7.
+ //
+ // ie. Google's OUI (00:1A:11) is encoded as 0x00001A11
+ optional uint32 vendor_prefix = 4;
+
+ // The Vendor ID of a device, returned in vendor_id below, can be
+ // either allocated by the Bluetooth SIG or USB IF, providing two
+ // completely overlapping namespaces for identifiers.
+ //
+ // This field should be read along with vendor_id to correctly
+ // identify the vendor. For example Google is identified by either
+ // vendor_id_source = VENDOR_ID_BLUETOOTH, vendor_id = 0x00E0 or
+ // vendor_id_source = VENDOR_ID_USB, vendor_id = 0x18D1.
+ //
+ // If the device does not support the Device ID specification the
+ // unknown value will be set.
+ enum VendorIDSource {
+ VENDOR_ID_UNKNOWN = 0;
+ VENDOR_ID_BLUETOOTH = 1;
+ VENDOR_ID_USB = 2;
+ }
+ optional VendorIDSource vendor_id_source = 8;
+
+ // Vendor ID of the device, where available.
+ optional uint32 vendor_id = 5;
+
+ // Product ID of the device, where available.
+ optional uint32 product_id = 6;
+
+ // Device ID of the device, generally the release or version number in
+ // BCD format, where available.
+ optional uint32 device_id = 7;
+ }
+ repeated PairedDevice paired_device = 3;
+ }
+ optional Bluetooth bluetooth = 11;
+
+ // Whether the internal display produces touch events. Omitted if unknown.
+ // Logged on ChromeOS only.
+ optional bool internal_display_supports_touch = 14;
+
+ // Vendor ids and product ids of external touchscreens.
+ message TouchScreen {
+ // Touch screen vendor id.
+ optional uint32 vendor_id = 1;
+ // Touch screen product id.
+ optional uint32 product_id = 2;
+ }
+ // Lists vendor and product ids of external touchscreens.
+ // Logged on ChromeOS only.
+ repeated TouchScreen external_touchscreen = 15;
+ }
+ optional Hardware hardware = 6;
+
+ // Information about the network connection.
+ message Network {
+ // Set to true if connection_type changed during the lifetime of the log.
+ optional bool connection_type_is_ambiguous = 1;
+
+ // See net::NetworkChangeNotifier::ConnectionType.
+ enum ConnectionType {
+ CONNECTION_UNKNOWN = 0;
+ CONNECTION_ETHERNET = 1;
+ CONNECTION_WIFI = 2;
+ CONNECTION_2G = 3;
+ CONNECTION_3G = 4;
+ CONNECTION_4G = 5;
+ }
+ // The connection type according to NetworkChangeNotifier.
+ optional ConnectionType connection_type = 2;
+
+ // Set to true if wifi_phy_layer_protocol changed during the lifetime of the log.
+ optional bool wifi_phy_layer_protocol_is_ambiguous = 3;
+
+ // See net::WifiPHYLayerProtocol.
+ enum WifiPHYLayerProtocol {
+ WIFI_PHY_LAYER_PROTOCOL_NONE = 0;
+ WIFI_PHY_LAYER_PROTOCOL_ANCIENT = 1;
+ WIFI_PHY_LAYER_PROTOCOL_A = 2;
+ WIFI_PHY_LAYER_PROTOCOL_B = 3;
+ WIFI_PHY_LAYER_PROTOCOL_G = 4;
+ WIFI_PHY_LAYER_PROTOCOL_N = 5;
+ WIFI_PHY_LAYER_PROTOCOL_UNKNOWN = 6;
+ }
+ // The physical layer mode of the associated wifi access point, if any.
+ optional WifiPHYLayerProtocol wifi_phy_layer_protocol = 4;
+ }
+ optional Network network = 13;
+
+ // Information on the Google Update install that is managing this client.
+ message GoogleUpdate {
+ // Whether the Google Update install is system-level or user-level.
+ optional bool is_system_install = 1;
+
+ // The date at which Google Update last started performing an automatic
+ // update check, in seconds since the Unix epoch.
+ optional int64 last_automatic_start_timestamp = 2;
+
+ // The date at which Google Update last successfully sent an update check
+ // and recieved an intact response from the server, in seconds since the
+ // Unix epoch. (The updates don't need to be successfully installed.)
+ optional int64 last_update_check_timestamp = 3;
+
+ // Describes a product being managed by Google Update. (This can also
+ // describe Google Update itself.)
+ message ProductInfo {
+ // The current version of the product that is installed.
+ optional string version = 1;
+
+ // The date at which Google Update successfully updated this product,
+ // stored in seconds since the Unix epoch. This is updated when an update
+ // is successfully applied, or if the server reports that no update
+ // is available.
+ optional int64 last_update_success_timestamp = 2;
+
+ // The result reported by the product updater on its last run.
+ enum InstallResult {
+ INSTALL_RESULT_SUCCESS = 0;
+ INSTALL_RESULT_FAILED_CUSTOM_ERROR = 1;
+ INSTALL_RESULT_FAILED_MSI_ERROR = 2;
+ INSTALL_RESULT_FAILED_SYSTEM_ERROR = 3;
+ INSTALL_RESULT_EXIT_CODE = 4;
+ }
+ optional InstallResult last_result = 3;
+
+ // The error code reported by the product updater on its last run. This
+ // will typically be a error code specific to the product installer.
+ optional int32 last_error = 4;
+
+ // The extra error code reported by the product updater on its last run.
+ // This will typically be a Win32 error code.
+ optional int32 last_extra_error = 5;
+ }
+ optional ProductInfo google_update_status = 4;
+ optional ProductInfo client_status = 5;
+ }
+ optional GoogleUpdate google_update = 11;
+
+ // Information on all installed plugins.
+ message Plugin {
+ // The plugin's self-reported name and filename (without path).
+ optional string name = 1;
+ optional string filename = 2;
+
+ // The plugin's version.
+ optional string version = 3;
+
+ // True if the plugin is disabled.
+ // If a client has multiple local Chrome user accounts, this is logged based
+ // on the first user account launched during the current session.
+ optional bool is_disabled = 4;
+
+ // True if the plugin is PPAPI.
+ optional bool is_pepper = 5;
+ }
+ repeated Plugin plugin = 7;
+
+ // Figures that can be used to generate application stability metrics.
+ // All values are counts of events since the last time that these
+ // values were reported.
+ // Next tag: 24
+ message Stability {
+ // Total amount of time that the program was running, in seconds,
+ // since the last time a log was recorded, as measured using a client-side
+ // clock implemented via TimeTicks, which guarantees that it is monotonic
+ // and does not jump if the user changes his/her clock. The TimeTicks
+ // implementation also makes the clock not count time the computer is
+ // suspended.
+ optional int64 incremental_uptime_sec = 1;
+
+ // Total amount of time that the program was running, in seconds,
+ // since startup, as measured using a client-side clock implemented
+ // via TimeTicks, which guarantees that it is monotonic and does not
+ // jump if the user changes his/her clock. The TimeTicks implementation
+ // also makes the clock not count time the computer is suspended.
+ // This field was added for M-35.
+ optional int64 uptime_sec = 23;
+
+ // Page loads along with renderer crashes and hangs, since page load count
+ // roughly corresponds to usage.
+ optional int32 page_load_count = 2;
+ optional int32 renderer_crash_count = 3;
+ optional int32 renderer_hang_count = 4;
+
+ // Number of renderer crashes that were for extensions.
+ // TODO(isherman): Figure out whether this is also counted in
+ // |renderer_crash_count|.
+ optional int32 extension_renderer_crash_count = 5;
+
+ // Number of non-renderer child process crashes.
+ optional int32 child_process_crash_count = 6;
+
+ // Number of times the browser has crashed while logged in as the "other
+ // user" (guest) account.
+ // Logged on ChromeOS only.
+ optional int32 other_user_crash_count = 7;
+
+ // Number of times the kernel has crashed.
+ // Logged on ChromeOS only.
+ optional int32 kernel_crash_count = 8;
+
+ // Number of times the system has shut down uncleanly.
+ // Logged on ChromeOS only.
+ optional int32 unclean_system_shutdown_count = 9;
+
+ //
+ // All the remaining fields in the Stability are recorded at most once per
+ // client session.
+ //
+
+ // The number of times the program was launched.
+ // This will typically be equal to 1. However, it is possible that Chrome
+ // was unable to upload stability metrics for previous launches (e.g. due to
+ // crashing early during startup), and hence this value might be greater
+ // than 1.
+ optional int32 launch_count = 15;
+ // The number of times that it didn't exit cleanly (which we assume to be
+ // mostly crashes).
+ optional int32 crash_count = 16;
+
+ // The number of times the program began, but did not complete, the shutdown
+ // process. (For example, this may occur when Windows is shutting down, and
+ // it only gives the process a few seconds to clean up.)
+ optional int32 incomplete_shutdown_count = 17;
+
+ // The number of times the program was able register with breakpad crash
+ // services.
+ optional int32 breakpad_registration_success_count = 18;
+
+ // The number of times the program failed to register with breakpad crash
+ // services. If crash registration fails then when the program crashes no
+ // crash report will be generated.
+ optional int32 breakpad_registration_failure_count = 19;
+
+ // The number of times the program has run under a debugger. This should
+ // be an exceptional condition. Running under a debugger prevents crash
+ // dumps from being generated.
+ optional int32 debugger_present_count = 20;
+
+ // The number of times the program has run without a debugger attached.
+ // This should be most common scenario and should be very close to
+ // |launch_count|.
+ optional int32 debugger_not_present_count = 21;
+
+ // Stability information for all installed plugins.
+ message PluginStability {
+ // The relevant plugin's information (name, etc.)
+ optional Plugin plugin = 1;
+
+ // The number of times this plugin's process was launched.
+ optional int32 launch_count = 2;
+
+ // The number of times this plugin was instantiated on a web page.
+ // This will be >= |launch_count|.
+ // (A page load with multiple sections drawn by this plugin will
+ // increase this count multiple times.)
+ optional int32 instance_count = 3;
+
+ // The number of times this plugin process crashed.
+ // This value will be <= |launch_count|.
+ optional int32 crash_count = 4;
+
+ // The number of times this plugin could not be loaded.
+ optional int32 loading_error_count = 5;
+ }
+ repeated PluginStability plugin_stability = 22;
+ }
+ optional Stability stability = 8;
+
+ // Description of a field trial or experiment that the user is currently
+ // enrolled in.
+ // All metrics reported in this upload can potentially be influenced by the
+ // field trial.
+ message FieldTrial {
+ // The name of the field trial, as a 32-bit identifier.
+ // Currently, the identifier is a hash of the field trial's name.
+ optional fixed32 name_id = 1;
+
+ // The user's group within the field trial, as a 32-bit identifier.
+ // Currently, the identifier is a hash of the group's name.
+ optional fixed32 group_id = 2;
+ }
+ repeated FieldTrial field_trial = 9;
+
+ // Number of users currently signed into a multiprofile session.
+ // A zero value indicates that the user count changed while the log is open.
+ // Logged only on ChromeOS.
+ optional uint32 multi_profile_user_count = 17;
+
+ // Information about extensions that are installed, masked to provide better
+ // privacy. Only extensions from a single profile are reported; this will
+ // generally be the profile used when the browser is started. The profile
+ // reported on will remain consistent at least until the browser is
+ // relaunched (or the profile is deleted by the user).
+ //
+ // Each client first picks a value for client_key derived from its UMA
+ // client_id:
+ // client_key = client_id % 4096
+ // Then, each installed extension is mapped into a hash bucket according to
+ // bucket = CityHash64(StringPrintf("%d:%s",
+ // client_key, extension_id)) % 1024
+ // The client reports the set of hash buckets occupied by all installed
+ // extensions. If multiple extensions map to the same bucket, that bucket is
+ // still only reported once.
+ repeated int32 occupied_extension_bucket = 18;
+}
diff --git a/components/metrics/proto/user_action_event.proto b/components/metrics/proto/user_action_event.proto
new file mode 100644
index 0000000..6817dcd
--- /dev/null
+++ b/components/metrics/proto/user_action_event.proto
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Stores information about an event that occurs in response to a user action,
+// e.g. an interaction with a browser UI element.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package metrics;
+
+// Next tag: 3
+message UserActionEventProto {
+ // The name of the action, hashed.
+ optional fixed64 name_hash = 1;
+
+ // The timestamp for the event, in seconds since the epoch.
+ optional int64 time = 2;
+}