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
This commit is contained in:
Ken Mixter 2010-08-26 18:35:06 -07:00
parent ccd84c03d2
commit 4c5daa4794
12 changed files with 663 additions and 306 deletions

View File

@ -26,8 +26,8 @@ extern "C" void CMetricsLibraryInit(CMetricsLibrary handle) {
}
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 CMetricsLibrarySendToUMA(CMetricsLibrary handle,
}
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();
}

View File

@ -21,13 +21,17 @@ void CMetricsLibraryInit(CMetricsLibrary handle);
// 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_

View File

@ -8,6 +8,7 @@
#include <base/eintr_wrapper.h>
#include <base/logging.h>
#include "metrics_library.h"
namespace chromeos_metrics {
@ -31,8 +32,7 @@ void TaggedCounter::Record::Add(int32 count) {
// TaggedCounter implementation.
TaggedCounter::TaggedCounter()
: filename_(NULL),
reporter_(NULL),
: reporter_(NULL),
reporter_handle_(NULL),
record_state_(kRecordInvalid) {}
@ -72,12 +72,13 @@ void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) {
}
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 @@ void TaggedCounter::WriteRecord(int fd) {
}
}
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;
}

View File

@ -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 @@ class TaggedCounterInterface {
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 TaggedCounterInterface {
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 @@ class TaggedCounter : public TaggedCounterInterface {
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 @@ class TaggedCounter : public TaggedCounterInterface {
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 @@ class FrequencyCounter {
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 @@ class FrequencyCounter {
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);

View File

@ -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 @@ class TaggedCounterMock : public TaggedCounterInterface {
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 @@ class FrequencyCounterMock : public FrequencyCounter {
void* reporter_handle,
time_t cycle_duration));
MOCK_METHOD1(Update, void(int32 count));
MOCK_METHOD0(FlushFinishedCycles, void());
};
} // namespace chromeos_metrics

View File

@ -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 RecordTest : public testing::Test {
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 @@ TEST_F(TaggedCounterTest, Update) {
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 @@ class FrequencyCounterTest : public testing::Test {
};
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 @@ TEST_F(FrequencyCounterTest, Init) {
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, GetCycleNumberForDay) {
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);
}

View File

@ -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 @@ const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
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 @@ const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
// 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 @@ MetricsDaemon::MetricsDaemon()
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 @@ void MetricsDaemon::Run(bool run_as_daemon) {
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 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) {
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 @@ void MetricsDaemon::ProcessUserCrash() {
// 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 @@ void MetricsDaemon::ProcessKernelCrash() {
// 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 @@ void MetricsDaemon::ProcessUncleanShutdown() {
// 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 @@ void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
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 << " "

View File

@ -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 @@ class MetricsDaemon {
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 @@ class MetricsDaemon {
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 @@ class MetricsDaemon {
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 @@ class MetricsDaemon {
// 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 @@ class MetricsDaemon {
// 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 @@ class MetricsDaemon {
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).

View File

@ -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 @@ class MetricsDaemonTest : public testing::Test {
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 @@ class MetricsDaemonTest : public testing::Test {
// 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 @@ class MetricsDaemonTest : public testing::Test {
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 @@ class MetricsDaemonTest : public testing::Test {
EXPECT_CALL(*user_crash_interval_, Update(0, count))
.Times(1)
.RetiresOnSaturation();
ExpectFrequencyFlushCalls();
}
// Adds active use aggregation counters update expectations that
@ -107,6 +168,7 @@ class MetricsDaemonTest : public testing::Test {
EXPECT_CALL(*user_crash_interval_, Update(_, _))
.Times(1)
.RetiresOnSaturation();
ExpectFrequencyFlushCalls();
}
// Adds a metrics library mock expectation that the specified metric
@ -170,6 +232,12 @@ class MetricsDaemonTest : public testing::Test {
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 @@ class MetricsDaemonTest : public testing::Test {
// 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 @@ TEST_F(MetricsDaemonTest, ReportDailyUse) {
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 @@ TEST_F(MetricsDaemonTest, MessageFilter) {
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 @@ TEST_F(MetricsDaemonTest, ProcessKernelCrash) {
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 @@ TEST_F(MetricsDaemonTest, ProcessUncleanShutdown) {
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 @@ TEST_F(MetricsDaemonTest, ProcessUserCrash) {
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 @@ TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) {
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) {

View File

@ -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 @@ static void PrintError(const char* message, const char* file,
}
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);

View File

@ -27,6 +27,9 @@ class MetricsLibrary : public MetricsLibraryInterface {
// 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 @@ class MetricsLibrary : public MetricsLibraryInterface {
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 @@ class MetricsLibrary : public MetricsLibraryInterface {
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_

View File

@ -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 @@ class MetricsLibraryTest : public testing::Test {
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 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) {
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 @@ TEST_F(MetricsLibraryTest, SendToUMA) {
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 @@ class CMetricsLibraryTest : public testing::Test {
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 @@ class CMetricsLibraryTest : public testing::Test {
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;