Add weekly crash counters, refactor metrics_daemon, respect opt-in in library.
BUG=5340,5814
Change-Id: I2c207055f1ebe48051193395e2dbe38d9140b025
Review URL: http://codereview.chromium.org/3171023
diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc
index 91e9ca6..e97f9ac 100644
--- a/metrics/c_metrics_library.cc
+++ b/metrics/c_metrics_library.cc
@@ -26,8 +26,8 @@
}
extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
- const char* name, int sample,
- int min, int max, int nbuckets) {
+ const char* name, int sample,
+ int min, int max, int nbuckets) {
MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
if (lib == NULL)
return 0;
@@ -35,10 +35,17 @@
}
extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
- const char* name, int sample,
- int max) {
+ const char* name, int sample,
+ int max) {
MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
if (lib == NULL)
return 0;
return lib->SendEnumToUMA(std::string(name), sample, max);
}
+
+extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) {
+ MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+ if (lib == NULL)
+ return 0;
+ return lib->AreMetricsEnabled();
+}
diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h
index 6d1e6f9..e691ad6 100644
--- a/metrics/c_metrics_library.h
+++ b/metrics/c_metrics_library.h
@@ -21,13 +21,17 @@
// C wrapper for MetricsLibrary::SendToUMA.
int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
- const char* name, int sample,
- int min, int max, int nbuckets);
+ const char* name, int sample,
+ int min, int max, int nbuckets);
// C wrapper for MetricsLibrary::SendEnumToUMA.
int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
- const char* name, int sample, int max);
+ const char* name, int sample, int max);
+
+// C wrapper for MetricsLibrary::AreMetricsEnabled.
+int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle);
+
#if defined(__cplusplus)
}
#endif
-#endif // C_METRICS_LIBRARY_H_
+#endif // C_METRICS_LIBRARY_H_
diff --git a/metrics/counter.cc b/metrics/counter.cc
index 4de1db1..95d1faf 100644
--- a/metrics/counter.cc
+++ b/metrics/counter.cc
@@ -8,6 +8,7 @@
#include <base/eintr_wrapper.h>
#include <base/logging.h>
+#include "metrics_library.h"
namespace chromeos_metrics {
@@ -31,8 +32,7 @@
// TaggedCounter implementation.
TaggedCounter::TaggedCounter()
- : filename_(NULL),
- reporter_(NULL),
+ : reporter_(NULL),
reporter_handle_(NULL),
record_state_(kRecordInvalid) {}
@@ -72,12 +72,13 @@
}
DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush;
- DCHECK(filename_);
+ DCHECK(!filename_.empty());
// NOTE: The assumption is that this TaggedCounter object is the
// sole owner of the persistent storage file so no locking is
// necessary.
- int fd = HANDLE_EINTR(open(filename_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ int fd = HANDLE_EINTR(open(filename_.c_str(),
+ O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
if (fd < 0) {
PLOG(WARNING) << "Unable to open the persistent counter file";
return;
@@ -183,21 +184,57 @@
}
}
+MetricsLibraryInterface* TaggedCounterReporter::metrics_lib_ = NULL;
+
+TaggedCounterReporter::TaggedCounterReporter()
+ : tagged_counter_(new TaggedCounter()),
+ min_(0),
+ max_(0),
+ buckets_(0) {
+}
+
+TaggedCounterReporter::~TaggedCounterReporter() {
+}
+
+void TaggedCounterReporter::Init(const char* filename,
+ const char* histogram_name,
+ int min,
+ int max,
+ int buckets) {
+ tagged_counter_->Init(filename, Report, this);
+ histogram_name_ = histogram_name;
+ min_ = min;
+ max_ = max;
+ buckets_ = buckets;
+ CHECK(min_ >= 0);
+ CHECK(max_ > min_);
+ CHECK(buckets_ > 0);
+}
+
+void TaggedCounterReporter::Report(void* handle, int32 tag, int32 count) {
+ TaggedCounterReporter* this_reporter =
+ reinterpret_cast<TaggedCounterReporter*>(handle);
+ DLOG(INFO) << "received metric: " << this_reporter->histogram_name_
+ << " " << count << " " << this_reporter->min_ << " "
+ << this_reporter->max_ << " " << this_reporter->buckets_;
+ CHECK(metrics_lib_ != NULL);
+ CHECK(this_reporter->buckets_ > 0);
+ metrics_lib_->SendToUMA(this_reporter->histogram_name_,
+ count,
+ this_reporter->min_,
+ this_reporter->max_,
+ this_reporter->buckets_);
+}
+
FrequencyCounter::FrequencyCounter() : cycle_duration_(1) {
}
FrequencyCounter::~FrequencyCounter() {
}
-void FrequencyCounter::Init(const char* filename,
- TaggedCounterInterface::Reporter reporter,
- void* reporter_handle,
+void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter,
time_t cycle_duration) {
- // Allow tests to inject tagged_counter_ dependency.
- if (tagged_counter_.get() == NULL) {
- tagged_counter_.reset(new TaggedCounter());
- }
- tagged_counter_->Init(filename, reporter, reporter_handle);
+ tagged_counter_.reset(tagged_counter);
DCHECK(cycle_duration > 0);
cycle_duration_ = cycle_duration;
}
diff --git a/metrics/counter.h b/metrics/counter.h
index a5e5302..e55cded 100644
--- a/metrics/counter.h
+++ b/metrics/counter.h
@@ -5,12 +5,15 @@
#ifndef METRICS_COUNTER_H_
#define METRICS_COUNTER_H_
+#include <string>
#include <time.h>
#include <base/basictypes.h>
#include <base/scoped_ptr.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
+class MetricsLibraryInterface;
+
namespace chromeos_metrics {
// Constants useful for frequency statistics.
@@ -44,16 +47,6 @@
virtual ~TaggedCounterInterface() {}
- // Initializes the counter by providing the persistent storage
- // location |filename| and a |reporter| callback for reporting
- // aggregated counts. |reporter_handle| is sent to the |reporter|
- // along with the aggregated counts.
- //
- // NOTE: The assumption is that this object is the sole owner of the
- // persistent storage file so no locking is currently implemented.
- virtual void Init(const char* filename,
- Reporter reporter, void* reporter_handle) = 0;
-
// Adds |count| of events for the given |tag|. If there's an
// existing aggregated count for a different tag, it's reported
// through the reporter callback and discarded.
@@ -67,12 +60,21 @@
class TaggedCounter : public TaggedCounterInterface {
public:
TaggedCounter();
- ~TaggedCounter();
+ virtual ~TaggedCounter();
+
+ // Initializes the counter by providing the persistent storage
+ // location |filename| and a |reporter| callback for reporting
+ // aggregated counts. |reporter_handle| is sent to the |reporter|
+ // along with the aggregated counts.
+ //
+ // NOTE: The assumption is that this object is the sole owner of the
+ // persistent storage file so no locking is currently implemented.
+ virtual void Init(const char* filename,
+ Reporter reporter, void* reporter_handle);
// Implementation of interface methods.
- void Init(const char* filename, Reporter reporter, void* reporter_handle);
- void Update(int32 tag, int32 count);
- void Flush();
+ virtual void Update(int32 tag, int32 count);
+ virtual void Flush();
private:
friend class RecordTest;
@@ -146,7 +148,7 @@
void WriteRecord(int fd);
// Persistent storage file path.
- const char* filename_;
+ std::string filename_;
// Aggregated data reporter callback and handle to pass-through.
Reporter reporter_;
@@ -159,6 +161,71 @@
RecordState record_state_;
};
+// TaggedCounterReporter provides a TaggedCounterInterface which both
+// counts tagged events and reports them up through the metrics
+// library to UMA.
+class TaggedCounterReporter : public TaggedCounterInterface {
+ public:
+ TaggedCounterReporter();
+ virtual ~TaggedCounterReporter();
+
+ // Set the metrics library used by all TaggedCounterReporter
+ // instances. We assume there is only one metrics library
+ // shared amongst all reporters.
+ static void SetMetricsLibraryInterface(MetricsLibraryInterface* metrics_lib) {
+ metrics_lib_ = metrics_lib;
+ }
+
+ // Initializes the counter by providing the persistent storage
+ // location |filename|, a |histogram_name| (a linear histogram) to
+ // report to with |min|, |max|, and |buckets| attributes for the
+ // histogram.
+ virtual void Init(const char* filename,
+ const char* histogram_name,
+ int min,
+ int max,
+ int buckets);
+
+ // Implementation of interface method.
+ virtual void Update(int32 tag, int32 count) {
+ tagged_counter_->Update(tag, count);
+ }
+ // Implementation of interface method.
+ virtual void Flush() {
+ tagged_counter_->Flush();
+ }
+
+ // Accessor functions.
+ const std::string& histogram_name() const {
+ return histogram_name_;
+ }
+
+ int min() const {
+ return min_;
+ }
+
+ int max() const {
+ return max_;
+ }
+
+ int buckets() const {
+ return buckets_;
+ }
+
+ protected:
+ friend class TaggedCounterReporterTest;
+ FRIEND_TEST(TaggedCounterReporterTest, Report);
+
+ static void Report(void* handle, int32 tag, int32 count);
+
+ static MetricsLibraryInterface* metrics_lib_;
+ scoped_ptr<TaggedCounter> tagged_counter_;
+ std::string histogram_name_;
+ int min_;
+ int max_;
+ int buckets_;
+};
+
// FrequencyCounter uses TaggedCounter to maintain a persistent
// storage of the number of events that occur in a given cycle
// duration (in other words, a frequency count). For example, to
@@ -172,13 +239,11 @@
FrequencyCounter();
virtual ~FrequencyCounter();
- // Initialize a frequency counter, which is necessary before first use.
- // |filename|, |reporter|, and |reporter_handle| are used as in
- // TaggedCounter::Init. |cycle_duration| is the number of seconds
- // in a cycle.
- virtual void Init(const char* filename,
- TaggedCounterInterface::Reporter reporter,
- void* reporter_handle,
+ // Initialize a frequency counter, which is necessary before first
+ // use. |tagged_counter| is used to store the counts, its memory
+ // will be managed by this FrequencyCounter. |cycle_duration| is
+ // the number of seconds in a cycle.
+ virtual void Init(TaggedCounterInterface* tagged_counter,
time_t cycle_duration);
// Record that an event occurred. |count| is the number of concurrent
// events that have occurred. The time is implicitly assumed to be the
@@ -187,9 +252,29 @@
UpdateInternal(count, time(NULL));
}
+ // Update the frequency counter based on the current time. If a
+ // cycle has finished, this will have the effect of flushing the
+ // cycle's count, without first requiring another update to the
+ // frequency counter. The more often this is called, the lower the
+ // latency to have a new sample submitted.
+ virtual void FlushFinishedCycles() {
+ Update(0);
+ }
+
+ // Accessor function.
+ const TaggedCounterInterface& tagged_counter() const {
+ return *tagged_counter_;
+ }
+
+ time_t cycle_duration() const {
+ return cycle_duration_;
+ }
+
private:
friend class FrequencyCounterTest;
FRIEND_TEST(FrequencyCounterTest, UpdateInternal);
+ FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForWeek);
+ FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForDay);
void UpdateInternal(int32 count, time_t now);
int32 GetCycleNumber(time_t now);
diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h
index 701cce4..cf2a486 100644
--- a/metrics/counter_mock.h
+++ b/metrics/counter_mock.h
@@ -13,7 +13,7 @@
namespace chromeos_metrics {
-class TaggedCounterMock : public TaggedCounterInterface {
+class TaggedCounterMock : public TaggedCounter {
public:
MOCK_METHOD3(Init, void(const char* filename,
Reporter reporter, void* reporter_handle));
@@ -21,6 +21,17 @@
MOCK_METHOD0(Flush, void());
};
+class TaggedCounterReporterMock : public TaggedCounterReporter {
+ public:
+ MOCK_METHOD5(Init, void(const char* filename,
+ const char* histogram_name,
+ int min,
+ int max,
+ int nbuckets));
+ MOCK_METHOD2(Update, void(int32 tag, int32 count));
+ MOCK_METHOD0(Flush, void());
+};
+
class FrequencyCounterMock : public FrequencyCounter {
public:
MOCK_METHOD4(Init, void(const char* filename,
@@ -28,6 +39,7 @@
void* reporter_handle,
time_t cycle_duration));
MOCK_METHOD1(Update, void(int32 count));
+ MOCK_METHOD0(FlushFinishedCycles, void());
};
} // namespace chromeos_metrics
diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc
index eb68b2a..3580516 100644
--- a/metrics/counter_test.cc
+++ b/metrics/counter_test.cc
@@ -13,6 +13,7 @@
#include "counter.h"
#include "counter_mock.h" // For TaggedCounterMock.
+#include "metrics_library_mock.h"
using ::testing::_;
using ::testing::MockFunction;
@@ -37,7 +38,7 @@
class TaggedCounterTest : public testing::Test {
protected:
virtual void SetUp() {
- EXPECT_EQ(NULL, counter_.filename_);
+ EXPECT_TRUE(counter_.filename_.empty());
EXPECT_TRUE(NULL == counter_.reporter_);
EXPECT_EQ(NULL, counter_.reporter_handle_);
EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
@@ -256,14 +257,85 @@
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
}
+static const char kTestFilename[] = "test_filename";
+static const char kTestHistogram[] = "test_histogram";
+const int kHistogramMin = 15;
+const int kHistogramMax = 1024;
+const int kHistogramBuckets = 23;
+
+class TaggedCounterReporterTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ tagged_counter_ = new StrictMock<TaggedCounterMock>();
+ reporter_.tagged_counter_.reset(tagged_counter_);
+ metrics_lib_.reset(new StrictMock<MetricsLibraryMock>);
+ reporter_.SetMetricsLibraryInterface(metrics_lib_.get());
+ ASSERT_TRUE(metrics_lib_.get() == reporter_.metrics_lib_);
+ }
+ virtual void TearDown() {
+ reporter_.SetMetricsLibraryInterface(NULL);
+ }
+
+ void DoInit();
+ StrictMock<TaggedCounterMock>* tagged_counter_;
+ TaggedCounterReporter reporter_;
+ scoped_ptr<MetricsLibraryMock> metrics_lib_;
+};
+
+void TaggedCounterReporterTest::DoInit() {
+ EXPECT_CALL(*tagged_counter_,
+ Init(kTestFilename,
+ TaggedCounterReporter::Report,
+ &reporter_))
+ .Times(1)
+ .RetiresOnSaturation();
+ reporter_.Init(kTestFilename,
+ kTestHistogram,
+ kHistogramMin,
+ kHistogramMax,
+ kHistogramBuckets);
+ EXPECT_EQ(kTestHistogram, reporter_.histogram_name_);
+ EXPECT_EQ(kHistogramBuckets, reporter_.buckets_);
+ EXPECT_EQ(kHistogramMax, reporter_.max_);
+ EXPECT_EQ(kHistogramMin, reporter_.min_);
+}
+
+TEST_F(TaggedCounterReporterTest, Init) {
+ DoInit();
+}
+
+TEST_F(TaggedCounterReporterTest, Update) {
+ DoInit();
+ EXPECT_CALL(*tagged_counter_, Update(1, 2))
+ .Times(1)
+ .RetiresOnSaturation();
+ reporter_.Update(1, 2);
+}
+
+TEST_F(TaggedCounterReporterTest, Flush) {
+ DoInit();
+ EXPECT_CALL(*tagged_counter_, Flush())
+ .Times(1)
+ .RetiresOnSaturation();
+ reporter_.Flush();
+}
+
+TEST_F(TaggedCounterReporterTest, Report) {
+ DoInit();
+ EXPECT_CALL(*metrics_lib_, SendToUMA(kTestHistogram,
+ 301,
+ kHistogramMin,
+ kHistogramMax,
+ kHistogramBuckets))
+ .Times(1)
+ .RetiresOnSaturation();
+ reporter_.Report(&reporter_, 127, 301);
+}
+
class FrequencyCounterTest : public testing::Test {
protected:
virtual void SetUp() {
- tagged_counter_ = new StrictMock<TaggedCounterMock>;
- frequency_counter_.tagged_counter_.reset(tagged_counter_);
- }
-
- static void FakeReporter(void *, int32, int32) {
+ tagged_counter_ = NULL;
}
void CheckInit(int32 cycle_duration);
@@ -276,14 +348,10 @@
};
void FrequencyCounterTest::CheckInit(int32 cycle_duration) {
- EXPECT_CALL(*tagged_counter_, Init(kTestRecordFile, FakeReporter, this))
- .Times(1)
- .RetiresOnSaturation();
- frequency_counter_.Init(kTestRecordFile,
- FakeReporter,
- this,
- cycle_duration);
+ tagged_counter_ = new StrictMock<TaggedCounterMock>;
+ frequency_counter_.Init(tagged_counter_, cycle_duration);
EXPECT_EQ(cycle_duration, frequency_counter_.cycle_duration_);
+ EXPECT_EQ(tagged_counter_, frequency_counter_.tagged_counter_.get());
}
TEST_F(FrequencyCounterTest, Init) {
@@ -292,10 +360,12 @@
void FrequencyCounterTest::CheckCycleNumber(int32 cycle_duration) {
CheckInit(cycle_duration);
- EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150));
- EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150 +
- cycle_duration - 1));
- EXPECT_EQ(151, frequency_counter_.GetCycleNumber(cycle_duration * 151 + 1));
+ EXPECT_EQ(150, frequency_counter_.GetCycleNumber(
+ cycle_duration * 150));
+ EXPECT_EQ(150, frequency_counter_.GetCycleNumber(
+ cycle_duration * 150 + cycle_duration - 1));
+ EXPECT_EQ(151, frequency_counter_.GetCycleNumber(
+ cycle_duration * 151 + 1));
EXPECT_EQ(0, frequency_counter_.GetCycleNumber(0));
}
@@ -310,7 +380,9 @@
TEST_F(FrequencyCounterTest, UpdateInternal) {
CheckInit(kSecondsPerWeek);
- EXPECT_CALL(*tagged_counter_, Update(150, 2));
+ EXPECT_CALL(*tagged_counter_, Update(150, 2))
+ .Times(1)
+ .RetiresOnSaturation();
frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150);
}
diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
index 0171fde..4d50cb5 100644
--- a/metrics/metrics_daemon.cc
+++ b/metrics/metrics_daemon.cc
@@ -4,10 +4,11 @@
#include "metrics_daemon.h"
-#include <dbus/dbus-glib-lowlevel.h>
+#include <string.h>
#include <base/file_util.h>
#include <base/logging.h>
+#include <dbus/dbus-glib-lowlevel.h>
#include "counter.h"
@@ -43,7 +44,7 @@
static const char kUncleanShutdownDetectedFile[] =
"/tmp/unclean-shutdown-detected";
-// static metrics parameters.
+// static metrics parameters
const char MetricsDaemon::kMetricDailyUseTimeName[] =
"Logging.DailyUseTime";
const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
@@ -73,16 +74,26 @@
// crash frequency metrics
const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
"Logging.AnyCrashesDaily";
+const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
+ "Logging.AnyCrashesWeekly";
const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
"Logging.KernelCrashesDaily";
+const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
+ "Logging.KernelCrashesWeekly";
const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
"Logging.UncleanShutdownsDaily";
+const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
+ "Logging.UncleanShutdownsWeekly";
const char MetricsDaemon::kMetricUserCrashesDailyName[] =
"Logging.UserCrashesDaily";
-const char MetricsDaemon::kMetricCrashesDailyMin = 1;
-const char MetricsDaemon::kMetricCrashesDailyMax = 100;
-const char MetricsDaemon::kMetricCrashesDailyBuckets = 50;
+const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
+ "Logging.UserCrashesWeekly";
+const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
+const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
+const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
+// persistent metrics path
+const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
// static
@@ -182,7 +193,17 @@
usemon_interval_(0),
usemon_source_(NULL) {}
-MetricsDaemon::~MetricsDaemon() {}
+MetricsDaemon::~MetricsDaemon() {
+ DeleteFrequencyCounters();
+}
+
+void MetricsDaemon::DeleteFrequencyCounters() {
+ for (FrequencyCounters::iterator i = frequency_counters_.begin();
+ i != frequency_counters_.end(); ++i) {
+ delete i->second;
+ i->second = NULL;
+ }
+}
void MetricsDaemon::Run(bool run_as_daemon) {
if (run_as_daemon && daemon(0, 0) != 0)
@@ -199,64 +220,72 @@
Loop();
}
+FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
+ return FilePath(kMetricsPath).Append(histogram_name);
+}
+
+void MetricsDaemon::ConfigureCrashIntervalReporter(
+ const char* histogram_name,
+ scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
+ reporter->reset(new chromeos_metrics::TaggedCounterReporter());
+ FilePath file_path = GetHistogramPath(histogram_name);
+ (*reporter)->Init(file_path.value().c_str(),
+ histogram_name,
+ kMetricCrashIntervalMin,
+ kMetricCrashIntervalMax,
+ kMetricCrashIntervalBuckets);
+}
+
+void MetricsDaemon::ConfigureCrashFrequencyReporter(
+ const char* histogram_name) {
+ scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
+ new chromeos_metrics::TaggedCounterReporter());
+ FilePath file_path = GetHistogramPath(histogram_name);
+ reporter->Init(file_path.value().c_str(),
+ histogram_name,
+ kMetricCrashFrequencyMin,
+ kMetricCrashFrequencyMax,
+ kMetricCrashFrequencyBuckets);
+ scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
+ new chromeos_metrics::FrequencyCounter());
+ time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
+ chromeos_metrics::kSecondsPerWeek :
+ chromeos_metrics::kSecondsPerDay;
+ new_counter->Init(
+ static_cast<chromeos_metrics::TaggedCounterInterface*>(
+ reporter.release()),
+ cycle_duration);
+ frequency_counters_[histogram_name] = new_counter.release();
+}
+
void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) {
testing_ = testing;
DCHECK(metrics_lib != NULL);
metrics_lib_ = metrics_lib;
+ chromeos_metrics::TaggedCounterReporter::
+ SetMetricsLibraryInterface(metrics_lib);
static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
daily_use_.reset(new chromeos_metrics::TaggedCounter());
daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
- static const char kUserCrashIntervalRecordFile[] =
- "/var/log/metrics/user-crash-interval";
- user_crash_interval_.reset(new chromeos_metrics::TaggedCounter());
- user_crash_interval_->Init(kUserCrashIntervalRecordFile,
- &ReportUserCrashInterval, this);
+ ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
+ &kernel_crash_interval_);
+ ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
+ &unclean_shutdown_interval_);
+ ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
+ &user_crash_interval_);
- static const char kKernelCrashIntervalRecordFile[] =
- "/var/log/metrics/kernel-crash-interval";
- kernel_crash_interval_.reset(new chromeos_metrics::TaggedCounter());
- kernel_crash_interval_->Init(kKernelCrashIntervalRecordFile,
- &ReportKernelCrashInterval, this);
-
- static const char kUncleanShutdownDetectedFile[] =
- "/var/log/metrics/unclean-shutdown-interval";
- unclean_shutdown_interval_.reset(new chromeos_metrics::TaggedCounter());
- unclean_shutdown_interval_->Init(kUncleanShutdownDetectedFile,
- &ReportUncleanShutdownInterval, this);
-
- static const char kUserCrashesDailyRecordFile[] =
- "/var/log/metrics/user-crashes-daily";
- user_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
- user_crashes_daily_->Init(kUserCrashesDailyRecordFile,
- &ReportUserCrashesDaily,
- this,
- chromeos_metrics::kSecondsPerDay);
-
- static const char kKernelCrashesDailyRecordFile[] =
- "/var/log/metrics/kernel-crashes-daily";
- kernel_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
- kernel_crashes_daily_->Init(kKernelCrashesDailyRecordFile,
- &ReportKernelCrashesDaily,
- this,
- chromeos_metrics::kSecondsPerDay);
-
- static const char kUncleanShutdownsDailyRecordFile[] =
- "/var/log/metrics/unclean-shutdowns-daily";
- unclean_shutdowns_daily_.reset(new chromeos_metrics::FrequencyCounter());
- unclean_shutdowns_daily_->Init(kUncleanShutdownsDailyRecordFile,
- &ReportUncleanShutdownsDaily,
- this,
- chromeos_metrics::kSecondsPerDay);
-
- static const char kAnyCrashesUserCrashDailyRecordFile[] =
- "/var/log/metrics/any-crashes-daily";
- any_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
- any_crashes_daily_->Init(kAnyCrashesUserCrashDailyRecordFile,
- &ReportAnyCrashesDaily,
- this,
- chromeos_metrics::kSecondsPerDay);
+ DeleteFrequencyCounters();
+ ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
+ ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
+ ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
+ ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
+ ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
+ ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
+ ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
+ ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
+ ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
// Don't setup D-Bus and GLib in test mode.
if (testing)
@@ -453,6 +482,12 @@
user_crash_interval_->Update(0, seconds);
kernel_crash_interval_->Update(0, seconds);
+ // Flush finished cycles of all frequency counters.
+ for (FrequencyCounters::iterator i = frequency_counters_.begin();
+ i != frequency_counters_.end(); ++i) {
+ i->second->FlushFinishedCycles();
+ }
+
// Schedules a use monitor on inactive->active transitions and
// unschedules it on active->inactive transitions.
if (!user_active_ && active)
@@ -473,8 +508,10 @@
// Reports the active use time since the last crash and resets it.
user_crash_interval_->Flush();
- user_crashes_daily_->Update(1);
- any_crashes_daily_->Update(1);
+ frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
+ frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
+ frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
+ frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
}
void MetricsDaemon::ProcessKernelCrash() {
@@ -484,8 +521,10 @@
// Reports the active use time since the last crash and resets it.
kernel_crash_interval_->Flush();
- kernel_crashes_daily_->Update(1);
- any_crashes_daily_->Update(1);
+ frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
+ frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
+ frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
+ frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
}
void MetricsDaemon::ProcessUncleanShutdown() {
@@ -495,8 +534,10 @@
// Reports the active use time since the last crash and resets it.
unclean_shutdown_interval_->Flush();
- unclean_shutdowns_daily_->Update(1);
- any_crashes_daily_->Update(1);
+ frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
+ frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
+ frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
+ frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
}
bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) {
@@ -584,69 +625,6 @@
kMetricDailyUseTimeBuckets);
}
-// static
-void MetricsDaemon::ReportCrashInterval(const char* histogram_name,
- void* handle, int count) {
- MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
- daemon->SendMetric(histogram_name, count,
- kMetricCrashIntervalMin,
- kMetricCrashIntervalMax,
- kMetricCrashIntervalBuckets);
-}
-
-// static
-void MetricsDaemon::ReportUserCrashInterval(void* handle,
- int tag, int count) {
- ReportCrashInterval(kMetricUserCrashIntervalName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportKernelCrashInterval(void* handle,
- int tag, int count) {
- ReportCrashInterval(kMetricKernelCrashIntervalName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportUncleanShutdownInterval(void* handle,
- int tag, int count) {
- ReportCrashInterval(kMetricUncleanShutdownIntervalName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportCrashesDailyFrequency(const char* histogram_name,
- void* handle,
- int count) {
- MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
- daemon->SendMetric(histogram_name, count,
- kMetricCrashesDailyMin,
- kMetricCrashesDailyMax,
- kMetricCrashesDailyBuckets);
-}
-
-// static
-void MetricsDaemon::ReportUserCrashesDaily(void* handle,
- int tag, int count) {
- ReportCrashesDailyFrequency(kMetricUserCrashesDailyName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportKernelCrashesDaily(void* handle,
- int tag, int count) {
- ReportCrashesDailyFrequency(kMetricKernelCrashesDailyName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportUncleanShutdownsDaily(void* handle,
- int tag, int count) {
- ReportCrashesDailyFrequency(kMetricUncleanShutdownsDailyName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportAnyCrashesDaily(void* handle, int tag, int count) {
- ReportCrashesDailyFrequency(kMetricAnyCrashesDailyName, handle, count);
-}
-
-
void MetricsDaemon::SendMetric(const string& name, int sample,
int min, int max, int nbuckets) {
DLOG(INFO) << "received metric: " << name << " " << sample << " "
diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h
index f98c34c..d400bf0 100644
--- a/metrics/metrics_daemon.h
+++ b/metrics/metrics_daemon.h
@@ -7,7 +7,9 @@
#include <dbus/dbus.h>
#include <glib.h>
+#include <map>
+#include <base/file_path.h>
#include <base/scoped_ptr.h>
#include <base/time.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
@@ -16,7 +18,8 @@
namespace chromeos_metrics {
class FrequencyCounter;
-class TaggedCounterInterface;
+class TaggedCounter;
+class TaggedCounterReporter;
}
class MetricsDaemon {
@@ -35,6 +38,10 @@
private:
friend class MetricsDaemonTest;
FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash);
+ FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent);
+ FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast);
+ FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
+ FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
FRIEND_TEST(MetricsDaemonTest, LookupNetworkState);
FRIEND_TEST(MetricsDaemonTest, LookupPowerState);
FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState);
@@ -89,11 +96,15 @@
int seconds_;
};
+ typedef std::map<std::string, chromeos_metrics::FrequencyCounter*>
+ FrequencyCounters;
+
// Metric parameters.
static const char kMetricAnyCrashesDailyName[];
- static const char kMetricCrashesDailyBuckets;
- static const char kMetricCrashesDailyMax;
- static const char kMetricCrashesDailyMin;
+ static const char kMetricAnyCrashesWeeklyName[];
+ static const char kMetricCrashFrequencyBuckets;
+ static const char kMetricCrashFrequencyMax;
+ static const char kMetricCrashFrequencyMin;
static const int kMetricCrashIntervalBuckets;
static const int kMetricCrashIntervalMax;
static const int kMetricCrashIntervalMin;
@@ -102,14 +113,18 @@
static const int kMetricDailyUseTimeMin;
static const char kMetricDailyUseTimeName[];
static const char kMetricKernelCrashesDailyName[];
+ static const char kMetricKernelCrashesWeeklyName[];
static const char kMetricKernelCrashIntervalName[];
+ static const char kMetricsPath[];
static const int kMetricTimeToNetworkDropBuckets;
static const int kMetricTimeToNetworkDropMax;
static const int kMetricTimeToNetworkDropMin;
static const char kMetricTimeToNetworkDropName[];
static const char kMetricUncleanShutdownIntervalName[];
static const char kMetricUncleanShutdownsDailyName[];
+ static const char kMetricUncleanShutdownsWeeklyName[];
static const char kMetricUserCrashesDailyName[];
+ static const char kMetricUserCrashesWeeklyName[];
static const char kMetricUserCrashIntervalName[];
// D-Bus message match strings.
@@ -124,6 +139,20 @@
// Array of user session states.
static const char* kSessionStates_[kNumberSessionStates];
+ // Clears and deletes the data contained in frequency_counters_.
+ void DeleteFrequencyCounters();
+
+ // Configures the given crash interval reporter.
+ void ConfigureCrashIntervalReporter(
+ const char* histogram_name,
+ scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter);
+
+ // Configures the given frequency counter reporter.
+ void ConfigureCrashFrequencyReporter(const char* histogram_name);
+
+ // Returns file path to persistent file for generating given histogram.
+ FilePath GetHistogramPath(const char* histogram_name);
+
// Creates the event loop and enters it.
void Loop();
@@ -203,48 +232,15 @@
// Unschedules a scheduled use monitor, if any.
void UnscheduleUseMonitor();
+ // Report daily use through UMA.
+ static void ReportDailyUse(void* handle, int tag, int count);
+
// Sends a regular (exponential) histogram sample to Chrome for
// transport to UMA. See MetricsLibrary::SendToUMA in
// metrics_library.h for a description of the arguments.
void SendMetric(const std::string& name, int sample,
int min, int max, int nbuckets);
- // TaggedCounter callback to process aggregated daily usage data and
- // send to UMA.
- static void ReportDailyUse(void* data, int tag, int count);
-
- // Helper to report a crash interval to UMA.
- static void ReportCrashInterval(const char* histogram_name,
- void* handle, int count);
-
- // TaggedCounter callback to process time between user-space process
- // crashes and send to UMA.
- static void ReportUserCrashInterval(void* data, int tag, int count);
-
- // TaggedCounter callback to process time between kernel crashes and
- // send to UMA.
- static void ReportKernelCrashInterval(void* data, int tag, int count);
-
- // TaggedCounter callback to process time between unclean shutdowns and
- // send to UMA.
- static void ReportUncleanShutdownInterval(void* data, int tag, int count);
-
- // Helper to report a daily crash frequency to UMA.
- static void ReportCrashesDailyFrequency(const char* histogram_name,
- void* handle, int count);
-
- // TaggedCounter callback to report daily crash frequency to UMA.
- static void ReportUserCrashesDaily(void* handle, int tag, int count);
-
- // TaggedCounter callback to report kernel crash frequency to UMA.
- static void ReportKernelCrashesDaily(void* handle, int tag, int count);
-
- // TaggedCounter callback to report unclean shutdown frequency to UMA.
- static void ReportUncleanShutdownsDaily(void* handle, int tag, int count);
-
- // TaggedCounter callback to report frequency of any crashes to UMA.
- static void ReportAnyCrashesDaily(void* handle, int tag, int count);
-
// Test mode.
bool testing_;
@@ -275,30 +271,20 @@
base::Time user_active_last_;
// Daily active use time in seconds.
- scoped_ptr<chromeos_metrics::TaggedCounterInterface> daily_use_;
+ scoped_ptr<chromeos_metrics::TaggedCounter> daily_use_;
// Active use time between user-space process crashes.
- scoped_ptr<chromeos_metrics::TaggedCounterInterface> user_crash_interval_;
+ scoped_ptr<chromeos_metrics::TaggedCounterReporter> user_crash_interval_;
// Active use time between kernel crashes.
- scoped_ptr<chromeos_metrics::TaggedCounterInterface> kernel_crash_interval_;
+ scoped_ptr<chromeos_metrics::TaggedCounterReporter> kernel_crash_interval_;
// Active use time between unclean shutdowns crashes.
- scoped_ptr<chromeos_metrics::TaggedCounterInterface>
+ scoped_ptr<chromeos_metrics::TaggedCounterReporter>
unclean_shutdown_interval_;
- // Daily count of user-space process crashes.
- scoped_ptr<chromeos_metrics::FrequencyCounter> user_crashes_daily_;
-
- // Daily count of kernel crashes.
- scoped_ptr<chromeos_metrics::FrequencyCounter> kernel_crashes_daily_;
-
- // Daily count of unclean shutdowns.
- scoped_ptr<chromeos_metrics::FrequencyCounter> unclean_shutdowns_daily_;
-
- // Daily count of any crashes (user-space processes, kernel, or
- // unclean shutdowns).
- scoped_ptr<chromeos_metrics::FrequencyCounter> any_crashes_daily_;
+ // Map of all frequency counters, to simplify flushing them.
+ FrequencyCounters frequency_counters_;
// Sleep period until the next daily usage aggregation performed by
// the daily use monitor (see ScheduleUseMonitor).
diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc
index e551cf9..9ba63e8 100644
--- a/metrics/metrics_daemon_test.cc
+++ b/metrics/metrics_daemon_test.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <utime.h>
+
#include <base/file_util.h>
#include <gtest/gtest.h>
@@ -11,14 +13,21 @@
using base::Time;
using base::TimeTicks;
+using chromeos_metrics::FrequencyCounter;
using chromeos_metrics::FrequencyCounterMock;
using chromeos_metrics::TaggedCounterMock;
+using chromeos_metrics::TaggedCounterReporter;
+using chromeos_metrics::TaggedCounterReporterMock;
using ::testing::_;
using ::testing::Return;
using ::testing::StrictMock;
static const int kSecondsPerDay = 24 * 60 * 60;
+static const char kTestDir[] = "test";
+static const char kLastFile[] = "test/last";
+static const char kCurrentFile[] = "test/current";
+
// This class allows a TimeTicks object to be initialized with seconds
// (rather than microseconds) through the protected TimeTicks(int64)
// constructor.
@@ -48,6 +57,38 @@
EXPECT_EQ(NULL, daemon_.user_crash_interval_.get());
daemon_.Init(true, &metrics_lib_);
+ // Check configuration of a few histograms.
+ FrequencyCounter* frequency_counter =
+ daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesDailyName];
+ const TaggedCounterReporter* reporter = GetReporter(frequency_counter);
+ EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesDailyName,
+ reporter->histogram_name());
+ EXPECT_EQ(chromeos_metrics::kSecondsPerDay,
+ frequency_counter->cycle_duration());
+ EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMin, reporter->min());
+ EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMax, reporter->max());
+ EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyBuckets, reporter->buckets());
+
+ frequency_counter =
+ daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesWeeklyName];
+ reporter = GetReporter(frequency_counter);
+ EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesWeeklyName,
+ reporter->histogram_name());
+ EXPECT_EQ(chromeos_metrics::kSecondsPerWeek,
+ frequency_counter->cycle_duration());
+
+ EXPECT_EQ(MetricsDaemon::kMetricKernelCrashIntervalName,
+ daemon_.kernel_crash_interval_->histogram_name());
+ EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMin,
+ daemon_.kernel_crash_interval_->min());
+ EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMax,
+ daemon_.kernel_crash_interval_->max());
+ EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalBuckets,
+ daemon_.kernel_crash_interval_->buckets());
+
+ EXPECT_EQ(MetricsDaemon::kMetricUncleanShutdownIntervalName,
+ daemon_.unclean_shutdown_interval_->histogram_name());
+
// Tests constructor initialization. Switches to mock counters.
EXPECT_TRUE(NULL != daemon_.daily_use_.get());
EXPECT_TRUE(NULL != daemon_.kernel_crash_interval_.get());
@@ -56,20 +97,20 @@
// Allocates mock counter and transfers ownership.
daily_use_ = new StrictMock<TaggedCounterMock>();
daemon_.daily_use_.reset(daily_use_);
- kernel_crash_interval_ = new StrictMock<TaggedCounterMock>();
+ kernel_crash_interval_ = new StrictMock<TaggedCounterReporterMock>();
daemon_.kernel_crash_interval_.reset(kernel_crash_interval_);
- user_crash_interval_ = new StrictMock<TaggedCounterMock>();
+ user_crash_interval_ = new StrictMock<TaggedCounterReporterMock>();
daemon_.user_crash_interval_.reset(user_crash_interval_);
- unclean_shutdown_interval_ = new StrictMock<TaggedCounterMock>();
+ unclean_shutdown_interval_ = new StrictMock<TaggedCounterReporterMock>();
daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_);
- kernel_crashes_daily_ = new StrictMock<FrequencyCounterMock>();
- daemon_.kernel_crashes_daily_.reset(kernel_crashes_daily_);
- user_crashes_daily_ = new StrictMock<FrequencyCounterMock>();
- daemon_.user_crashes_daily_.reset(user_crashes_daily_);
- unclean_shutdowns_daily_ = new StrictMock<FrequencyCounterMock>();
- daemon_.unclean_shutdowns_daily_.reset(unclean_shutdowns_daily_);
- any_crashes_daily_ = new StrictMock<FrequencyCounterMock>();
- daemon_.any_crashes_daily_.reset(any_crashes_daily_);
+
+ // Reset all frequency counter reporters to mocks for further testing.
+ MetricsDaemon::FrequencyCounters::iterator i;
+ for (i = daemon_.frequency_counters_.begin();
+ i != daemon_.frequency_counters_.end(); ++i) {
+ delete i->second;
+ i->second = new StrictMock<FrequencyCounterMock>();
+ }
EXPECT_FALSE(daemon_.user_active_);
EXPECT_TRUE(daemon_.user_active_last_.is_null());
@@ -77,10 +118,29 @@
EXPECT_TRUE(daemon_.network_state_last_.is_null());
EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_);
EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_);
+
+ file_util::Delete(FilePath(kTestDir), true);
+ file_util::CreateDirectory(FilePath(kTestDir));
}
virtual void TearDown() {}
+ const TaggedCounterReporter*
+ GetReporter(FrequencyCounter* frequency_counter) const {
+ return static_cast<const TaggedCounterReporter*>(
+ &frequency_counter->tagged_counter());
+ }
+
+ void ExpectFrequencyFlushCalls() {
+ MetricsDaemon::FrequencyCounters::iterator i;
+ for (i = daemon_.frequency_counters_.begin();
+ i != daemon_.frequency_counters_.end(); ++i) {
+ FrequencyCounterMock* mock =
+ static_cast<FrequencyCounterMock*>(i->second);
+ EXPECT_CALL(*mock, FlushFinishedCycles());
+ }
+ }
+
// Adds active use aggregation counters update expectations that the
// specified tag/count update will be generated.
void ExpectActiveUseUpdate(int daily_tag, int count) {
@@ -93,6 +153,7 @@
EXPECT_CALL(*user_crash_interval_, Update(0, count))
.Times(1)
.RetiresOnSaturation();
+ ExpectFrequencyFlushCalls();
}
// Adds active use aggregation counters update expectations that
@@ -107,6 +168,7 @@
EXPECT_CALL(*user_crash_interval_, Update(_, _))
.Times(1)
.RetiresOnSaturation();
+ ExpectFrequencyFlushCalls();
}
// Adds a metrics library mock expectation that the specified metric
@@ -170,6 +232,12 @@
dbus_message_unref(msg);
}
+ // Get the frequency counter for the given name.
+ FrequencyCounterMock& GetFrequencyMock(const char* histogram_name) {
+ return *static_cast<FrequencyCounterMock*>(
+ daemon_.frequency_counters_[histogram_name]);
+ }
+
// The MetricsDaemon under test.
MetricsDaemon daemon_;
@@ -181,14 +249,9 @@
// update calls are marked as failures. They are pointers so that
// they can replace the scoped_ptr's allocated by the daemon.
StrictMock<TaggedCounterMock>* daily_use_;
- StrictMock<TaggedCounterMock>* kernel_crash_interval_;
- StrictMock<TaggedCounterMock>* user_crash_interval_;
- StrictMock<TaggedCounterMock>* unclean_shutdown_interval_;
-
- StrictMock<FrequencyCounterMock>* kernel_crashes_daily_;
- StrictMock<FrequencyCounterMock>* user_crashes_daily_;
- StrictMock<FrequencyCounterMock>* unclean_shutdowns_daily_;
- StrictMock<FrequencyCounterMock>* any_crashes_daily_;
+ StrictMock<TaggedCounterReporterMock>* kernel_crash_interval_;
+ StrictMock<TaggedCounterReporterMock>* user_crash_interval_;
+ StrictMock<TaggedCounterReporterMock>* unclean_shutdown_interval_;
};
TEST_F(MetricsDaemonTest, CheckSystemCrash) {
@@ -217,22 +280,6 @@
MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5);
}
-TEST_F(MetricsDaemonTest, ReportKernelCrashInterval) {
- ExpectMetric(MetricsDaemon::kMetricKernelCrashIntervalName, 50,
- MetricsDaemon::kMetricCrashIntervalMin,
- MetricsDaemon::kMetricCrashIntervalMax,
- MetricsDaemon::kMetricCrashIntervalBuckets);
- MetricsDaemon::ReportKernelCrashInterval(&daemon_, 0, 50);
-}
-
-TEST_F(MetricsDaemonTest, ReportUncleanShutdownInterval) {
- ExpectMetric(MetricsDaemon::kMetricUncleanShutdownIntervalName, 50,
- MetricsDaemon::kMetricCrashIntervalMin,
- MetricsDaemon::kMetricCrashIntervalMax,
- MetricsDaemon::kMetricCrashIntervalBuckets);
- MetricsDaemon::ReportUncleanShutdownInterval(&daemon_, 0, 50);
-}
-
TEST_F(MetricsDaemonTest, LookupNetworkState) {
EXPECT_EQ(MetricsDaemon::kNetworkStateOnline,
daemon_.LookupNetworkState("online"));
@@ -268,15 +315,25 @@
DeleteDBusMessage(msg);
IgnoreActiveUseUpdate();
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+ Update(1))
+ .Times(1)
+ .RetiresOnSaturation();
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+ Update(1))
+ .Times(1)
+ .RetiresOnSaturation();
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName),
+ Update(1))
+ .Times(1)
+ .RetiresOnSaturation();
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName),
+ Update(1))
+ .Times(1)
+ .RetiresOnSaturation();
EXPECT_CALL(*user_crash_interval_, Flush())
.Times(1)
.RetiresOnSaturation();
- EXPECT_CALL(*user_crashes_daily_, Update(1))
- .Times(1)
- .RetiresOnSaturation();
- EXPECT_CALL(*any_crashes_daily_, Update(1))
- .Times(1)
- .RetiresOnSaturation();
msg = NewDBusSignalString("/",
"org.chromium.CrashReporter",
"UserCrash",
@@ -410,8 +467,14 @@
EXPECT_CALL(*kernel_crash_interval_, Flush())
.Times(1)
.RetiresOnSaturation();
- EXPECT_CALL(*kernel_crashes_daily_, Update(1));
- EXPECT_CALL(*any_crashes_daily_, Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesDailyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesWeeklyName),
+ Update(1));
daemon_.ProcessKernelCrash();
}
@@ -420,8 +483,15 @@
EXPECT_CALL(*unclean_shutdown_interval_, Flush())
.Times(1)
.RetiresOnSaturation();
- EXPECT_CALL(*unclean_shutdowns_daily_, Update(1));
- EXPECT_CALL(*any_crashes_daily_, Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsDailyName),
+ Update(1));
+ EXPECT_CALL(
+ GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsWeeklyName),
+ Update(1));
daemon_.ProcessUncleanShutdown();
}
@@ -430,8 +500,14 @@
EXPECT_CALL(*user_crash_interval_, Flush())
.Times(1)
.RetiresOnSaturation();
- EXPECT_CALL(*user_crashes_daily_, Update(1));
- EXPECT_CALL(*any_crashes_daily_, Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName),
+ Update(1));
+ EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName),
+ Update(1));
daemon_.ProcessUserCrash();
}
@@ -514,20 +590,10 @@
EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_);
}
-TEST_F(MetricsDaemonTest, ReportUserCrashInterval) {
- ExpectMetric(MetricsDaemon::kMetricUserCrashIntervalName, 50,
- MetricsDaemon::kMetricCrashIntervalMin,
- MetricsDaemon::kMetricCrashIntervalMax,
- MetricsDaemon::kMetricCrashIntervalBuckets);
- MetricsDaemon::ReportUserCrashInterval(&daemon_, 0, 50);
-}
-
-TEST_F(MetricsDaemonTest, ReportCrashesDailyFrequency) {
- ExpectMetric("foobar", 50,
- MetricsDaemon::kMetricCrashesDailyMin,
- MetricsDaemon::kMetricCrashesDailyMax,
- MetricsDaemon::kMetricCrashesDailyBuckets);
- MetricsDaemon::ReportCrashesDailyFrequency("foobar", &daemon_, 50);
+TEST_F(MetricsDaemonTest, GetHistogramPath) {
+ EXPECT_EQ("/var/log/metrics/Logging.AnyCrashesDaily",
+ daemon_.GetHistogramPath(
+ MetricsDaemon::kMetricAnyCrashesDailyName).value());
}
int main(int argc, char** argv) {
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
index e4087ef..3e3987d 100644
--- a/metrics/metrics_library.cc
+++ b/metrics/metrics_library.cc
@@ -6,6 +6,7 @@
#include <errno.h>
#include <sys/file.h>
+#include <sys/stat.h>
#include <cstdarg>
#include <cstdio>
@@ -14,12 +15,14 @@
#define READ_WRITE_ALL_FILE_FLAGS \
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
-static const char kAutotestPath[] =
- "/var/log/metrics/autotest-events";
-static const char kUMAEventsPath[] =
- "/var/log/metrics/uma-events";
+static const char kAutotestPath[] = "/var/log/metrics/autotest-events";
+static const char kUMAEventsPath[] = "/var/log/metrics/uma-events";
+static const char kConsentFile[] = "/home/chronos/Consent To Send Stats";
static const int32_t kBufferSize = 1024;
+time_t MetricsLibrary::cached_enabled_time_ = 0;
+bool MetricsLibrary::cached_enabled_ = false;
+
using std::string;
// TODO(sosa@chromium.org) - use Chromium logger instead of stderr
@@ -38,9 +41,24 @@
}
MetricsLibrary::MetricsLibrary()
- : uma_events_file_(NULL) {}
+ : uma_events_file_(NULL),
+ consent_file_(kConsentFile) {}
+
+bool MetricsLibrary::AreMetricsEnabled() {
+ static struct stat stat_buffer;
+ time_t this_check_time = time(NULL);
+
+ if (this_check_time != cached_enabled_time_) {
+ cached_enabled_time_ = this_check_time;
+ cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0);
+ }
+ return cached_enabled_;
+}
bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) {
+ if (!AreMetricsEnabled())
+ return true;
+
int chrome_fd = open(uma_events_file_,
O_WRONLY | O_APPEND | O_CREAT,
READ_WRITE_ALL_FILE_FLAGS);
diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h
index 76fa451..52da94d 100644
--- a/metrics/metrics_library.h
+++ b/metrics/metrics_library.h
@@ -27,6 +27,9 @@
// Initializes the library.
void Init();
+ // Returns whether or not metrics collection is enabled.
+ bool AreMetricsEnabled();
+
// Sends histogram data to Chrome for transport to UMA and returns
// true on success. This method results in the equivalent of an
// asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS
@@ -69,6 +72,7 @@
private:
friend class CMetricsLibraryTest;
friend class MetricsLibraryTest;
+ FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
@@ -89,7 +93,14 @@
int32_t FormatChromeMessage(int32_t buffer_size, char* buffer,
const char* format, ...);
+ // Time at which we last checked if metrics were enabled.
+ static time_t cached_enabled_time_;
+
+ // Cached state of whether or not metrics were enabled.
+ static bool cached_enabled_;
+
const char* uma_events_file_;
+ const char* consent_file_;
};
#endif // METRICS_LIBRARY_H_
diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc
index 596abd3..cbf4cce 100644
--- a/metrics/metrics_library_test.cc
+++ b/metrics/metrics_library_test.cc
@@ -12,6 +12,15 @@
static const FilePath kTestUMAEventsFile("test-uma-events");
+static const char kTestConsent[] = "test-consent";
+
+static void SetMetricsEnabled(bool enabled) {
+ if (enabled)
+ ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1));
+ else
+ file_util::Delete(FilePath(kTestConsent), false);
+}
+
class MetricsLibraryTest : public testing::Test {
protected:
virtual void SetUp() {
@@ -19,15 +28,63 @@
lib_.Init();
EXPECT_TRUE(NULL != lib_.uma_events_file_);
lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str();
+ SetMetricsEnabled(true);
+ // Defeat metrics enabled caching between tests.
+ lib_.cached_enabled_time_ = 0;
+ lib_.consent_file_ = kTestConsent;
}
virtual void TearDown() {
file_util::Delete(kTestUMAEventsFile, false);
}
+ void VerifyEnabledCacheHit(bool to_value);
+ void VerifyEnabledCacheEviction(bool to_value);
+
MetricsLibrary lib_;
};
+TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
+ SetMetricsEnabled(false);
+ EXPECT_FALSE(lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+ EXPECT_TRUE(lib_.AreMetricsEnabled());
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) {
+ // We might step from one second to the next one time, but not 100
+ // times in a row.
+ for (int i = 0; i < 100; ++i) {
+ lib_.cached_enabled_time_ = 0;
+ SetMetricsEnabled(!to_value);
+ ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+ SetMetricsEnabled(to_value);
+ if (lib_.AreMetricsEnabled() == !to_value)
+ return;
+ }
+ ADD_FAILURE() << "Did not see evidence of caching";
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
+ lib_.cached_enabled_time_ = 0;
+ SetMetricsEnabled(!to_value);
+ ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+ SetMetricsEnabled(to_value);
+ ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5);
+ // Sleep one second (or cheat to be faster).
+ --lib_.cached_enabled_time_;
+ ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) {
+ VerifyEnabledCacheHit(false);
+ VerifyEnabledCacheHit(true);
+ VerifyEnabledCacheEviction(false);
+ VerifyEnabledCacheEviction(true);
+}
+
TEST_F(MetricsLibraryTest, FormatChromeMessage) {
char buf[7];
const int kLen = 6;
@@ -55,6 +112,12 @@
EXPECT_EQ(0, memcmp(exp, buf, kLen));
}
+TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) {
+ SetMetricsEnabled(false);
+ EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3));
+ EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile));
+}
+
TEST_F(MetricsLibraryTest, SendMessageToChrome) {
EXPECT_TRUE(lib_.SendMessageToChrome(4, "test"));
EXPECT_TRUE(lib_.SendMessageToChrome(7, "content"));
@@ -84,6 +147,12 @@
EXPECT_EQ(0, memcmp(exp, buf, kLen));
}
+TEST_F(MetricsLibraryTest, SendToUMANotEnabled) {
+ SetMetricsEnabled(false);
+ EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50));
+ EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile));
+}
+
class CMetricsLibraryTest : public testing::Test {
protected:
virtual void SetUp() {
@@ -93,6 +162,9 @@
CMetricsLibraryInit(lib_);
EXPECT_TRUE(NULL != ml.uma_events_file_);
ml.uma_events_file_ = kTestUMAEventsFile.value().c_str();
+ SetMetricsEnabled(true);
+ reinterpret_cast<MetricsLibrary*>(lib_)->cached_enabled_time_ = 0;
+ reinterpret_cast<MetricsLibrary*>(lib_)->consent_file_ = kTestConsent;
}
virtual void TearDown() {
@@ -103,6 +175,15 @@
CMetricsLibrary lib_;
};
+TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) {
+ SetMetricsEnabled(false);
+ EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_));
+}
+
+TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) {
+ EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_));
+}
+
TEST_F(CMetricsLibraryTest, SendEnumToUMA) {
char buf[100];
const int kLen = 40;