metrics: refactor counters

The metrics daemon had counter classes that represented
persistent, tagged values, and UMA stats.  To create some
derived UMA stats we need to separate persistency and UMA stats,
and the tagging doesn't help either.

This rewrite is supposed to keep the same functionality.

BUG=chromium:339588
TEST=ran and checked a few histograms
BRANCH=none

Change-Id: Ia1121ab2db391d71edffab9f52afe29ce17686ba
Reviewed-on: https://chromium-review.googlesource.com/188082
Tested-by: Luigi Semenzato <semenzato@chromium.org>
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
Commit-Queue: Luigi Semenzato <semenzato@chromium.org>
diff --git a/metrics/counter.cc b/metrics/counter.cc
deleted file mode 100644
index f71b4fb..0000000
--- a/metrics/counter.cc
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "counter.h"
-
-#include <fcntl.h>
-
-#include <base/logging.h>
-#include <base/posix/eintr_wrapper.h>
-
-#include "metrics_library.h"
-
-namespace chromeos_metrics {
-
-// TaggedCounter::Record implementation.
-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;
-}
-
-void TaggedCounter::Record::Add(int32 count) {
-  if (count <= 0)
-    return;
-
-  // Saturates on positive overflow.
-  int64 new_count = static_cast<int64>(count_) + static_cast<int64>(count);
-  if (new_count > kint32max)
-    count_ = kint32max;
-  else
-    count_ = static_cast<int32>(new_count);
-}
-
-// TaggedCounter implementation.
-TaggedCounter::TaggedCounter()
-    : reporter_(NULL),
-      reporter_handle_(NULL),
-      record_state_(kRecordInvalid) {}
-
-TaggedCounter::~TaggedCounter() {}
-
-void TaggedCounter::Init(const char* filename,
-                         Reporter reporter, void* reporter_handle) {
-  DCHECK(filename);
-  filename_ = filename;
-  reporter_ = reporter;
-  reporter_handle_ = reporter_handle;
-  record_state_ = kRecordInvalid;
-}
-
-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,  // report_tag
-                 0,  // reset_tag
-                 0,  // count
-                 true);  // Do 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)
-      return;
-  } 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_.report_tag() == report_tag &&
-        record_.reset_tag() == reset_tag)
-      return;
-  }
-
-  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
-  // sole owner of the persistent storage file so no locking is
-  // necessary.
-  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;
-  }
-  ReadRecord(fd);
-  ReportRecord(report_tag, reset_tag, flush);
-  UpdateRecord(report_tag, reset_tag, count, flush);
-  WriteRecord(fd);
-
-  HANDLE_EINTR(close(fd));
-}
-
-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;
-      return;
-    }
-    // This shouldn't happen normally unless somebody messed with the
-    // persistent storage file.
-    NOTREACHED();
-    record_state_ = kRecordNullDirty;
-    return;
-  }
-  record_state_ = kRecordNull;
-}
-
-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 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_.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;
-  }
-}
-
-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(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_.report_tag(), report_tag);
-      DCHECK_EQ(record_.reset_tag(), reset_tag);
-      if (count > 0) {
-        record_.Add(count);
-        record_state_ = kRecordValidDirty;
-      }
-      break;
-
-    default:
-      NOTREACHED();
-  }
-}
-
-void TaggedCounter::WriteRecord(int fd) {
-  switch (record_state_) {
-    case kRecordNullDirty:
-      // Truncates the aggregation file to discard the record.
-      PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0);
-      record_state_ = kRecordNull;
-      break;
-
-    case kRecordValidDirty:
-      // Updates the accumulator record in the file if there's new data.
-      PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0);
-      PLOG_IF(WARNING,
-              HANDLE_EINTR(write(fd, &record_, sizeof(record_))) !=
-              sizeof(record_));
-      record_state_ = kRecordValid;
-      break;
-
-    case kRecordNull:
-    case kRecordValid:
-      // Nothing to do.
-      break;
-
-    default:
-      NOTREACHED();
-  }
-}
-
-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 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(TaggedCounterInterface* tagged_counter,
-                            time_t cycle_duration) {
-  tagged_counter_.reset(tagged_counter);
-  DCHECK_GT(cycle_duration, 0);
-  cycle_duration_ = cycle_duration;
-}
-
-void FrequencyCounter::UpdateInternal(int32 count, time_t now) {
-  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
diff --git a/metrics/counter.h b/metrics/counter.h
deleted file mode 100644
index 679aaaf..0000000
--- a/metrics/counter.h
+++ /dev/null
@@ -1,345 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef METRICS_COUNTER_H_
-#define METRICS_COUNTER_H_
-
-#include <string>
-#include <time.h>
-
-#include <base/basictypes.h>
-#include <base/memory/scoped_ptr.h>
-#include <gtest/gtest_prod.h>  // for FRIEND_TEST
-
-class MetricsLibraryInterface;
-
-namespace chromeos_metrics {
-
-// Constants useful for frequency statistics.
-const int kSecondsPerDay = 60 * 60 * 24;
-const int kSecondsPerWeek = kSecondsPerDay * 7;
-
-// 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.
-//
-// The primary reason for using an interface is to allow easier unit
-// testing in clients through mocking thus avoiding file access and
-// callbacks. Of course, it also enables alternative implementations
-// of the counter with additional features.
-class TaggedCounterInterface {
- public:
-  // Callback type used for reporting aggregated or flushed data.
-  // Once this callback is invoked by the counter, the reported
-  // aggregated data is discarded.
-  //
-  // |handle| is the |reporter_handle| pointer passed through Init.
-  // |count| is aggregated count.
-  typedef void (*Reporter)(void* handle, int32 count);
-
-  virtual ~TaggedCounterInterface() {}
-
-  // 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.
-  virtual void Flush() = 0;
-};
-
-class TaggedCounter : public TaggedCounterInterface {
- public:
-  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.
-  virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count);
-  virtual void Flush();
-
- private:
-  friend class RecordTest;
-  friend class TaggedCounterTest;
-  FRIEND_TEST(TaggedCounterTest, BadFileLocation);
-  FRIEND_TEST(TaggedCounterTest, Flush);
-  FRIEND_TEST(TaggedCounterTest, InitFromFile);
-  FRIEND_TEST(TaggedCounterTest, Update);
-
-  // 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 {
-    kRecordInvalid,    // Invalid record, sync from persistent storage needed.
-    kRecordNull,       // No current record, persistent storage synced.
-    kRecordNullDirty,  // No current record, persistent storage is invalid.
-    kRecordValid,      // Current record valid, persistent storage synced.
-    kRecordValidDirty  // Current record valid, persistent storage is invalid.
-  };
-
-  // 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 all fields reset to 0.
-  Record() : report_tag_(0), reset_tag_(0), count_(0) {}
-
-    // 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);
-
-    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:
-    // 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(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
-  // cached record state to either null, or valid depending on the
-  // persistent storage contents.
-  void ReadRecord(int fd);
-
-  // 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 tags and |count|. This
-  // method expects either a null cached record, or a valid cached
-  // record with the same tags as given. If |flush| is true, the method
-  // asserts that the cached record is null and returns.
-  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
-  // record state to non-dirty.
-  void WriteRecord(int fd);
-
-  // Persistent storage file path.
-  std::string filename_;
-
-  // Aggregated data reporter callback and handle to pass-through.
-  Reporter reporter_;
-  void* reporter_handle_;
-
-  // Current cached aggregation record.
-  Record record_;
-
-  // Current cached aggregation record state.
-  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(uint32 report_tag, uint32 reset_tag, int32 count) {
-    tagged_counter_->Update(report_tag, reset_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 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
-// count the number of blips per day, initialize |cycle_duration| to
-// chromeos_metrics::kSecondsPerDay, and call Update with the number
-// of blips that happen concurrently (usually 1).  Reporting of the
-// value is done through TaggedCounter's reporter function.
-class FrequencyCounter {
- public:
-  // Create a new frequency counter.
-  FrequencyCounter();
-  virtual ~FrequencyCounter();
-
-  // 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
-  // time of the call.
-  virtual void Update(int32 count) {
-    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);
-
-  time_t cycle_duration_;
-  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_
diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h
deleted file mode 100644
index 77272a1..0000000
--- a/metrics/counter_mock.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef METRICS_COUNTER_MOCK_H_
-#define METRICS_COUNTER_MOCK_H_
-
-#include <string>
-
-#include <gmock/gmock.h>
-
-#include "counter.h"
-
-namespace chromeos_metrics {
-
-class TaggedCounterMock : public TaggedCounter {
- public:
-  MOCK_METHOD3(Init, void(const char* filename,
-                          Reporter reporter, void* reporter_handle));
-  MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count));
-  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_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count));
-  MOCK_METHOD0(Flush, void());
-};
-
-class FrequencyCounterMock : public FrequencyCounter {
- public:
-  MOCK_METHOD2(Init, void(TaggedCounterInterface* tagged_counter,
-                          time_t cycle_duration));
-  MOCK_METHOD1(Update, void(int32 count));
-  MOCK_METHOD0(FlushFinishedCycles, void());
-};
-
-}  // namespace chromeos_metrics
-
-#endif  // METRICS_COUNTER_MOCK_H_
diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc
deleted file mode 100644
index 90c3cd8..0000000
--- a/metrics/counter_test.cc
+++ /dev/null
@@ -1,461 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <sys/file.h>
-
-#include <base/file_util.h>
-#include <base/logging.h>
-#include <base/posix/eintr_wrapper.h>
-#include <base/strings/string_util.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "counter.h"
-#include "counter_mock.h"  // For TaggedCounterMock.
-#include "metrics_library_mock.h"
-
-using base::FilePath;
-using ::testing::_;
-using ::testing::MockFunction;
-using ::testing::StrictMock;
-
-namespace chromeos_metrics {
-
-static const char kTestRecordFile[] = "record-file";
-static const char kDoesNotExistFile[] = "/does/not/exist";
-
-class RecordTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    EXPECT_EQ(0, record_.report_tag());
-    EXPECT_EQ(0, record_.reset_tag());
-    EXPECT_EQ(0, record_.count());
-  }
-
-  // The record under test.
-  TaggedCounter::Record record_;
-};
-
-class TaggedCounterTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    EXPECT_TRUE(counter_.filename_.empty());
-    EXPECT_TRUE(NULL == counter_.reporter_);
-    EXPECT_EQ(NULL, counter_.reporter_handle_);
-    EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
-
-    counter_.Init(kTestRecordFile, &Reporter, this);
-    EXPECT_TRUE(AssertNoOrEmptyRecordFile());
-    EXPECT_EQ(kTestRecordFile, counter_.filename_);
-    EXPECT_TRUE(Reporter == counter_.reporter_);
-    EXPECT_EQ(this, counter_.reporter_handle_);
-    EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
-
-    // The test fixture object will be used by the log message handler.
-    test_ = this;
-    logging::SetLogMessageHandler(HandleLogMessages);
-  }
-
-  virtual void TearDown() {
-    logging::SetLogMessageHandler(NULL);
-    test_ = NULL;
-    base::DeleteFile(FilePath(kTestRecordFile), false);
-  }
-
-  testing::AssertionResult AssertRecord(const char* expr_reset_tag,
-                                        const char* expr_count,
-                                        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;
-      msg << "Unable to open " << kTestRecordFile;
-      return testing::AssertionFailure(msg);
-    }
-
-    TaggedCounter::Record record;
-    if (!base::ReadFromFD(fd, reinterpret_cast<char*>(&record),
-                          sizeof(record))) {
-      testing::Message msg;
-      msg << "Unable to read " << sizeof(record) << " bytes from "
-          << kTestRecordFile;
-      HANDLE_EINTR(close(fd));
-      return testing::AssertionFailure(msg);
-    }
-
-    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.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);
-    }
-
-    HANDLE_EINTR(close(fd));
-    return testing::AssertionSuccess();
-  }
-
-  // Returns true if the persistent record file does not exist or is
-  // empty, false otherwise.
-  bool AssertNoOrEmptyRecordFile() {
-    base::FilePath record_file(counter_.filename_);
-    int64 record_file_size;
-    return !base::PathExists(record_file) ||
-           (base::GetFileSize(record_file, &record_file_size) &&
-            record_file_size == 0);
-  }
-
-  // Adds a reporter call expectation that the specified tag/count
-  // callback will be generated.
-  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 count) {
-    TaggedCounterTest* test = static_cast<TaggedCounterTest*>(handle);
-    ASSERT_FALSE(NULL == test);
-    test->reporter_.Call(handle, count);
-  }
-
-  // Collects log messages in the |log_| member string so that they
-  // can be analyzed for errors and expected behavior.
-  static bool HandleLogMessages(int severity,
-                                const char* file,
-                                int line,
-                                size_t message_start,
-                                const std::string& str) {
-    test_->log_.append(str);
-    test_->log_.append("\n");
-
-    // Returning true would mute the log.
-    return false;
-  }
-
-  // Returns true if the counter log contains |pattern|, false otherwise.
-  bool LogContains(const std::string& pattern) const {
-    return log_.find(pattern) != std::string::npos;
-  }
-
-  // The TaggedCounter object under test.
-  TaggedCounter counter_;
-
-  // The accumulated counter log.
-  std::string log_;
-
-  // Reporter mock to set callback expectations on.
-  StrictMock<MockFunction<void(void* handle, int32 count)> > reporter_;
-
-  // Pointer to the current test fixture.
-  static TaggedCounterTest* test_;
-};
-
-// static
-TaggedCounterTest* TaggedCounterTest::test_ = NULL;
-
-TEST_F(RecordTest, Init) {
-  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(/* 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());
-}
-
-TEST_F(RecordTest, Add) {
-  record_.Add(/* count */ -1);
-  EXPECT_EQ(0, record_.count());
-
-  record_.Add(/* count */ 5);
-  EXPECT_EQ(5, record_.count());
-
-  record_.Add(/* count */ 10);
-  EXPECT_EQ(15, record_.count());
-
-  record_.Add(/* count */ -2);
-  EXPECT_EQ(15, record_.count());
-
-  record_.Add(/* count */ kint32max);
-  EXPECT_EQ(kint32max, record_.count());
-
-  record_.Add(/* count */ 1);
-  EXPECT_EQ(kint32max, record_.count());
-}
-
-TEST_F(TaggedCounterTest, BadFileLocation) {
-  // Checks that the counter doesn't die badly if the file can't be
-  // created.
-  counter_.Init(kDoesNotExistFile,
-                /* reporter */ NULL, /* reporter_handle */ NULL);
-  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_);
-  base::DeleteFile(FilePath(kDoesNotExistFile), false);
-}
-
-TEST_F(TaggedCounterTest, Flush) {
-  counter_.Flush();
-  EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
-
-  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(/* 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(/* count */ 70);
-  counter_.Flush();
-  EXPECT_TRUE(AssertNoOrEmptyRecordFile());
-  EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
-}
-
-TEST_F(TaggedCounterTest, InitFromFile) {
-  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(/* 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(/* 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(/* count */ 60);
-  counter_.Init(kTestRecordFile, &Reporter, this);
-  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(/* 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(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 40);
-  EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 70);
-  EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
-
-  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(/* 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";
-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, 0, 2))
-      .Times(1)
-      .RetiresOnSaturation();
-  reporter_.Update(1, 0, 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_, 301);
-}
-
-class FrequencyCounterTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    tagged_counter_ = NULL;
-  }
-
-  void CheckInit(int32 cycle_duration);
-  void CheckCycleNumber(int32 cycle_duration);
-
-  FrequencyCounter frequency_counter_;
-  StrictMock<TaggedCounterMock>* tagged_counter_;
-
-  TaggedCounter::Reporter reporter_;
-};
-
-void FrequencyCounterTest::CheckInit(int32 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) {
-  CheckInit(100);
-}
-
-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(0, frequency_counter_.GetCycleNumber(0));
-}
-
-
-TEST_F(FrequencyCounterTest, GetCycleNumberForWeek) {
-  CheckCycleNumber(kSecondsPerWeek);
-}
-
-TEST_F(FrequencyCounterTest, GetCycleNumberForDay) {
-  CheckCycleNumber(kSecondsPerDay);
-}
-
-TEST_F(FrequencyCounterTest, UpdateInternal) {
-  CheckInit(kSecondsPerWeek);
-  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) {
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp
index 0a5ac48..2906a64 100644
--- a/metrics/metrics.gyp
+++ b/metrics/metrics.gyp
@@ -34,7 +34,7 @@
         ],
       },
       'sources': [
-        'counter.cc',
+        'persistent_integer.cc',
         'metrics_daemon.cc',
         'metrics_daemon_main.cc',
       ]
@@ -74,15 +74,6 @@
           ]
         },
         {
-          'target_name': 'counter_test',
-          'type': 'executable',
-          'includes': ['../common-mk/common_test.gypi'],
-          'sources': [
-            'counter.cc',
-            'counter_test.cc',
-          ]
-        },
-        {
           'target_name': 'timer_test',
           'type': 'executable',
           'includes': ['../common-mk/common_test.gypi'],
diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
index d6eb8a3..883c132 100644
--- a/metrics/metrics_daemon.cc
+++ b/metrics/metrics_daemon.cc
@@ -22,13 +22,12 @@
 #include <chromeos/dbus/service_constants.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
-#include "counter.h"
-
 using base::FilePath;
 using base::StringPrintf;
 using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
+using chromeos_metrics::PersistentInteger;
 using std::map;
 using std::string;
 using std::vector;
@@ -58,54 +57,7 @@
 
 const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
 static const char kUncleanShutdownDetectedFile[] =
-      "/var/run/unclean-shutdown-detected";
-
-// static metrics parameters
-const char MetricsDaemon::kMetricDailyUseTimeName[] =
-    "Logging.DailyUseTime";
-const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
-const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
-const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
-
-// crash interval metrics
-const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
-    "Logging.KernelCrashInterval";
-const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
-    "Logging.UncleanShutdownInterval";
-const char MetricsDaemon::kMetricUserCrashIntervalName[] =
-    "Logging.UserCrashInterval";
-
-const int MetricsDaemon::kMetricCrashIntervalMin = 1;
-const int MetricsDaemon::kMetricCrashIntervalMax =
-    4 * kSecondsPerWeek;
-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::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[] =
-    "Logging.UncleanShutdownsWeekly";
-const char MetricsDaemon::kMetricUserCrashesDailyName[] =
-    "Logging.UserCrashesDaily";
-const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
-    "Logging.UserCrashesWeekly";
-const int MetricsDaemon::kMetricCrashFrequencyMin = 1;
-const int MetricsDaemon::kMetricCrashFrequencyMax = 100;
-const int MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
+    "/var/run/unclean-shutdown-detected";
 
 // disk stats metrics
 
@@ -159,13 +111,6 @@
 const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
     "Platform.CpuFrequencyThermalScaling";
 
-// 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,
@@ -205,7 +150,6 @@
       stats_initial_time_(0) {}
 
 MetricsDaemon::~MetricsDaemon() {
-  DeleteFrequencyCounters();
 }
 
 double MetricsDaemon::GetActiveTime() {
@@ -219,14 +163,6 @@
   }
 }
 
-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) {
   base::AtExitManager at_exit_manager;
 
@@ -236,72 +172,22 @@
   if (CheckSystemCrash(kKernelCrashDetectedFile)) {
     ProcessKernelCrash();
   }
-  kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash());
 
   if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
     ProcessUncleanShutdown();
   }
 
+  // On OS version change, clear version stats (which are reported daily).
+  int32 version = GetOsVersionHash();
+  if (version_cycle_->Get() != version) {
+    version_cycle_->Set(version);
+    SendKernelCrashesCumulativeCountSample();
+    kernel_crashes_version_count_->Set(0);
+  }
+
   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::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;
@@ -328,31 +214,39 @@
   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);
+  daily_use_.reset(
+      new PersistentInteger("Logging.DailyUseTime"));
 
-  ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
-                                 &kernel_crash_interval_);
-  ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
-                                 &unclean_shutdown_interval_);
-  ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
-                                 &user_crash_interval_);
+  kernel_crash_interval_.reset(
+      new PersistentInteger("Logging.KernelCrashInterval"));
+  unclean_shutdown_interval_.reset(
+      new PersistentInteger("Logging.UncleanShutdownInterval"));
+  user_crash_interval_.reset(
+      new PersistentInteger("Logging.UserCrashInterval"));
 
-  DeleteFrequencyCounters();
-  ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
-  ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
-  ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
-  ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
-  ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
-  ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
-  ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
-  ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
+  any_crashes_daily_count_.reset(
+      new PersistentInteger("Logging.AnyCrashesDaily"));
+  any_crashes_weekly_count_.reset(
+      new PersistentInteger("Logging.AnyCrashesWeekly"));
+  user_crashes_daily_count_.reset(
+      new PersistentInteger("Logging.UserCrashesDaily"));
+  user_crashes_weekly_count_.reset(
+      new PersistentInteger("Logging.UserCrashesWeekly"));
+  kernel_crashes_daily_count_.reset(
+      new PersistentInteger("Logging.KernelCrashesDaily"));
+  kernel_crashes_weekly_count_.reset(
+      new PersistentInteger("Logging.KernelCrashesWeekly"));
+  kernel_crashes_version_count_.reset(
+      new PersistentInteger("Logging.KernelCrashesSinceUpdate"));
+  unclean_shutdowns_daily_count_.reset(
+      new PersistentInteger("Logging.UncleanShutdownsDaily"));
+  unclean_shutdowns_weekly_count_.reset(
+      new PersistentInteger("Logging.UncleanShutdownsWeekly"));
 
-  ConfigureCrashVersionReporter(kMetricKernelCrashesVersionName);
+  daily_cycle_.reset(new PersistentInteger("daily.cycle"));
+  weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
+  version_cycle_.reset(new PersistentInteger("version.cycle"));
 
   diskstats_path_ = diskstats_path;
   vmstats_path_ = vmstats_path;
@@ -504,6 +398,37 @@
   return kUnknownSessionState;
 }
 
+void MetricsDaemon::ReportStats(Time now) {
+  TimeDelta since_epoch = now - Time::UnixEpoch();
+  int day = since_epoch.InDays();
+  int week = day / 7;
+
+  if (daily_cycle_->Get() == day) {
+    // We did today already.
+    return;
+  }
+  daily_cycle_->Set(day);
+
+  // Daily stats.
+  SendCrashFrequencySample(any_crashes_daily_count_);
+  SendCrashFrequencySample(user_crashes_daily_count_);
+  SendCrashFrequencySample(kernel_crashes_daily_count_);
+  SendCrashFrequencySample(unclean_shutdowns_daily_count_);
+  SendKernelCrashesCumulativeCountSample();
+
+  if (weekly_cycle_->Get() == week) {
+    // We did this week already.
+    return;
+  }
+  weekly_cycle_->Set(week);
+
+  // Weekly stats.
+  SendCrashFrequencySample(any_crashes_weekly_count_);
+  SendCrashFrequencySample(user_crashes_weekly_count_);
+  SendCrashFrequencySample(kernel_crashes_weekly_count_);
+  SendCrashFrequencySample(unclean_shutdowns_weekly_count_);
+}
+
 void MetricsDaemon::SetUserActiveState(bool active, Time now) {
   DLOG(INFO) << "user: " << (active ? "active" : "inactive");
 
@@ -519,21 +444,12 @@
       seconds = static_cast<int>(since_active.InSeconds());
     }
   }
-  TimeDelta since_epoch = now - Time();
-  int day = since_epoch.InDays();
-  daily_use_->Update(day, seconds, 0);
-  user_crash_interval_->Update(0, seconds, 0);
-  kernel_crash_interval_->Update(0, seconds, 0);
+  daily_use_->Add(seconds);
+  user_crash_interval_->Add(seconds);
+  kernel_crash_interval_->Add(seconds);
 
-  // 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());
+  // Report daily and weekly stats as needed.
+  ReportStats(now);
 
   // Schedules a use monitor on inactive->active transitions and
   // unschedules it on active->inactive transitions.
@@ -553,12 +469,12 @@
   SetUserActiveState(user_active_, Time::Now());
 
   // Reports the active use time since the last crash and resets it.
-  user_crash_interval_->Flush();
+  SendCrashIntervalSample(user_crash_interval_);
 
-  frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
-  frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
-  frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
-  frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+  user_crashes_daily_count_->Add(1);
+  user_crashes_weekly_count_->Add(1);
 }
 
 void MetricsDaemon::ProcessKernelCrash() {
@@ -566,14 +482,14 @@
   SetUserActiveState(user_active_, Time::Now());
 
   // Reports the active use time since the last crash and resets it.
-  kernel_crash_interval_->Flush();
+  SendCrashIntervalSample(kernel_crash_interval_);
 
-  frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
-  frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
-  frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
-  frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+  kernel_crashes_daily_count_->Add(1);
+  kernel_crashes_weekly_count_->Add(1);
 
-  kernel_crash_version_counter_->Update(1, GetOsVersionHash());
+  kernel_crashes_version_count_->Add(1);
 }
 
 void MetricsDaemon::ProcessUncleanShutdown() {
@@ -581,12 +497,12 @@
   SetUserActiveState(user_active_, Time::Now());
 
   // Reports the active use time since the last crash and resets it.
-  unclean_shutdown_interval_->Flush();
+  SendCrashIntervalSample(unclean_shutdown_interval_);
 
-  frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
-  frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
-  frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
-  frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
+  unclean_shutdowns_daily_count_->Add(1);
+  unclean_shutdowns_weekly_count_->Add(1);
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
 }
 
 bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
@@ -830,7 +746,7 @@
   // scaled_freq is not the actual turbo frequency.  We indicate this situation
   // with a 101% value.
   int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
-  SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102);
+  SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
 }
 
 // static
@@ -868,29 +784,29 @@
   switch (stats_state_) {
     case kStatsShort:
       if (diskstats_success) {
-        SendMetric(kMetricReadSectorsShortName,
+        SendSample(kMetricReadSectorsShortName,
                    read_sectors_per_second,
                    1,
                    kMetricSectorsIOMax,
                    kMetricSectorsBuckets);
-        SendMetric(kMetricWriteSectorsShortName,
+        SendSample(kMetricWriteSectorsShortName,
                    write_sectors_per_second,
                    1,
                    kMetricSectorsIOMax,
                    kMetricSectorsBuckets);
       }
       if (vmstats_success) {
-        SendMetric(kMetricPageFaultsShortName,
+        SendSample(kMetricPageFaultsShortName,
                    page_faults_per_second,
                    1,
                    kMetricPageFaultsMax,
                    kMetricPageFaultsBuckets);
-        SendMetric(kMetricSwapInShortName,
+        SendSample(kMetricSwapInShortName,
                    swap_in_per_second,
                    1,
                    kMetricPageFaultsMax,
                    kMetricPageFaultsBuckets);
-        SendMetric(kMetricSwapOutShortName,
+        SendSample(kMetricSwapOutShortName,
                    swap_out_per_second,
                    1,
                    kMetricPageFaultsMax,
@@ -903,12 +819,12 @@
       break;
     case kStatsLong:
       if (diskstats_success) {
-        SendMetric(kMetricReadSectorsLongName,
+        SendSample(kMetricReadSectorsLongName,
                    read_sectors_per_second,
                    1,
                    kMetricSectorsIOMax,
                    kMetricSectorsBuckets);
-        SendMetric(kMetricWriteSectorsLongName,
+        SendSample(kMetricWriteSectorsLongName,
                    write_sectors_per_second,
                    1,
                    kMetricSectorsIOMax,
@@ -918,17 +834,17 @@
         write_sectors_ = write_sectors_now;
       }
       if (vmstats_success) {
-        SendMetric(kMetricPageFaultsLongName,
+        SendSample(kMetricPageFaultsLongName,
                    page_faults_per_second,
                    1,
                    kMetricPageFaultsMax,
                    kMetricPageFaultsBuckets);
-        SendMetric(kMetricSwapInLongName,
+        SendSample(kMetricSwapInLongName,
                    swap_in_per_second,
                    1,
                    kMetricPageFaultsMax,
                    kMetricPageFaultsBuckets);
-        SendMetric(kMetricSwapOutLongName,
+        SendSample(kMetricSwapOutLongName,
                    swap_out_per_second,
                    1,
                    kMetricPageFaultsMax,
@@ -1018,11 +934,11 @@
       case kMeminfoOp_HistPercent:
         // report value as percent of total memory
         percent = fields[i].value * 100 / total_memory;
-        SendLinearMetric(metrics_name, percent, 100, 101);
+        SendLinearSample(metrics_name, percent, 100, 101);
         break;
       case kMeminfoOp_HistLog:
         // report value in kbytes, log scale, 4Gb max
-        SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
+        SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
         break;
       case kMeminfoOp_SwapTotal:
         swap_total = fields[i].value;
@@ -1034,8 +950,8 @@
   if (swap_total > 0) {
     int swap_used = swap_total - swap_free;
     int swap_used_percent = swap_used * 100 / swap_total;
-    SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
-    SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
+    SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
+    SendLinearSample("Platform.MeminfoSwapUsedPercent", swap_used_percent,
                      100, 101);
   }
   return true;
@@ -1142,7 +1058,7 @@
   }
   string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
                                            memuse_interval_index_);
-  SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
+  SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
                    100, 101);
   return true;
 }
@@ -1154,20 +1070,49 @@
 
   MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
   int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
-  daemon->SendMetric(kMetricDailyUseTimeName, minutes,
-                     kMetricDailyUseTimeMin,
-                     kMetricDailyUseTimeMax,
-                     kMetricDailyUseTimeBuckets);
+  daemon->SendSample("Logging.DailyUseTime",
+                     minutes,
+                     1,
+                     kMinutesPerDay,
+                     50);
 }
 
-void MetricsDaemon::SendMetric(const string& name, int sample,
+void MetricsDaemon::SendSample(const string& name, int sample,
                                int min, int max, int nbuckets) {
   DLOG(INFO) << "received metric: " << name << " " << sample << " "
              << min << " " << max << " " << nbuckets;
   metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
 }
 
-void MetricsDaemon::SendLinearMetric(const string& name, int sample,
+void MetricsDaemon::SendKernelCrashesCumulativeCountSample() {
+  // Report the number of crashes for this OS version, but don't clear the
+  // counter.  It is cleared elsewhere on version change.
+  SendSample(kernel_crashes_version_count_->Name(),
+             kernel_crashes_version_count_->Get(),
+             1,                        // value of first bucket
+             500,                      // value of last bucket
+             100);                     // number of buckets
+}
+
+void MetricsDaemon::SendCrashIntervalSample(
+    const scoped_ptr<PersistentInteger>& interval) {
+  SendSample(interval->Name(),
+             interval->GetAndClear(),
+             1,                        // value of first bucket
+             4 * kSecondsPerWeek,      // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendCrashFrequencySample(
+    const scoped_ptr<PersistentInteger>& frequency) {
+  SendSample(frequency->Name(),
+             frequency->GetAndClear(),
+             1,                        // value of first bucket
+             100,                      // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendLinearSample(const string& name, int sample,
                                      int max, int nbuckets) {
   DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
              << max << " " << nbuckets;
diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h
index 6d8f81b..e4bf853 100644
--- a/metrics/metrics_daemon.h
+++ b/metrics/metrics_daemon.h
@@ -15,13 +15,9 @@
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "metrics_library.h"
+#include "persistent_integer.h"
 
-namespace chromeos_metrics {
-class FrequencyCounter;
-class VersionCounter;
-class TaggedCounter;
-class TaggedCounterReporter;
-}
+using chromeos_metrics::PersistentInteger;
 
 class MetricsDaemon {
 
@@ -66,7 +62,7 @@
   FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval);
   FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval);
   FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged);
-  FRIEND_TEST(MetricsDaemonTest, SendMetric);
+  FRIEND_TEST(MetricsDaemonTest, SendSample);
   FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics);
   FRIEND_TEST(MetricsDaemonTest, SessionStateChanged);
   FRIEND_TEST(MetricsDaemonTest, SetUserActiveState);
@@ -128,37 +124,7 @@
     uint64_t swap_out_;       // pages swapped out
   };
 
-  typedef std::map<std::string, chromeos_metrics::FrequencyCounter*>
-    FrequencyCounters;
-
   // Metric parameters.
-  static const char kMetricAnyCrashesDailyName[];
-  static const char kMetricAnyCrashesWeeklyName[];
-  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[];
-  static const char kMetricUserCrashesDailyName[];
-  static const char kMetricUserCrashesWeeklyName[];
-  static const char kMetricUserCrashIntervalName[];
   static const char kMetricReadSectorsLongName[];
   static const char kMetricReadSectorsShortName[];
   static const char kMetricWriteSectorsLongName[];
@@ -189,23 +155,6 @@
   // Returns the active time since boot (uptime minus sleep time) in seconds.
   double GetActiveTime();
 
-  // 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);
-
-  // 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);
-
   // Creates the event loop and enters it.
   void Loop();
 
@@ -285,15 +234,26 @@
   // 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,
+  void SendSample(const std::string& name, int sample,
                   int min, int max, int nbuckets);
 
   // Sends a linear histogram sample to Chrome for transport to UMA. See
   // MetricsLibrary::SendToUMA in metrics_library.h for a description of the
   // arguments.
-  void SendLinearMetric(const std::string& name, int sample,
+  void SendLinearSample(const std::string& name, int sample,
                         int max, int nbuckets);
 
+  // Sends a histogram sample with the total number of kernel crashes since the
+  // last version update.
+  void SendKernelCrashesCumulativeCountSample();
+
+  // Sends a sample representing a time interval between two crashes of the
+  // same type.
+  void SendCrashIntervalSample(const scoped_ptr<PersistentInteger>& interval);
+
+  // Sends a sample representing a frequency of crashes of some type.
+  void SendCrashFrequencySample(const scoped_ptr<PersistentInteger>& frequency);
+
   // Initializes vm and disk stats reporting.
   void StatsReporterInit();
 
@@ -361,6 +321,9 @@
   // Reads an integer CPU frequency value from sysfs.
   bool ReadFreqToInt(const std::string& sysfs_file_name, int* value);
 
+  // Report UMA stats when cycles (daily or weekly) have changed.
+  void ReportStats(base::Time now);
+
   // Reads the current OS version from /etc/lsb-release and hashes it
   // to a unsigned 32-bit int.
   uint32 GetOsVersionHash();
@@ -391,26 +354,6 @@
   // the timestamp.
   base::Time user_active_last_;
 
-  // Daily active use time in seconds.
-  scoped_ptr<chromeos_metrics::TaggedCounter> daily_use_;
-
-  // Active use time between user-space process crashes.
-  scoped_ptr<chromeos_metrics::TaggedCounterReporter> user_crash_interval_;
-
-  // Active use time between kernel crashes.
-  scoped_ptr<chromeos_metrics::TaggedCounterReporter> kernel_crash_interval_;
-
-  // Active use time between unclean shutdowns crashes.
-  scoped_ptr<chromeos_metrics::TaggedCounterReporter>
-      unclean_shutdown_interval_;
-
-  // 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_;
@@ -432,6 +375,27 @@
   StatsState stats_state_;
   double stats_initial_time_;
 
+  // Persistent counters for crash statistics.
+  scoped_ptr<PersistentInteger> daily_cycle_;
+  scoped_ptr<PersistentInteger> weekly_cycle_;
+  scoped_ptr<PersistentInteger> version_cycle_;
+
+  scoped_ptr<PersistentInteger> daily_use_;
+
+  scoped_ptr<PersistentInteger> user_crash_interval_;
+  scoped_ptr<PersistentInteger> kernel_crash_interval_;
+  scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
+
+  scoped_ptr<PersistentInteger> any_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> any_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> user_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> user_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
+  scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
+  scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
+
   std::string diskstats_path_;
   std::string vmstats_path_;
   std::string scaling_max_freq_path_;
diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc
index 255f260..aea0f4b 100644
--- a/metrics/metrics_daemon_test.cc
+++ b/metrics/metrics_daemon_test.cc
@@ -13,25 +13,21 @@
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
-#include "counter_mock.h"
 #include "metrics_daemon.h"
 #include "metrics_library_mock.h"
+#include "persistent_integer_mock.h"
 
 using base::FilePath;
 using base::StringPrintf;
 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 std::string;
 using std::vector;
 using ::testing::_;
 using ::testing::Return;
 using ::testing::StrictMock;
 using ::testing::AtLeast;
+using chromeos_metrics::PersistentIntegerMock;
 
 static const int kSecondsPerDay = 24 * 60 * 60;
 
@@ -61,9 +57,6 @@
 class MetricsDaemonTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    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] = base::StringPrintf(kFakeDiskStatsFormat,
                                            kFakeReadSectors[0],
                                            kFakeWriteSectors[0]);
@@ -74,64 +67,10 @@
     CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 10000000);
     CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 10000000);
 
+    chromeos_metrics::PersistentInteger::SetTestingMode(true);
     daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath,
         kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath);
 
-    // 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());
-    EXPECT_TRUE(NULL != daemon_.user_crash_interval_.get());
-
-    // Allocates mock counter and transfers ownership.
-    daily_use_ = new StrictMock<TaggedCounterMock>();
-    daemon_.daily_use_.reset(daily_use_);
-    kernel_crash_interval_ = new StrictMock<TaggedCounterReporterMock>();
-    daemon_.kernel_crash_interval_.reset(kernel_crash_interval_);
-    user_crash_interval_ = new StrictMock<TaggedCounterReporterMock>();
-    daemon_.user_crash_interval_.reset(user_crash_interval_);
-    unclean_shutdown_interval_ = new StrictMock<TaggedCounterReporterMock>();
-    daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_);
-
-    // 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());
     EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_);
@@ -139,6 +78,24 @@
 
     base::DeleteFile(FilePath(kTestDir), true);
     base::CreateDirectory(FilePath(kTestDir));
+
+    // Replace original persistent values with mock ones.
+    daily_use_mock_ =
+        new StrictMock<PersistentIntegerMock>("1.mock");
+    daemon_.daily_use_.reset(daily_use_mock_);
+
+    kernel_crash_interval_mock_ =
+        new StrictMock<PersistentIntegerMock>("2.mock");
+    daemon_.kernel_crash_interval_.reset(kernel_crash_interval_mock_);
+
+    user_crash_interval_mock_ =
+        new StrictMock<PersistentIntegerMock>("3.mock");
+    daemon_.user_crash_interval_.reset(user_crash_interval_mock_);
+
+    unclean_shutdown_interval_mock_ =
+        new StrictMock<PersistentIntegerMock>("4.mock");
+    daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_);
+
   }
 
   virtual void TearDown() {
@@ -147,57 +104,37 @@
     EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath));
   }
 
-  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) {
-    EXPECT_CALL(*daily_use_, Update(daily_tag, count, 0))
+  // specified count will be added.
+  void ExpectActiveUseUpdate(int count) {
+    EXPECT_CALL(*daily_use_mock_, Add(count))
         .Times(1)
         .RetiresOnSaturation();
-    EXPECT_CALL(*kernel_crash_interval_, Update(0, count, 0))
+    EXPECT_CALL(*kernel_crash_interval_mock_, Add(count))
         .Times(1)
         .RetiresOnSaturation();
-    EXPECT_CALL(*user_crash_interval_, Update(0, count, 0))
+    EXPECT_CALL(*user_crash_interval_mock_, Add(count))
         .Times(1)
         .RetiresOnSaturation();
-    ExpectFrequencyFlushCalls();
   }
 
-  // Adds active use aggregation counters update expectations that
-  // ignore the update arguments.
+  // As above, but ignore values of counter updates.
   void IgnoreActiveUseUpdate() {
-    EXPECT_CALL(*daily_use_, Update(_, _, _))
+    EXPECT_CALL(*daily_use_mock_, Add(_))
         .Times(1)
         .RetiresOnSaturation();
-    EXPECT_CALL(*kernel_crash_interval_, Update(_, _, _))
+    EXPECT_CALL(*kernel_crash_interval_mock_, Add(_))
         .Times(1)
         .RetiresOnSaturation();
-    EXPECT_CALL(*user_crash_interval_, Update(_, _, _))
+    EXPECT_CALL(*user_crash_interval_mock_, Add(_))
         .Times(1)
         .RetiresOnSaturation();
-    ExpectFrequencyFlushCalls();
   }
 
   // Adds a metrics library mock expectation that the specified metric
   // will be generated.
-  void ExpectMetric(const string& name, int sample,
-                    int min, int max, int buckets) {
-    EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, min, max, buckets))
+  void ExpectSample(int sample) {
+    EXPECT_CALL(metrics_lib_, SendToUMA(_, sample, _, _, _))
         .Times(1)
         .WillOnce(Return(true))
         .RetiresOnSaturation();
@@ -205,11 +142,8 @@
 
   // Adds a metrics library mock expectation that the specified daily
   // use time metric will be generated.
-  void ExpectDailyUseTimeMetric(int sample) {
-    ExpectMetric(MetricsDaemon::kMetricDailyUseTimeName, sample,
-                 MetricsDaemon::kMetricDailyUseTimeMin,
-                 MetricsDaemon::kMetricDailyUseTimeMax,
-                 MetricsDaemon::kMetricDailyUseTimeBuckets);
+  void ExpectDailyUseTimeSample(int sample) {
+    ExpectSample(sample);
   }
 
   // Converts from seconds to a Time object.
@@ -247,12 +181,6 @@
     dbus_message_unref(msg);
   }
 
-  // Gets the frequency counter for the given name.
-  FrequencyCounterMock& GetFrequencyMock(const char* histogram_name) {
-    return *static_cast<FrequencyCounterMock*>(
-        daemon_.frequency_counters_[histogram_name]);
-  }
-
   // Creates or overwrites an input file containing fake disk stats.
   void CreateFakeDiskStatsFile(const char* fake_stats) {
     if (unlink(kFakeDiskStatsPath) < 0) {
@@ -277,17 +205,13 @@
   // The MetricsDaemon under test.
   MetricsDaemon daemon_;
 
-  // Metrics library mock. It's a strict mock so that all unexpected
-  // metric generation calls are marked as failures.
+  // Mocks. They are strict mock so that all unexpected
+  // calls are marked as failures.
   StrictMock<MetricsLibraryMock> metrics_lib_;
-
-  // Counter mocks. They are strict mocks so that all unexpected
-  // 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<TaggedCounterReporterMock>* kernel_crash_interval_;
-  StrictMock<TaggedCounterReporterMock>* user_crash_interval_;
-  StrictMock<TaggedCounterReporterMock>* unclean_shutdown_interval_;
+  StrictMock<PersistentIntegerMock>* daily_use_mock_;
+  StrictMock<PersistentIntegerMock>* kernel_crash_interval_mock_;
+  StrictMock<PersistentIntegerMock>* user_crash_interval_mock_;
+  StrictMock<PersistentIntegerMock>* unclean_shutdown_interval_mock_;
 };
 
 TEST_F(MetricsDaemonTest, CheckSystemCrash) {
@@ -305,10 +229,10 @@
 }
 
 TEST_F(MetricsDaemonTest, ReportDailyUse) {
-  ExpectDailyUseTimeMetric(/* sample */ 2);
+  ExpectDailyUseTimeSample(/* sample */ 2);
   MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 90);
 
-  ExpectDailyUseTimeMetric(/* sample */ 1);
+  ExpectDailyUseTimeSample(/* sample */ 1);
   MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 89);
 
   // There should be no metrics generated for the calls below.
@@ -335,6 +259,9 @@
 }
 
 TEST_F(MetricsDaemonTest, MessageFilter) {
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0));
+
   DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
   DBusHandlerResult res =
       MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_);
@@ -342,25 +269,6 @@
   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();
   vector<string> signal_args;
   msg = NewDBusSignalString("/",
                             "org.chromium.CrashReporter",
@@ -421,13 +329,16 @@
 }
 
 TEST_F(MetricsDaemonTest, PowerStateChanged) {
-  ExpectActiveUseUpdate(7, 0);
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0));
+
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ true,
                              TestTime(7 * kSecondsPerDay + 15));
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(7 * kSecondsPerDay + 15), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(7, 30);
+  ExpectActiveUseUpdate(30);
   daemon_.PowerStateChanged("mem", TestTime(7 * kSecondsPerDay + 45));
   EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_);
   EXPECT_FALSE(daemon_.user_active_);
@@ -438,82 +349,36 @@
   EXPECT_FALSE(daemon_.user_active_);
   EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(7, 0);
+  ExpectActiveUseUpdate(0);
   daemon_.PowerStateChanged("otherstate", TestTime(7 * kSecondsPerDay + 185));
   EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_);
   EXPECT_FALSE(daemon_.user_active_);
   EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_);
 }
 
-TEST_F(MetricsDaemonTest, ProcessKernelCrash) {
-  IgnoreActiveUseUpdate();
-  EXPECT_CALL(*kernel_crash_interval_, Flush())
-      .Times(1)
-      .RetiresOnSaturation();
-  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();
-}
-
-TEST_F(MetricsDaemonTest, ProcessUncleanShutdown) {
-  IgnoreActiveUseUpdate();
-  EXPECT_CALL(*unclean_shutdown_interval_, Flush())
-      .Times(1)
-      .RetiresOnSaturation();
-  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();
-}
-
-TEST_F(MetricsDaemonTest, ProcessUserCrash) {
-  IgnoreActiveUseUpdate();
-  EXPECT_CALL(*user_crash_interval_, Flush())
-      .Times(1)
-      .RetiresOnSaturation();
-  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();
-}
-
-TEST_F(MetricsDaemonTest, SendMetric) {
-  ExpectMetric("Dummy.Metric", 3, 1, 100, 50);
-  daemon_.SendMetric("Dummy.Metric", /* sample */ 3,
+TEST_F(MetricsDaemonTest, SendSample) {
+  ExpectSample(3);
+  daemon_.SendSample("Dummy.Metric", /* sample */ 3,
                      /* min */ 1, /* max */ 100, /* buckets */ 50);
 }
 
 TEST_F(MetricsDaemonTest, SessionStateChanged) {
-  ExpectActiveUseUpdate(15, 0);
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0));
+
+  ExpectActiveUseUpdate(0);
   daemon_.SessionStateChanged("started", TestTime(15 * kSecondsPerDay + 20));
   EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_);
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(15 * kSecondsPerDay + 20), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(15, 130);
+  ExpectActiveUseUpdate(130);
   daemon_.SessionStateChanged("stopped", TestTime(15 * kSecondsPerDay + 150));
   EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_);
   EXPECT_FALSE(daemon_.user_active_);
   EXPECT_EQ(TestTime(15 * kSecondsPerDay + 150), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(15, 0);
+  ExpectActiveUseUpdate(0);
   daemon_.SessionStateChanged("otherstate",
                               TestTime(15 * kSecondsPerDay + 300));
   EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_);
@@ -522,31 +387,34 @@
 }
 
 TEST_F(MetricsDaemonTest, SetUserActiveState) {
-  ExpectActiveUseUpdate(5, 0);
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0));
+
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ false,
                              TestTime(5 * kSecondsPerDay + 10));
   EXPECT_FALSE(daemon_.user_active_);
   EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(6, 0);
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ true,
                              TestTime(6 * kSecondsPerDay + 20));
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(6 * kSecondsPerDay + 20), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(6, 100);
+  ExpectActiveUseUpdate(100);
   daemon_.SetUserActiveState(/* active */ true,
                              TestTime(6 * kSecondsPerDay + 120));
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(6 * kSecondsPerDay + 120), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(6, 110);
+  ExpectActiveUseUpdate(110);
   daemon_.SetUserActiveState(/* active */ false,
                              TestTime(6 * kSecondsPerDay + 230));
   EXPECT_FALSE(daemon_.user_active_);
   EXPECT_EQ(TestTime(6 * kSecondsPerDay + 230), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(6, 0);
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ false,
                              TestTime(6 * kSecondsPerDay + 260));
   EXPECT_FALSE(daemon_.user_active_);
@@ -554,31 +422,28 @@
 }
 
 TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) {
-  ExpectActiveUseUpdate(10, 0);
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0));
+
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ true,
                              TestTime(10 * kSecondsPerDay + 500));
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(10 * kSecondsPerDay + 500), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(10, 0);
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ true,
                              TestTime(10 * kSecondsPerDay + 300));
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(10 * kSecondsPerDay + 300), daemon_.user_active_last_);
 
-  ExpectActiveUseUpdate(10, 0);
+  ExpectActiveUseUpdate(0);
   daemon_.SetUserActiveState(/* active */ true,
                              TestTime(10 * kSecondsPerDay + 1000));
   EXPECT_TRUE(daemon_.user_active_);
   EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_);
 }
 
-TEST_F(MetricsDaemonTest, GetHistogramPath) {
-  EXPECT_EQ("/var/log/metrics/Logging.AnyCrashesDaily",
-            daemon_.GetHistogramPath(
-                MetricsDaemon::kMetricAnyCrashesDailyName).value());
-}
-
 TEST_F(MetricsDaemonTest, ReportDiskStats) {
   long int read_sectors_now, write_sectors_now;
 
diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc
new file mode 100644
index 0000000..c6ccf0e
--- /dev/null
+++ b/metrics/persistent_integer.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "persistent_integer.h"
+
+#include <fcntl.h>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+
+#include "metrics_library.h"
+
+namespace chromeos_metrics {
+
+// The directory for the persistent storage.
+const char* const kBackingFilesDirectory = "/var/log/metrics/";
+
+// Static class member instantiation.
+bool PersistentInteger::testing_ = false;
+
+PersistentInteger::PersistentInteger(const std::string& name) :
+      value_(0),
+      version_(kVersion),
+      name_(name),
+      synced_(false) {
+  if (testing_) {
+    backing_file_name_ = name_;
+  } else {
+    backing_file_name_ = kBackingFilesDirectory + name_;
+  }
+}
+
+PersistentInteger::~PersistentInteger() {}
+
+void PersistentInteger::Set(int64 value) {
+  value_ = value;
+  Write(value);
+}
+
+int64 PersistentInteger::Get() {
+  // If not synced, then read.  If the read fails, it's a good idea to write.
+  if (!synced_ && !Read())
+    Write(value_);
+  return value_;
+}
+
+int64 PersistentInteger::GetAndClear() {
+  int64 v = Get();
+  Set(0);
+  return v;
+}
+
+void PersistentInteger::Add(int64 x) {
+  Set(Get() + x);
+}
+
+void PersistentInteger::Write(int64 value) {
+  int fd = HANDLE_EINTR(open(backing_file_name_.c_str(),
+                             O_WRONLY | O_CREAT | O_TRUNC,
+                             S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH));
+  PCHECK(fd >= 0) << "cannot open " << backing_file_name_ << " for writing";
+  PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) ==
+          sizeof(version_)) &&
+         (HANDLE_EINTR(write(fd, &value_, sizeof(value_))) ==
+          sizeof(value_)))
+      << "cannot write to " << backing_file_name_;
+  close(fd);
+  synced_ = true;
+}
+
+bool PersistentInteger::Read() {
+  int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_RDONLY));
+  if (fd < 0) {
+    PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading";
+    return false;
+  }
+  int32 version;
+  int64 value;
+  bool read_succeeded = false;
+  if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) &&
+      version == version_ &&
+      HANDLE_EINTR(read(fd, &value, sizeof(value))) == sizeof(value)) {
+    value_ = value;
+    read_succeeded = true;
+    synced_ = true;
+  }
+  close(fd);
+  return read_succeeded;
+}
+
+void PersistentInteger::SetTestingMode(bool testing) {
+  testing_ = testing;
+}
+
+
+}  // namespace chromeos_metrics
diff --git a/metrics/persistent_integer.h b/metrics/persistent_integer.h
new file mode 100644
index 0000000..7d920b5
--- /dev/null
+++ b/metrics/persistent_integer.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef METRICS_PERSISTENT_INTEGER_H_
+#define METRICS_PERSISTENT_INTEGER_H_
+
+#include <base/basictypes.h>
+#include <string>
+
+namespace chromeos_metrics {
+
+// PersistentIntegers is a named 64-bit integer value backed by a file.
+// The in-memory value acts as a write-through cache of the file value.
+// If the backing file doesn't exist or has bad content, the value is 0.
+
+class PersistentInteger {
+ public:
+  PersistentInteger(const std::string& name);
+
+  // Virtual only because of mock.
+  virtual ~PersistentInteger();
+
+  // Sets the value.  This writes through to the backing file.
+  void Set(int64 v);
+
+  // Gets the value.  May sync from backing file first.
+  int64 Get();
+
+  // Returns the name of the object.
+  std::string Name() { return name_; }
+
+  // Convenience function for Get() followed by Set(0).
+  int64 GetAndClear();
+
+  // Convenience function for v = Get, Set(v + x).
+  // Virtual only because of mock.
+  virtual void Add(int64 x);
+
+  // After calling with |testing| = true, changes some behavior for the purpose
+  // of testing.  For instance: instances created while testing use the current
+  // directory for the backing files.
+  static void SetTestingMode(bool testing);
+
+ private:
+  static const int kVersion = 1001;
+
+  // Writes |value| to the backing file, creating it if necessary.
+  void Write(int64 value);
+
+  // Reads the value from the backing file, stores it in |value_|, and returns
+  // true if the backing file is valid.  Returns false otherwise.
+  bool Read();
+
+  int64 value_;
+  int32 version_;
+  std::string name_;
+  std::string backing_file_name_;
+  bool synced_;
+  static bool testing_;
+};
+
+}
+
+#endif  // METRICS_PERSISTENT_INTEGER_H_
diff --git a/metrics/persistent_integer_mock.h b/metrics/persistent_integer_mock.h
new file mode 100644
index 0000000..46570f9
--- /dev/null
+++ b/metrics/persistent_integer_mock.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef METRICS_PERSISTENT_INTEGER_MOCK_H_
+#define METRICS_PERSISTENT_INTEGER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "persistent_integer.h"
+
+namespace chromeos_metrics {
+
+class PersistentIntegerMock : public PersistentInteger {
+ public:
+    PersistentIntegerMock(const std::string& name) : PersistentInteger(name) {}
+    MOCK_METHOD1(Add, void(int64 count));
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_PERSISTENT_INTEGER_MOCK_H_