metrics: add per-version cumulative counters
This adds counters that accumulate some measurement across an OS version; they are reset at version updates, but reported more frequently (for instance, daily). Such counts could be obtained by pseudonymous dremel queries, but this is more convenient. The code replaces the "tag" datum in the counters with two tags: a "report tag" and a "reset tag". When the report tag changes, the count is reported but not reset. When the reset tag changes, the count is both reported and reset. This also adds one usage of the new counter which tracks the total number of kernel crashes since the most recent OS version update. The state machine in counter.cc changes a bit because it's no longer true that a counter is reset after reporting it. That logic is still rather confusing, and could use a rewrite. BUG=chromium:339588 TEST=ran on target under various situations BRANCH=none Change-Id: I5f83731e1a3d6e055b6d0f89111c9ffc60ccfcb9 Reviewed-on: https://chromium-review.googlesource.com/185081 Reviewed-by: Daniel Erat <derat@chromium.org> Commit-Queue: Luigi Semenzato <semenzato@chromium.org> Tested-by: Luigi Semenzato <semenzato@chromium.org>
This commit is contained in:
parent
c5a92347a5
commit
859b3f0ad3
|
@ -14,8 +14,11 @@
|
|||
namespace chromeos_metrics {
|
||||
|
||||
// TaggedCounter::Record implementation.
|
||||
void TaggedCounter::Record::Init(int32 tag, int32 count) {
|
||||
tag_ = tag;
|
||||
void TaggedCounter::Record::Init(uint32 report_tag,
|
||||
uint32 reset_tag,
|
||||
int32 count) {
|
||||
report_tag_ = report_tag;
|
||||
reset_tag_ = reset_tag;
|
||||
count_ = (count > 0) ? count : 0;
|
||||
}
|
||||
|
||||
|
@ -48,19 +51,24 @@ void TaggedCounter::Init(const char* filename,
|
|||
record_state_ = kRecordInvalid;
|
||||
}
|
||||
|
||||
void TaggedCounter::Update(int32 tag, int32 count) {
|
||||
UpdateInternal(tag,
|
||||
void TaggedCounter::Update(uint32 report_tag, uint32 reset_tag, int32 count) {
|
||||
UpdateInternal(report_tag,
|
||||
reset_tag,
|
||||
count,
|
||||
false); // No flush.
|
||||
}
|
||||
|
||||
void TaggedCounter::Flush() {
|
||||
UpdateInternal(0, // tag
|
||||
UpdateInternal(0, // report_tag
|
||||
0, // reset_tag
|
||||
0, // count
|
||||
true); // Do flush.
|
||||
}
|
||||
|
||||
void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) {
|
||||
void TaggedCounter::UpdateInternal(uint32 report_tag,
|
||||
uint32 reset_tag,
|
||||
int32 count,
|
||||
bool flush) {
|
||||
if (flush) {
|
||||
// Flushing but record is null, so nothing to do.
|
||||
if (record_state_ == kRecordNull)
|
||||
|
@ -68,11 +76,15 @@ void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) {
|
|||
} else {
|
||||
// If there's no new data and the last record in the aggregation
|
||||
// file is with the same tag, there's nothing to do.
|
||||
if (count <= 0 && record_state_ == kRecordValid && record_.tag() == tag)
|
||||
if (count <= 0 &&
|
||||
record_state_ == kRecordValid &&
|
||||
record_.report_tag() == report_tag &&
|
||||
record_.reset_tag() == reset_tag)
|
||||
return;
|
||||
}
|
||||
|
||||
DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush;
|
||||
DLOG(INFO) << "report_tag: " << report_tag << " reset_tag: " << reset_tag
|
||||
<< " count: " << count << " flush: " << flush;
|
||||
DCHECK(!filename_.empty());
|
||||
|
||||
// NOTE: The assumption is that this TaggedCounter object is the
|
||||
|
@ -84,10 +96,9 @@ void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) {
|
|||
PLOG(WARNING) << "Unable to open the persistent counter file";
|
||||
return;
|
||||
}
|
||||
|
||||
ReadRecord(fd);
|
||||
ReportRecord(tag, flush);
|
||||
UpdateRecord(tag, count, flush);
|
||||
ReportRecord(report_tag, reset_tag, flush);
|
||||
UpdateRecord(report_tag, reset_tag, count, flush);
|
||||
WriteRecord(fd);
|
||||
|
||||
HANDLE_EINTR(close(fd));
|
||||
|
@ -97,6 +108,9 @@ void TaggedCounter::ReadRecord(int fd) {
|
|||
if (record_state_ != kRecordInvalid)
|
||||
return;
|
||||
|
||||
// Three cases: 1. empty file (first time), 2. size of file == size of record
|
||||
// (normal case), 3. size of file != size of record (new version). We treat
|
||||
// cases 1 and 3 identically.
|
||||
if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) {
|
||||
if (record_.count() >= 0) {
|
||||
record_state_ = kRecordValid;
|
||||
|
@ -111,42 +125,59 @@ void TaggedCounter::ReadRecord(int fd) {
|
|||
record_state_ = kRecordNull;
|
||||
}
|
||||
|
||||
void TaggedCounter::ReportRecord(int32 tag, bool flush) {
|
||||
void TaggedCounter::ReportRecord(uint32 report_tag,
|
||||
uint32 reset_tag,
|
||||
bool flush) {
|
||||
// If no valid record, there's nothing to report.
|
||||
if (record_state_ != kRecordValid) {
|
||||
DCHECK_EQ(record_state_, kRecordNull);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the current record has the same tag as the new tag, it's not
|
||||
// ready to be reported yet.
|
||||
if (!flush && record_.tag() == tag)
|
||||
// If the current record has the same tags as the new ones, it's
|
||||
// not ready to be reported yet.
|
||||
if (!flush &&
|
||||
record_.report_tag() == report_tag &&
|
||||
record_.reset_tag() == reset_tag)
|
||||
return;
|
||||
|
||||
if (reporter_) {
|
||||
reporter_(reporter_handle_, record_.tag(), record_.count());
|
||||
reporter_(reporter_handle_, record_.count());
|
||||
}
|
||||
// If the report tag has changed, update it to the current one.
|
||||
if (record_.report_tag() != report_tag) {
|
||||
record_.set_report_tag(report_tag);
|
||||
record_state_ = kRecordValidDirty;
|
||||
}
|
||||
// If the reset tag has changed, the new state is NullDirty, no
|
||||
// matter whether the report tag has changed or not.
|
||||
if (record_.reset_tag() != reset_tag) {
|
||||
record_state_ = kRecordNullDirty;
|
||||
}
|
||||
record_state_ = kRecordNullDirty;
|
||||
}
|
||||
|
||||
void TaggedCounter::UpdateRecord(int32 tag, int32 count, bool flush) {
|
||||
if (flush) {
|
||||
DCHECK(record_state_ == kRecordNull || record_state_ == kRecordNullDirty);
|
||||
void TaggedCounter::UpdateRecord(uint32 report_tag,
|
||||
uint32 reset_tag,
|
||||
int32 count,
|
||||
bool flush) {
|
||||
if (flush &&
|
||||
(record_state_ == kRecordNull || record_state_ == kRecordNullDirty))
|
||||
return;
|
||||
}
|
||||
|
||||
switch (record_state_) {
|
||||
case kRecordNull:
|
||||
case kRecordNullDirty:
|
||||
// Current record is null, starting a new record.
|
||||
record_.Init(tag, count);
|
||||
record_.Init(report_tag, reset_tag, count);
|
||||
record_state_ = kRecordValidDirty;
|
||||
break;
|
||||
|
||||
case kRecordValid:
|
||||
case kRecordValidDirty:
|
||||
// If there's an existing record for the current tag,
|
||||
// accumulates the counts.
|
||||
DCHECK_EQ(record_.tag(), tag);
|
||||
DCHECK_EQ(record_.report_tag(), report_tag);
|
||||
DCHECK_EQ(record_.reset_tag(), reset_tag);
|
||||
if (count > 0) {
|
||||
record_.Add(count);
|
||||
record_state_ = kRecordValidDirty;
|
||||
|
@ -212,7 +243,7 @@ void TaggedCounterReporter::Init(const char* filename,
|
|||
CHECK(buckets_ > 0);
|
||||
}
|
||||
|
||||
void TaggedCounterReporter::Report(void* handle, int32 tag, int32 count) {
|
||||
void TaggedCounterReporter::Report(void* handle, int32 count) {
|
||||
TaggedCounterReporter* this_reporter =
|
||||
reinterpret_cast<TaggedCounterReporter*>(handle);
|
||||
DLOG(INFO) << "received metric: " << this_reporter->histogram_name_
|
||||
|
@ -236,17 +267,41 @@ FrequencyCounter::~FrequencyCounter() {
|
|||
void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter,
|
||||
time_t cycle_duration) {
|
||||
tagged_counter_.reset(tagged_counter);
|
||||
DCHECK(cycle_duration > 0);
|
||||
DCHECK_GT(cycle_duration, 0);
|
||||
cycle_duration_ = cycle_duration;
|
||||
}
|
||||
|
||||
void FrequencyCounter::UpdateInternal(int32 count, time_t now) {
|
||||
DCHECK(tagged_counter_.get() != NULL);
|
||||
tagged_counter_->Update(GetCycleNumber(now), count);
|
||||
DCHECK(tagged_counter_.get());
|
||||
tagged_counter_->Update(0, GetCycleNumber(now), count);
|
||||
}
|
||||
|
||||
int32 FrequencyCounter::GetCycleNumber(time_t now) {
|
||||
return now / cycle_duration_;
|
||||
}
|
||||
|
||||
VersionCounter::VersionCounter() : cycle_duration_(1) {
|
||||
}
|
||||
|
||||
VersionCounter::~VersionCounter() {
|
||||
}
|
||||
|
||||
void VersionCounter::Init(TaggedCounterInterface* tagged_counter,
|
||||
time_t cycle_duration) {
|
||||
tagged_counter_.reset(tagged_counter);
|
||||
DCHECK_GT(cycle_duration, 0);
|
||||
cycle_duration_ = cycle_duration;
|
||||
}
|
||||
|
||||
void VersionCounter::UpdateInternal(int32 count,
|
||||
time_t now,
|
||||
uint32 version_hash) {
|
||||
DCHECK(tagged_counter_.get());
|
||||
tagged_counter_->Update(GetCycleNumber(now), version_hash, count);
|
||||
}
|
||||
|
||||
int32 VersionCounter::GetCycleNumber(time_t now) {
|
||||
return now / cycle_duration_;
|
||||
}
|
||||
|
||||
} // namespace chromeos_metrics
|
||||
|
|
|
@ -20,15 +20,14 @@ namespace chromeos_metrics {
|
|||
const int kSecondsPerDay = 60 * 60 * 24;
|
||||
const int kSecondsPerWeek = kSecondsPerDay * 7;
|
||||
|
||||
// TaggedCounter maintains a persistent storage (i.e., a file)
|
||||
// aggregation counter for a given tag (e.g., day, hour) that survives
|
||||
// TaggedCounter maintains a persistent storage (i.e., a file) aggregation
|
||||
// counter for given tags (e.g., day, hour, version number) that survives
|
||||
// system shutdowns, reboots and crashes, as well as daemon process
|
||||
// restarts. The counter object is initialized by pointing to the
|
||||
// persistent storage file and providing a callback used for reporting
|
||||
// aggregated data. The counter can then be updated with additional
|
||||
// event counts. The aggregated count is reported through the
|
||||
// callback when the counter is explicitly flushed or when data for a
|
||||
// new tag arrives.
|
||||
// restarts. The counter object is initialized by pointing to the persistent
|
||||
// storage file and providing a callback used for reporting aggregated data.
|
||||
// The counter can then be updated with additional event counts. The
|
||||
// aggregated count is reported through the callback when the counter is
|
||||
// explicitly flushed or when data for a new tag arrives.
|
||||
//
|
||||
// The primary reason for using an interface is to allow easier unit
|
||||
// testing in clients through mocking thus avoiding file access and
|
||||
|
@ -41,16 +40,16 @@ class TaggedCounterInterface {
|
|||
// aggregated data is discarded.
|
||||
//
|
||||
// |handle| is the |reporter_handle| pointer passed through Init.
|
||||
// |tag| is the tag associated with the aggregated count.
|
||||
// |count| is aggregated count.
|
||||
typedef void (*Reporter)(void* handle, int32 tag, int32 count);
|
||||
typedef void (*Reporter)(void* handle, int32 count);
|
||||
|
||||
virtual ~TaggedCounterInterface() {}
|
||||
|
||||
// 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.
|
||||
virtual void Update(int32 tag, int32 count) = 0;
|
||||
// Adds |count| of events for the given tags. If there's an existing
|
||||
// aggregated count for different tags, it's reported through the reporter
|
||||
// callback, and optionally discarded, depending on whether |report_tag|
|
||||
// changed or |reset_tag| changed.
|
||||
virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) = 0;
|
||||
|
||||
// Reports the current aggregated count (if any) through the
|
||||
// reporter callback and discards it.
|
||||
|
@ -73,7 +72,7 @@ class TaggedCounter : public TaggedCounterInterface {
|
|||
Reporter reporter, void* reporter_handle);
|
||||
|
||||
// Implementation of interface methods.
|
||||
virtual void Update(int32 tag, int32 count);
|
||||
virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count);
|
||||
virtual void Flush();
|
||||
|
||||
private:
|
||||
|
@ -84,7 +83,7 @@ class TaggedCounter : public TaggedCounterInterface {
|
|||
FRIEND_TEST(TaggedCounterTest, InitFromFile);
|
||||
FRIEND_TEST(TaggedCounterTest, Update);
|
||||
|
||||
// The current tag/count record is cached by the counter object to
|
||||
// The current record is cached by the counter object to
|
||||
// avoid potentially unnecessary I/O. The cached record can be in
|
||||
// one of the following states:
|
||||
enum RecordState {
|
||||
|
@ -95,34 +94,42 @@ class TaggedCounter : public TaggedCounterInterface {
|
|||
kRecordValidDirty // Current record valid, persistent storage is invalid.
|
||||
};
|
||||
|
||||
// Defines the tag/count record. Objects of this class are synced
|
||||
// Defines the record. Objects of this class are synced
|
||||
// with the persistent storage through binary reads/writes.
|
||||
class Record {
|
||||
public:
|
||||
// Creates a new Record with |tag_| and |count_| reset to 0.
|
||||
Record() : tag_(0), count_(0) {}
|
||||
// Creates a new Record with all fields reset to 0.
|
||||
Record() : report_tag_(0), reset_tag_(0), count_(0) {}
|
||||
|
||||
// Initializes with |tag| and |count|. If |count| is negative,
|
||||
// |count_| is set to 0.
|
||||
void Init(int32 tag, int32 count);
|
||||
// Initializes with |report_tag|, |reset_tag| and |count|.
|
||||
// If |count| is negative, |count_| is set to 0.
|
||||
void Init(uint32 report_tag, uint32 reset_tag, int32 count);
|
||||
|
||||
// Adds |count| to the current |count_|. Negative |count| is
|
||||
// ignored. In case of positive overflow, |count_| is saturated to
|
||||
// kint32max.
|
||||
void Add(int32 count);
|
||||
|
||||
int32 tag() const { return tag_; }
|
||||
uint32 report_tag() const { return report_tag_; }
|
||||
void set_report_tag(uint32 report_tag) { report_tag_ = report_tag; }
|
||||
uint32 reset_tag() const { return reset_tag_; }
|
||||
int32 count() const { return count_; }
|
||||
|
||||
private:
|
||||
int32 tag_;
|
||||
// When |report_tag_| changes, the counter is reported as a UMA sample.
|
||||
// When |reset_tag_| changes, the counter is both reported and reset.
|
||||
uint32 report_tag_;
|
||||
uint32 reset_tag_;
|
||||
int32 count_;
|
||||
};
|
||||
|
||||
// Implementation of the Update and Flush methods. Goes through the
|
||||
// necessary steps to read, report, update, and sync the aggregated
|
||||
// record.
|
||||
void UpdateInternal(int32 tag, int32 count, bool flush);
|
||||
void UpdateInternal(uint32 report_tag,
|
||||
uint32 reset_tag,
|
||||
int32 count,
|
||||
bool flush);
|
||||
|
||||
// If the current cached record is invalid, reads it from persistent
|
||||
// storage specified through file descriptor |fd| and updates the
|
||||
|
@ -130,17 +137,19 @@ class TaggedCounter : public TaggedCounterInterface {
|
|||
// persistent storage contents.
|
||||
void ReadRecord(int fd);
|
||||
|
||||
// If there's an existing valid record and either |flush| is true,
|
||||
// or the new |tag| is different than the old one, reports the
|
||||
// aggregated data through the reporter callback and resets the
|
||||
// cached record.
|
||||
void ReportRecord(int32 tag, bool flush);
|
||||
// If there's an existing valid record and either |flush| is true, or either
|
||||
// new tag is different than the old one, reports the aggregated data through
|
||||
// the reporter callback, and possibly resets the cached record.
|
||||
void ReportRecord(uint32 report_tag, uint32 reset_tag, bool flush);
|
||||
|
||||
// Updates the cached record given the new |tag| and |count|. This
|
||||
// Updates the cached record given the new tags and |count|. This
|
||||
// method expects either a null cached record, or a valid cached
|
||||
// record with the same tag as |tag|. If |flush| is true, the method
|
||||
// record with the same tags as given. If |flush| is true, the method
|
||||
// asserts that the cached record is null and returns.
|
||||
void UpdateRecord(int32 tag, int32 count, bool flush);
|
||||
void UpdateRecord(uint32 report_tag,
|
||||
uint32 reset_tag,
|
||||
int32 count,
|
||||
bool flush);
|
||||
|
||||
// If the cached record state is dirty, updates the persistent
|
||||
// storage specified through file descriptor |fd| and switches the
|
||||
|
@ -187,8 +196,8 @@ class TaggedCounterReporter : public TaggedCounterInterface {
|
|||
int buckets);
|
||||
|
||||
// Implementation of interface method.
|
||||
virtual void Update(int32 tag, int32 count) {
|
||||
tagged_counter_->Update(tag, count);
|
||||
virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) {
|
||||
tagged_counter_->Update(report_tag, reset_tag, count);
|
||||
}
|
||||
// Implementation of interface method.
|
||||
virtual void Flush() {
|
||||
|
@ -216,7 +225,7 @@ class TaggedCounterReporter : public TaggedCounterInterface {
|
|||
friend class TaggedCounterReporterTest;
|
||||
FRIEND_TEST(TaggedCounterReporterTest, Report);
|
||||
|
||||
static void Report(void* handle, int32 tag, int32 count);
|
||||
static void Report(void* handle, int32 count);
|
||||
|
||||
static MetricsLibraryInterface* metrics_lib_;
|
||||
scoped_ptr<TaggedCounter> tagged_counter_;
|
||||
|
@ -283,6 +292,54 @@ class FrequencyCounter {
|
|||
scoped_ptr<TaggedCounterInterface> tagged_counter_;
|
||||
};
|
||||
|
||||
// VersionCounter is like a FrequencyCounter, but it exposes
|
||||
// separate "report" and "reset" tags, for counters that should
|
||||
// be reported more often than they are reset.
|
||||
class VersionCounter {
|
||||
public:
|
||||
VersionCounter();
|
||||
virtual ~VersionCounter();
|
||||
|
||||
// Initialize a version counter, which is necessary before first use.
|
||||
// |tagged_counter| is used to store the counts. Its memory is 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 |count| events have occurred. The
|
||||
// time is implicitly assumed to be the time of the call.
|
||||
// The version hash is passed.
|
||||
virtual void Update(int32 count, uint32 version_hash) {
|
||||
UpdateInternal(count, time(NULL), version_hash);
|
||||
}
|
||||
|
||||
// Reports the counter if enough time has passed, and also resets it if the
|
||||
// version number has changed.
|
||||
virtual void FlushOnChange(uint32 version_hash) {
|
||||
UpdateInternal(0, time(NULL), version_hash);
|
||||
}
|
||||
|
||||
// Accessor function.
|
||||
const TaggedCounterInterface& tagged_counter() const {
|
||||
return *tagged_counter_;
|
||||
}
|
||||
|
||||
time_t cycle_duration() const {
|
||||
return cycle_duration_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class VersionCounterTest;
|
||||
FRIEND_TEST(VersionCounterTest, UpdateInternal);
|
||||
|
||||
void UpdateInternal(int32 count, time_t now, uint32 version_hash);
|
||||
// TODO(semenzato): it's generally better to use base::TimeTicks (for
|
||||
// monotonically-increasing timestamps) or base::Time (for wall time)
|
||||
int32 GetCycleNumber(time_t now);
|
||||
time_t cycle_duration_;
|
||||
scoped_ptr<TaggedCounterInterface> tagged_counter_;
|
||||
};
|
||||
|
||||
} // namespace chromeos_metrics
|
||||
|
||||
#endif // METRICS_COUNTER_H_
|
||||
|
|
|
@ -17,7 +17,7 @@ class TaggedCounterMock : public TaggedCounter {
|
|||
public:
|
||||
MOCK_METHOD3(Init, void(const char* filename,
|
||||
Reporter reporter, void* reporter_handle));
|
||||
MOCK_METHOD2(Update, void(int tag, int count));
|
||||
MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count));
|
||||
MOCK_METHOD0(Flush, void());
|
||||
};
|
||||
|
||||
|
@ -28,7 +28,7 @@ class TaggedCounterReporterMock : public TaggedCounterReporter {
|
|||
int min,
|
||||
int max,
|
||||
int nbuckets));
|
||||
MOCK_METHOD2(Update, void(int32 tag, int32 count));
|
||||
MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count));
|
||||
MOCK_METHOD0(Flush, void());
|
||||
};
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ static const char kDoesNotExistFile[] = "/does/not/exist";
|
|||
class RecordTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
EXPECT_EQ(0, record_.tag());
|
||||
EXPECT_EQ(0, record_.report_tag());
|
||||
EXPECT_EQ(0, record_.reset_tag());
|
||||
EXPECT_EQ(0, record_.count());
|
||||
}
|
||||
|
||||
|
@ -62,11 +63,28 @@ class TaggedCounterTest : public testing::Test {
|
|||
base::DeleteFile(FilePath(kTestRecordFile), false);
|
||||
}
|
||||
|
||||
// Asserts that the record file contains the specified contents.
|
||||
testing::AssertionResult AssertRecord(const char* expr_tag,
|
||||
testing::AssertionResult AssertRecord(const char* expr_reset_tag,
|
||||
const char* expr_count,
|
||||
int32 expected_tag,
|
||||
uint32 expected_reset_tag,
|
||||
int32 expected_count) {
|
||||
return AssertRecordFull(12345, expected_reset_tag, expected_count, false);
|
||||
}
|
||||
|
||||
// Asserts that the record file contains the specified contents.
|
||||
testing::AssertionResult AssertRecord3(const char* expr_report_tag,
|
||||
const char* expr_reset_tag,
|
||||
const char* expr_count,
|
||||
uint32 expected_report_tag,
|
||||
uint32 expected_reset_tag,
|
||||
int32 expected_count) {
|
||||
return AssertRecordFull(expected_report_tag, expected_reset_tag,
|
||||
expected_count, true);
|
||||
}
|
||||
|
||||
testing::AssertionResult AssertRecordFull(uint32 expected_report_tag,
|
||||
uint32 expected_reset_tag,
|
||||
int32 expected_count,
|
||||
bool check_report_tag) {
|
||||
int fd = HANDLE_EINTR(open(kTestRecordFile, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
testing::Message msg;
|
||||
|
@ -84,10 +102,16 @@ class TaggedCounterTest : public testing::Test {
|
|||
return testing::AssertionFailure(msg);
|
||||
}
|
||||
|
||||
if (record.tag() != expected_tag || record.count() != expected_count) {
|
||||
if ((check_report_tag && (record.report_tag() != expected_report_tag)) ||
|
||||
record.reset_tag() != expected_reset_tag ||
|
||||
record.count() != expected_count) {
|
||||
testing::Message msg;
|
||||
msg << "actual record (" << record.tag() << ", " << record.count()
|
||||
<< ") expected (" << expected_tag << ", " << expected_count << ")";
|
||||
msg << "actual record (" << record.report_tag() << ", "
|
||||
<< record.reset_tag() << ", " << record.count()
|
||||
<< ") expected (" << expected_report_tag << ", "
|
||||
<< expected_reset_tag << ", " << expected_count << ")";
|
||||
if (!check_report_tag)
|
||||
msg << "\n(ignore differences in the first field)";
|
||||
HANDLE_EINTR(close(fd));
|
||||
return testing::AssertionFailure(msg);
|
||||
}
|
||||
|
@ -99,7 +123,7 @@ class TaggedCounterTest : public testing::Test {
|
|||
// Returns true if the persistent record file does not exist or is
|
||||
// empty, false otherwise.
|
||||
bool AssertNoOrEmptyRecordFile() {
|
||||
FilePath record_file(counter_.filename_);
|
||||
base::FilePath record_file(counter_.filename_);
|
||||
int64 record_file_size;
|
||||
return !base::PathExists(record_file) ||
|
||||
(base::GetFileSize(record_file, &record_file_size) &&
|
||||
|
@ -108,18 +132,18 @@ class TaggedCounterTest : public testing::Test {
|
|||
|
||||
// Adds a reporter call expectation that the specified tag/count
|
||||
// callback will be generated.
|
||||
void ExpectReporterCall(int32 tag, int32 count) {
|
||||
EXPECT_CALL(reporter_, Call(_, tag, count))
|
||||
void ExpectReporterCall(int32 count) {
|
||||
EXPECT_CALL(reporter_, Call(_, count))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
}
|
||||
|
||||
// The reporter callback forwards the call to the reporter mock so
|
||||
// that we can set call expectations.
|
||||
static void Reporter(void* handle, int32 tag, int32 count) {
|
||||
static void Reporter(void* handle, int32 count) {
|
||||
TaggedCounterTest* test = static_cast<TaggedCounterTest*>(handle);
|
||||
ASSERT_FALSE(NULL == test);
|
||||
test->reporter_.Call(handle, tag, count);
|
||||
test->reporter_.Call(handle, count);
|
||||
}
|
||||
|
||||
// Collects log messages in the |log_| member string so that they
|
||||
|
@ -148,8 +172,7 @@ class TaggedCounterTest : public testing::Test {
|
|||
std::string log_;
|
||||
|
||||
// Reporter mock to set callback expectations on.
|
||||
StrictMock<MockFunction<void(void* handle,
|
||||
int32 tag, int32 count)> > reporter_;
|
||||
StrictMock<MockFunction<void(void* handle, int32 count)> > reporter_;
|
||||
|
||||
// Pointer to the current test fixture.
|
||||
static TaggedCounterTest* test_;
|
||||
|
@ -159,12 +182,14 @@ class TaggedCounterTest : public testing::Test {
|
|||
TaggedCounterTest* TaggedCounterTest::test_ = NULL;
|
||||
|
||||
TEST_F(RecordTest, Init) {
|
||||
record_.Init(/* tag */ 5, /* count */ -1);
|
||||
EXPECT_EQ(5, record_.tag());
|
||||
record_.Init(/* report_tag */ 8, /* reset_tag */ 5, /* count */ -1);
|
||||
EXPECT_EQ(8, record_.report_tag());
|
||||
EXPECT_EQ(5, record_.reset_tag());
|
||||
EXPECT_EQ(0, record_.count());
|
||||
|
||||
record_.Init(/* tag */ -2, /* count */ 10);
|
||||
EXPECT_EQ(-2, record_.tag());
|
||||
record_.Init(/* report_tag */ -8, /* reset_tag */ -2, /* count */ 10);
|
||||
EXPECT_EQ(-8, record_.report_tag());
|
||||
EXPECT_EQ(-2, record_.reset_tag());
|
||||
EXPECT_EQ(10, record_.count());
|
||||
}
|
||||
|
||||
|
@ -193,7 +218,7 @@ TEST_F(TaggedCounterTest, BadFileLocation) {
|
|||
// created.
|
||||
counter_.Init(kDoesNotExistFile,
|
||||
/* reporter */ NULL, /* reporter_handle */ NULL);
|
||||
counter_.Update(/* tag */ 10, /* count */ 20);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 10, /* count */ 20);
|
||||
EXPECT_TRUE(LogContains("Unable to open the persistent counter file: "
|
||||
"No such file or directory"));
|
||||
EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
|
||||
|
@ -204,62 +229,72 @@ TEST_F(TaggedCounterTest, Flush) {
|
|||
counter_.Flush();
|
||||
EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
|
||||
|
||||
counter_.Update(/* tag */ 40, /* count */ 60);
|
||||
ExpectReporterCall(/* tag */ 40, /* count */ 60);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 40, /* count */ 60);
|
||||
ExpectReporterCall(/* count */ 60);
|
||||
counter_.Flush();
|
||||
EXPECT_TRUE(AssertNoOrEmptyRecordFile());
|
||||
EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
|
||||
|
||||
counter_.Update(/* tag */ 41, /* count */ 70);
|
||||
counter_.record_.Init(/* tag */ 0, /* count */ 0);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 41, /* count */ 70);
|
||||
counter_.record_.Init(/* report_tag */ 0, /* reset_tag */ 0, /* count */ 0);
|
||||
counter_.record_state_ = TaggedCounter::kRecordInvalid;
|
||||
ExpectReporterCall(/* tag */ 41, /* count */ 70);
|
||||
ExpectReporterCall(/* count */ 70);
|
||||
counter_.Flush();
|
||||
EXPECT_TRUE(AssertNoOrEmptyRecordFile());
|
||||
EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
|
||||
}
|
||||
|
||||
TEST_F(TaggedCounterTest, InitFromFile) {
|
||||
counter_.Update(/* tag */ 30, /* count */ 50);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 30, /* count */ 50);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 50);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
counter_.Init(kTestRecordFile, &Reporter, this);
|
||||
counter_.Update(/* tag */ 30, /* count */ 40);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 30, /* count */ 40);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 90);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
counter_.Init(kTestRecordFile, &Reporter, this);
|
||||
ExpectReporterCall(/* tag */ 30, /* count */ 90);
|
||||
counter_.Update(/* tag */ 31, /* count */ 60);
|
||||
ExpectReporterCall(/* count */ 90);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 31, /* count */ 60);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 31, /* seconds */ 60);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
ExpectReporterCall(/* tag */ 31, /* count */ 60);
|
||||
ExpectReporterCall(/* count */ 60);
|
||||
counter_.Init(kTestRecordFile, &Reporter, this);
|
||||
counter_.Update(/* tag */ 32, /* count */ 0);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 32, /* count */ 0);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 32, /* seconds */ 0);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
}
|
||||
|
||||
TEST_F(TaggedCounterTest, Update) {
|
||||
counter_.Update(/* tag */ 20, /* count */ 30);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 30);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 30);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
counter_.Update(/* tag */ 20, /* count */ 40);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 40);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 70);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
ExpectReporterCall(/* tag */ 20, /* count */ 70);
|
||||
counter_.Update(/* tag */ 21, /* count */ 15);
|
||||
ExpectReporterCall(/* count */ 70);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 21, /* count */ 15);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 21, /* seconds */ 15);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
ExpectReporterCall(/* tag */ 21, /* count */ 15);
|
||||
counter_.Update(/* tag */ 22, /* count */ 0);
|
||||
ExpectReporterCall(/* count */ 15);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 22, /* count */ 0);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 0);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
|
||||
ExpectReporterCall(/* count */ 33);
|
||||
counter_.Update(/* report_tag */ 0, /* reset_tag */ 22, /* count */ 33);
|
||||
EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 33);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
// Check that changing the report tag does not reset the counter.
|
||||
counter_.Update(/* report_tag */ 1, /* reset_tag */ 22, /* count */ 0);
|
||||
EXPECT_PRED_FORMAT3(AssertRecord3, /* version */ 1,
|
||||
/* day */ 22, /* seconds */ 33);
|
||||
EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
|
||||
}
|
||||
|
||||
static const char kTestFilename[] = "test_filename";
|
||||
|
@ -311,10 +346,10 @@ TEST_F(TaggedCounterReporterTest, Init) {
|
|||
|
||||
TEST_F(TaggedCounterReporterTest, Update) {
|
||||
DoInit();
|
||||
EXPECT_CALL(*tagged_counter_, Update(1, 2))
|
||||
EXPECT_CALL(*tagged_counter_, Update(1, 0, 2))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
reporter_.Update(1, 2);
|
||||
reporter_.Update(1, 0, 2);
|
||||
}
|
||||
|
||||
TEST_F(TaggedCounterReporterTest, Flush) {
|
||||
|
@ -334,7 +369,7 @@ TEST_F(TaggedCounterReporterTest, Report) {
|
|||
kHistogramBuckets))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
reporter_.Report(&reporter_, 127, 301);
|
||||
reporter_.Report(&reporter_, 301);
|
||||
}
|
||||
|
||||
class FrequencyCounterTest : public testing::Test {
|
||||
|
@ -385,12 +420,39 @@ TEST_F(FrequencyCounterTest, GetCycleNumberForDay) {
|
|||
|
||||
TEST_F(FrequencyCounterTest, UpdateInternal) {
|
||||
CheckInit(kSecondsPerWeek);
|
||||
EXPECT_CALL(*tagged_counter_, Update(150, 2))
|
||||
EXPECT_CALL(*tagged_counter_, Update(0, 150, 2))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150);
|
||||
}
|
||||
|
||||
class VersionCounterTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
tagged_counter_ = NULL;
|
||||
}
|
||||
void Init();
|
||||
|
||||
VersionCounter version_counter_;
|
||||
StrictMock<TaggedCounterMock>* tagged_counter_;
|
||||
|
||||
TaggedCounter::Reporter reporter_;
|
||||
};
|
||||
|
||||
void VersionCounterTest::Init() {
|
||||
tagged_counter_ = new StrictMock<TaggedCounterMock>;
|
||||
version_counter_.Init(tagged_counter_, 1);
|
||||
EXPECT_EQ(tagged_counter_, version_counter_.tagged_counter_.get());
|
||||
}
|
||||
|
||||
TEST_F(VersionCounterTest, UpdateInternal) {
|
||||
Init();
|
||||
EXPECT_CALL(*tagged_counter_, Update(0, 150, 2))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
version_counter_.UpdateInternal(2, 0, 150);
|
||||
}
|
||||
|
||||
} // namespace chromeos_metrics
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
|
@ -9,12 +9,16 @@
|
|||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <base/at_exit.h>
|
||||
#include <base/file_util.h>
|
||||
#include <base/files/file_path.h>
|
||||
#include <base/hash.h>
|
||||
#include <base/logging.h>
|
||||
#include <base/strings/string_number_conversions.h>
|
||||
#include <base/strings/string_split.h>
|
||||
#include <base/strings/string_util.h>
|
||||
#include <base/strings/stringprintf.h>
|
||||
#include <base/sys_info.h>
|
||||
#include <chromeos/dbus/service_constants.h>
|
||||
#include <dbus/dbus-glib-lowlevel.h>
|
||||
|
||||
|
@ -85,6 +89,12 @@ const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
|
|||
"Logging.KernelCrashesDaily";
|
||||
const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
|
||||
"Logging.KernelCrashesWeekly";
|
||||
const char MetricsDaemon::kMetricKernelCrashesVersionName[] =
|
||||
"Logging.KernelCrashesSinceUpdate";
|
||||
const int MetricsDaemon::kMetricCumulativeCrashCountMin = 1;
|
||||
const int MetricsDaemon::kMetricCumulativeCrashCountMax = 500;
|
||||
const int MetricsDaemon::kMetricCumulativeCrashCountBuckets = 100;
|
||||
|
||||
const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
|
||||
"Logging.UncleanShutdownsDaily";
|
||||
const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
|
||||
|
@ -93,9 +103,9 @@ const char MetricsDaemon::kMetricUserCrashesDailyName[] =
|
|||
"Logging.UserCrashesDaily";
|
||||
const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
|
||||
"Logging.UserCrashesWeekly";
|
||||
const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
|
||||
const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
|
||||
const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
|
||||
const int MetricsDaemon::kMetricCrashFrequencyMin = 1;
|
||||
const int MetricsDaemon::kMetricCrashFrequencyMax = 100;
|
||||
const int MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
|
||||
|
||||
// disk stats metrics
|
||||
|
||||
|
@ -152,6 +162,10 @@ const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
|
|||
// persistent metrics path
|
||||
const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
|
||||
|
||||
// file containing OS version string
|
||||
const char MetricsDaemon::kLsbReleasePath[] = "/etc/lsb-release";
|
||||
|
||||
|
||||
// static
|
||||
const char* MetricsDaemon::kPowerStates_[] = {
|
||||
#define STATE(name, capname) #name,
|
||||
|
@ -214,12 +228,15 @@ void MetricsDaemon::DeleteFrequencyCounters() {
|
|||
}
|
||||
|
||||
void MetricsDaemon::Run(bool run_as_daemon) {
|
||||
base::AtExitManager at_exit_manager;
|
||||
|
||||
if (run_as_daemon && daemon(0, 0) != 0)
|
||||
return;
|
||||
|
||||
if (CheckSystemCrash(kKernelCrashDetectedFile)) {
|
||||
ProcessKernelCrash();
|
||||
}
|
||||
kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash());
|
||||
|
||||
if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
|
||||
ProcessUncleanShutdown();
|
||||
|
@ -266,6 +283,42 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter(
|
|||
frequency_counters_[histogram_name] = new_counter.release();
|
||||
}
|
||||
|
||||
void MetricsDaemon::ConfigureCrashVersionReporter(
|
||||
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,
|
||||
kMetricCumulativeCrashCountMin,
|
||||
kMetricCumulativeCrashCountMax,
|
||||
kMetricCumulativeCrashCountBuckets);
|
||||
scoped_ptr<chromeos_metrics::VersionCounter> new_counter(
|
||||
new chromeos_metrics::VersionCounter());
|
||||
new_counter->Init(
|
||||
static_cast<chromeos_metrics::TaggedCounterInterface*>(
|
||||
reporter.release()),
|
||||
chromeos_metrics::kSecondsPerDay);
|
||||
kernel_crash_version_counter_ = new_counter.release();
|
||||
}
|
||||
|
||||
uint32 MetricsDaemon::GetOsVersionHash() {
|
||||
static uint32 cached_version_hash = 0;
|
||||
static bool version_hash_is_cached = false;
|
||||
if (version_hash_is_cached)
|
||||
return cached_version_hash;
|
||||
version_hash_is_cached = true;
|
||||
std::string version;
|
||||
if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &version)) {
|
||||
cached_version_hash = base::Hash(version);
|
||||
} else if (testing_) {
|
||||
cached_version_hash = 42; // return any plausible value for the hash
|
||||
} else {
|
||||
LOG(FATAL) << "could not find CHROMEOS_RELEASE_VERSION";
|
||||
}
|
||||
return cached_version_hash;
|
||||
}
|
||||
|
||||
void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
|
||||
const string& diskstats_path,
|
||||
const string& vmstats_path,
|
||||
|
@ -299,6 +352,8 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
|
|||
ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
|
||||
ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
|
||||
|
||||
ConfigureCrashVersionReporter(kMetricKernelCrashesVersionName);
|
||||
|
||||
diskstats_path_ = diskstats_path;
|
||||
vmstats_path_ = vmstats_path;
|
||||
scaling_max_freq_path_ = scaling_max_freq_path;
|
||||
|
@ -328,19 +383,19 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
|
|||
|
||||
vector<string> matches;
|
||||
matches.push_back(
|
||||
StringPrintf("type='signal',interface='%s',path='/',member='%s'",
|
||||
kCrashReporterInterface,
|
||||
kCrashReporterUserCrashSignal));
|
||||
base::StringPrintf("type='signal',interface='%s',path='/',member='%s'",
|
||||
kCrashReporterInterface,
|
||||
kCrashReporterUserCrashSignal));
|
||||
matches.push_back(
|
||||
StringPrintf("type='signal',interface='%s',path='%s',member='%s'",
|
||||
power_manager::kPowerManagerInterface,
|
||||
power_manager::kPowerManagerServicePath,
|
||||
power_manager::kPowerStateChangedSignal));
|
||||
base::StringPrintf("type='signal',interface='%s',path='%s',member='%s'",
|
||||
power_manager::kPowerManagerInterface,
|
||||
power_manager::kPowerManagerServicePath,
|
||||
power_manager::kPowerStateChangedSignal));
|
||||
matches.push_back(
|
||||
StringPrintf("type='signal',sender='%s',interface='%s',path='%s'",
|
||||
login_manager::kSessionManagerServiceName,
|
||||
login_manager::kSessionManagerInterface,
|
||||
login_manager::kSessionManagerServicePath));
|
||||
base::StringPrintf("type='signal',sender='%s',interface='%s',path='%s'",
|
||||
login_manager::kSessionManagerServiceName,
|
||||
login_manager::kSessionManagerInterface,
|
||||
login_manager::kSessionManagerServicePath));
|
||||
|
||||
// Registers D-Bus matches for the signals we would like to catch.
|
||||
for (vector<string>::const_iterator it = matches.begin();
|
||||
|
@ -466,15 +521,19 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) {
|
|||
}
|
||||
TimeDelta since_epoch = now - Time();
|
||||
int day = since_epoch.InDays();
|
||||
daily_use_->Update(day, seconds);
|
||||
user_crash_interval_->Update(0, seconds);
|
||||
kernel_crash_interval_->Update(0, seconds);
|
||||
daily_use_->Update(day, seconds, 0);
|
||||
user_crash_interval_->Update(0, seconds, 0);
|
||||
kernel_crash_interval_->Update(0, seconds, 0);
|
||||
|
||||
// Flush finished cycles of all frequency counters.
|
||||
for (FrequencyCounters::iterator i = frequency_counters_.begin();
|
||||
i != frequency_counters_.end(); ++i) {
|
||||
i->second->FlushFinishedCycles();
|
||||
}
|
||||
// Report count if we're on a new cycle. FlushOnChange can also reset the
|
||||
// counter, but not when called from here, because any version change has
|
||||
// already been processed during initialization.
|
||||
kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash());
|
||||
|
||||
// Schedules a use monitor on inactive->active transitions and
|
||||
// unschedules it on active->inactive transitions.
|
||||
|
@ -513,6 +572,8 @@ void MetricsDaemon::ProcessKernelCrash() {
|
|||
frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
|
||||
frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
|
||||
frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
|
||||
|
||||
kernel_crash_version_counter_->Update(1, GetOsVersionHash());
|
||||
}
|
||||
|
||||
void MetricsDaemon::ProcessUncleanShutdown() {
|
||||
|
@ -535,8 +596,7 @@ bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
|
|||
|
||||
// Deletes the crash-detected file so that the daemon doesn't report
|
||||
// another kernel crash in case it's restarted.
|
||||
base::DeleteFile(crash_detected,
|
||||
false); // recursive
|
||||
base::DeleteFile(crash_detected, false); // not recursive
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -951,7 +1011,8 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
|
|||
int swap_free = 0;
|
||||
// Send all fields retrieved, except total memory.
|
||||
for (unsigned int i = 1; i < fields.size(); i++) {
|
||||
string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
|
||||
string metrics_name = base::StringPrintf("Platform.Meminfo%s",
|
||||
fields[i].name);
|
||||
int percent;
|
||||
switch (fields[i].op) {
|
||||
case kMeminfoOp_HistPercent:
|
||||
|
@ -1079,15 +1140,15 @@ bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
|
|||
LOG(WARNING) << "borked meminfo parser";
|
||||
return false;
|
||||
}
|
||||
string metrics_name = StringPrintf("Platform.MemuseAnon%d",
|
||||
memuse_interval_index_);
|
||||
string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
|
||||
memuse_interval_index_);
|
||||
SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
|
||||
100, 101);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
|
||||
void MetricsDaemon::ReportDailyUse(void* handle, int count) {
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
namespace chromeos_metrics {
|
||||
class FrequencyCounter;
|
||||
class VersionCounter;
|
||||
class TaggedCounter;
|
||||
class TaggedCounterReporter;
|
||||
}
|
||||
|
@ -133,20 +134,25 @@ class MetricsDaemon {
|
|||
// Metric parameters.
|
||||
static const char kMetricAnyCrashesDailyName[];
|
||||
static const char kMetricAnyCrashesWeeklyName[];
|
||||
static const char kMetricCrashFrequencyBuckets;
|
||||
static const char kMetricCrashFrequencyMax;
|
||||
static const char kMetricCrashFrequencyMin;
|
||||
static const int kMetricCrashFrequencyBuckets;
|
||||
static const int kMetricCrashFrequencyMax;
|
||||
static const int kMetricCrashFrequencyMin;
|
||||
static const int kMetricCrashIntervalBuckets;
|
||||
static const int kMetricCrashIntervalMax;
|
||||
static const int kMetricCrashIntervalMin;
|
||||
static const int kMetricCumulativeCrashCountBuckets;
|
||||
static const int kMetricCumulativeCrashCountMax;
|
||||
static const int kMetricCumulativeCrashCountMin;
|
||||
static const int kMetricDailyUseTimeBuckets;
|
||||
static const int kMetricDailyUseTimeMax;
|
||||
static const int kMetricDailyUseTimeMin;
|
||||
static const char kMetricDailyUseTimeName[];
|
||||
static const char kMetricKernelCrashesDailyName[];
|
||||
static const char kMetricKernelCrashesWeeklyName[];
|
||||
static const char kMetricKernelCrashesVersionName[];
|
||||
static const char kMetricKernelCrashIntervalName[];
|
||||
static const char kMetricsPath[];
|
||||
static const char kLsbReleasePath[];
|
||||
static const char kMetricUncleanShutdownIntervalName[];
|
||||
static const char kMetricUncleanShutdownsDailyName[];
|
||||
static const char kMetricUncleanShutdownsWeeklyName[];
|
||||
|
@ -194,6 +200,9 @@ class MetricsDaemon {
|
|||
// Configures the given frequency counter reporter.
|
||||
void ConfigureCrashFrequencyReporter(const char* histogram_name);
|
||||
|
||||
// Configures the given version counter reporter.
|
||||
void ConfigureCrashVersionReporter(const char* histogram_name);
|
||||
|
||||
// Returns file path to persistent file for generating given histogram.
|
||||
base::FilePath GetHistogramPath(const char* histogram_name);
|
||||
|
||||
|
@ -271,7 +280,7 @@ class MetricsDaemon {
|
|||
void UnscheduleUseMonitor();
|
||||
|
||||
// Report daily use through UMA.
|
||||
static void ReportDailyUse(void* handle, int tag, int count);
|
||||
static void ReportDailyUse(void* handle, int count);
|
||||
|
||||
// Sends a regular (exponential) histogram sample to Chrome for
|
||||
// transport to UMA. See MetricsLibrary::SendToUMA in
|
||||
|
@ -352,6 +361,10 @@ class MetricsDaemon {
|
|||
// Reads an integer CPU frequency value from sysfs.
|
||||
bool ReadFreqToInt(const std::string& sysfs_file_name, int* value);
|
||||
|
||||
// Reads the current OS version from /etc/lsb-release and hashes it
|
||||
// to a unsigned 32-bit int.
|
||||
uint32 GetOsVersionHash();
|
||||
|
||||
// Test mode.
|
||||
bool testing_;
|
||||
|
||||
|
@ -394,6 +407,10 @@ class MetricsDaemon {
|
|||
// Map of all frequency counters, to simplify flushing them.
|
||||
FrequencyCounters frequency_counters_;
|
||||
|
||||
// This contains a cumulative number of kernel crashes since the latest
|
||||
// version update.
|
||||
chromeos_metrics::VersionCounter* kernel_crash_version_counter_;
|
||||
|
||||
// Sleep period until the next daily usage aggregation performed by
|
||||
// the daily use monitor (see ScheduleUseMonitor).
|
||||
int usemon_interval_;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <base/at_exit.h>
|
||||
#include <base/file_util.h>
|
||||
#include <base/strings/stringprintf.h>
|
||||
#include <chromeos/dbus/service_constants.h>
|
||||
|
@ -63,10 +64,12 @@ class MetricsDaemonTest : public testing::Test {
|
|||
EXPECT_EQ(NULL, daemon_.daily_use_.get());
|
||||
EXPECT_EQ(NULL, daemon_.kernel_crash_interval_.get());
|
||||
EXPECT_EQ(NULL, daemon_.user_crash_interval_.get());
|
||||
kFakeDiskStats[0] = StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[0], kFakeWriteSectors[0]);
|
||||
kFakeDiskStats[1] = StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[1], kFakeWriteSectors[1]);
|
||||
kFakeDiskStats[0] = base::StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[0],
|
||||
kFakeWriteSectors[0]);
|
||||
kFakeDiskStats[1] = base::StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[1],
|
||||
kFakeWriteSectors[1]);
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str());
|
||||
CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 10000000);
|
||||
CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 10000000);
|
||||
|
@ -163,13 +166,13 @@ class MetricsDaemonTest : public testing::Test {
|
|||
// Adds active use aggregation counters update expectations that the
|
||||
// specified tag/count update will be generated.
|
||||
void ExpectActiveUseUpdate(int daily_tag, int count) {
|
||||
EXPECT_CALL(*daily_use_, Update(daily_tag, count))
|
||||
EXPECT_CALL(*daily_use_, Update(daily_tag, count, 0))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(*kernel_crash_interval_, Update(0, count))
|
||||
EXPECT_CALL(*kernel_crash_interval_, Update(0, count, 0))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(*user_crash_interval_, Update(0, count))
|
||||
EXPECT_CALL(*user_crash_interval_, Update(0, count, 0))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
ExpectFrequencyFlushCalls();
|
||||
|
@ -178,13 +181,13 @@ class MetricsDaemonTest : public testing::Test {
|
|||
// Adds active use aggregation counters update expectations that
|
||||
// ignore the update arguments.
|
||||
void IgnoreActiveUseUpdate() {
|
||||
EXPECT_CALL(*daily_use_, Update(_, _))
|
||||
EXPECT_CALL(*daily_use_, Update(_, _, _))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(*kernel_crash_interval_, Update(_, _))
|
||||
EXPECT_CALL(*kernel_crash_interval_, Update(_, _, _))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
EXPECT_CALL(*user_crash_interval_, Update(_, _))
|
||||
EXPECT_CALL(*user_crash_interval_, Update(_, _, _))
|
||||
.Times(1)
|
||||
.RetiresOnSaturation();
|
||||
ExpectFrequencyFlushCalls();
|
||||
|
@ -291,7 +294,7 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) {
|
|||
static const char kKernelCrashDetected[] = "test-kernel-crash-detected";
|
||||
EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
|
||||
|
||||
FilePath crash_detected(kKernelCrashDetected);
|
||||
base::FilePath crash_detected(kKernelCrashDetected);
|
||||
file_util::WriteFile(crash_detected, "", 0);
|
||||
EXPECT_TRUE(base::PathExists(crash_detected));
|
||||
EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected));
|
||||
|
@ -303,14 +306,14 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) {
|
|||
|
||||
TEST_F(MetricsDaemonTest, ReportDailyUse) {
|
||||
ExpectDailyUseTimeMetric(/* sample */ 2);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 20, /* count */ 90);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 90);
|
||||
|
||||
ExpectDailyUseTimeMetric(/* sample */ 1);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 23, /* count */ 89);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 89);
|
||||
|
||||
// There should be no metrics generated for the calls below.
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 50, /* count */ 0);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 0);
|
||||
MetricsDaemon::ReportDailyUse(&daemon_, /* count */ -5);
|
||||
}
|
||||
|
||||
TEST_F(MetricsDaemonTest, LookupPowerState) {
|
||||
|
@ -698,5 +701,8 @@ TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
|
|||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
// Some libchrome calls need this.
|
||||
base::AtExitManager at_exit_manager;
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) {
|
|||
buffer,
|
||||
1,
|
||||
&result));
|
||||
ASSERT_TRUE(file_util::WriteFile(FilePath(kTestMounts),
|
||||
ASSERT_TRUE(file_util::WriteFile(base::FilePath(kTestMounts),
|
||||
kTestContents,
|
||||
strlen(kTestContents)));
|
||||
EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
|
||||
|
|
Loading…
Reference in New Issue