summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-22 03:42:12 +0000
committerkaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-22 03:42:12 +0000
commit2f7d9cdf62cb6729838a9c67e1a6efea7c739302 (patch)
tree01b27817539910ecffacfceb0f3b27b6d7deb092
parent00b8ae882e96f41c41c27d3070897f02dd75d30b (diff)
downloadchromium_src-2f7d9cdf62cb6729838a9c67e1a6efea7c739302.zip
chromium_src-2f7d9cdf62cb6729838a9c67e1a6efea7c739302.tar.gz
chromium_src-2f7d9cdf62cb6729838a9c67e1a6efea7c739302.tar.bz2
SampleSet -> HistogramSamples which can be reused by SparseHistogram
BUG=139612 Review URL: https://chromiumcodereview.appspot.com/10829466 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158166 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base.gyp1
-rw-r--r--base/base.gypi4
-rw-r--r--base/metrics/bucket_ranges.h2
-rw-r--r--base/metrics/histogram.cc228
-rw-r--r--base/metrics/histogram.h98
-rw-r--r--base/metrics/histogram_flattener.h6
-rw-r--r--base/metrics/histogram_samples.cc126
-rw-r--r--base/metrics/histogram_samples.h89
-rw-r--r--base/metrics/histogram_snapshot_manager.cc89
-rw-r--r--base/metrics/histogram_snapshot_manager.h25
-rw-r--r--base/metrics/histogram_unittest.cc80
-rw-r--r--base/metrics/sample_vector.cc160
-rw-r--r--base/metrics/sample_vector.h81
-rw-r--r--base/metrics/sample_vector_unittest.cc265
-rw-r--r--chrome/browser/chrome_browser_application_mac_unittest.mm17
-rw-r--r--chrome/browser/net/http_pipelining_compatibility_client_unittest.cc70
-rw-r--r--chrome/browser/protector/default_search_provider_change_browsertest.cc8
-rw-r--r--chrome/common/metrics/metrics_log_base.cc49
-rw-r--r--chrome/common/metrics/metrics_log_base.h6
-rw-r--r--chrome/common/metrics/metrics_service_base.cc2
-rw-r--r--chrome/common/metrics/metrics_service_base.h6
-rw-r--r--chrome/test/base/uma_histogram_helper.cc59
-rw-r--r--chrome/test/base/uma_histogram_helper.h17
-rw-r--r--chrome_frame/metrics_service.h3
-rw-r--r--content/common/child_histogram_message_filter.cc4
-rw-r--r--content/common/child_histogram_message_filter.h6
-rw-r--r--net/cookies/cookie_monster_unittest.cc19
-rw-r--r--net/disk_cache/stats.cc10
-rw-r--r--net/disk_cache/stats.h6
-rw-r--r--net/disk_cache/stats_histogram.cc79
-rw-r--r--net/disk_cache/stats_histogram.h38
-rw-r--r--net/socket_stream/socket_stream_metrics_unittest.cc94
-rw-r--r--net/url_request/url_request_throttler_unittest.cc39
33 files changed, 1202 insertions, 584 deletions
diff --git a/base/base.gyp b/base/base.gyp
index b63f7b1..17fde6b 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -440,6 +440,7 @@
'message_loop_unittest.cc',
'message_pump_glib_unittest.cc',
'message_pump_libevent_unittest.cc',
+ 'metrics/sample_vector_unittest.cc',
'metrics/bucket_ranges_unittest.cc',
'metrics/field_trial_unittest.cc',
'metrics/histogram_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index c4e14ad..91f7273 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -252,6 +252,8 @@
'message_pump_default.h',
'message_pump_win.cc',
'message_pump_win.h',
+ 'metrics/sample_vector.cc',
+ 'metrics/sample_vector.h',
'metrics/bucket_ranges.cc',
'metrics/bucket_ranges.h',
'metrics/histogram.cc',
@@ -259,6 +261,8 @@
'metrics/histogram_base.cc',
'metrics/histogram_base.h',
'metrics/histogram_flattener.h',
+ 'metrics/histogram_samples.cc',
+ 'metrics/histogram_samples.h',
'metrics/histogram_snapshot_manager.cc',
'metrics/histogram_snapshot_manager.h',
'metrics/sparse_histogram.cc',
diff --git a/base/metrics/bucket_ranges.h b/base/metrics/bucket_ranges.h
index b77f52e..90b9047 100644
--- a/base/metrics/bucket_ranges.h
+++ b/base/metrics/bucket_ranges.h
@@ -26,7 +26,7 @@
namespace base {
-class BASE_EXPORT_PRIVATE BucketRanges {
+class BASE_EXPORT BucketRanges {
public:
typedef std::vector<HistogramBase::Sample> Ranges;
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index dbc76ef..9e490a0 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -34,104 +34,6 @@ typedef HistogramBase::Sample Sample;
// static
const size_t Histogram::kBucketCount_MAX = 16384u;
-Histogram::SampleSet::SampleSet(size_t size)
- : counts_(size, 0),
- sum_(0),
- redundant_count_(0) {}
-
-Histogram::SampleSet::SampleSet()
- : counts_(),
- sum_(0),
- redundant_count_(0) {}
-
-Histogram::SampleSet::~SampleSet() {}
-
-void Histogram::SampleSet::Resize(size_t size) {
- counts_.resize(size, 0);
-}
-
-void Histogram::SampleSet::Accumulate(Sample value, Count count,
- size_t index) {
- DCHECK(count == 1 || count == -1);
- counts_[index] += count;
- sum_ += count * value;
- redundant_count_ += count;
- DCHECK_GE(counts_[index], 0);
- DCHECK_GE(sum_, 0);
- DCHECK_GE(redundant_count_, 0);
-}
-
-Count Histogram::SampleSet::TotalCount() const {
- Count total = 0;
- for (Counts::const_iterator it = counts_.begin();
- it != counts_.end();
- ++it) {
- total += *it;
- }
- return total;
-}
-
-void Histogram::SampleSet::Add(const SampleSet& other) {
- DCHECK_EQ(counts_.size(), other.counts_.size());
- sum_ += other.sum_;
- redundant_count_ += other.redundant_count_;
- for (size_t index = 0; index < counts_.size(); ++index)
- counts_[index] += other.counts_[index];
-}
-
-void Histogram::SampleSet::Subtract(const SampleSet& other) {
- DCHECK_EQ(counts_.size(), other.counts_.size());
- // Note: Race conditions in snapshotting a sum may lead to (temporary)
- // negative values when snapshots are later combined (and deltas calculated).
- // As a result, we don't currently CHCEK() for positive values.
- sum_ -= other.sum_;
- redundant_count_ -= other.redundant_count_;
- for (size_t index = 0; index < counts_.size(); ++index) {
- counts_[index] -= other.counts_[index];
- DCHECK_GE(counts_[index], 0);
- }
-}
-
-bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
- pickle->WriteInt64(sum_);
- pickle->WriteInt64(redundant_count_);
- pickle->WriteUInt64(counts_.size());
-
- for (size_t index = 0; index < counts_.size(); ++index) {
- pickle->WriteInt(counts_[index]);
- }
-
- return true;
-}
-
-bool Histogram::SampleSet::Deserialize(PickleIterator* iter) {
- DCHECK_EQ(counts_.size(), 0u);
- DCHECK_EQ(sum_, 0);
- DCHECK_EQ(redundant_count_, 0);
-
- uint64 counts_size;
-
- if (!iter->ReadInt64(&sum_) ||
- !iter->ReadInt64(&redundant_count_) ||
- !iter->ReadUInt64(&counts_size)) {
- return false;
- }
-
- if (counts_size == 0)
- return false;
-
- int count = 0;
- for (uint64 index = 0; index < counts_size; ++index) {
- int i;
- if (!iter->ReadInt(&i))
- return false;
- counts_.push_back(i);
- count += i;
- }
- DCHECK_EQ(count, redundant_count_);
- return count == redundant_count_;
-}
-
// TODO(rtenneti): delete this code after debugging.
void CheckCorruption(const Histogram& histogram, bool new_histogram) {
const std::string& histogram_name = histogram.histogram_name();
@@ -245,24 +147,27 @@ void Histogram::InitializeBucketRanges(Sample minimum,
ranges->ResetChecksum();
}
-// static
void Histogram::Add(int value) {
+ DCHECK_EQ(0, ranges(0));
+ DCHECK_EQ(kSampleType_MAX, ranges(bucket_count_));
+
if (value > kSampleType_MAX - 1)
value = kSampleType_MAX - 1;
if (value < 0)
value = 0;
- size_t index = BucketIndex(value);
- DCHECK_GE(value, ranges(index));
- DCHECK_LT(value, ranges(index + 1));
- Accumulate(value, 1, index);
+ samples_->Accumulate(value, 1);
}
void Histogram::AddBoolean(bool value) {
DCHECK(false);
}
-void Histogram::AddSampleSet(const SampleSet& sample) {
- sample_.Add(sample);
+void Histogram::AddSamples(const HistogramSamples& samples) {
+ samples_->Add(samples);
+}
+
+bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
+ return samples_->AddFromPickle(iter);
}
void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
@@ -283,7 +188,7 @@ void Histogram::WriteAscii(string* output) const {
// static
string Histogram::SerializeHistogramInfo(const Histogram& histogram,
- const SampleSet& snapshot) {
+ const HistogramSamples& snapshot) {
DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type());
DCHECK(histogram.bucket_ranges()->HasValidChecksum());
@@ -296,10 +201,10 @@ string Histogram::SerializeHistogramInfo(const Histogram& histogram,
pickle.WriteInt(histogram.histogram_type());
pickle.WriteInt(histogram.flags());
- snapshot.Serialize(&pickle);
-
histogram.SerializeRanges(&pickle);
+ snapshot.Serialize(&pickle);
+
return string(static_cast<const char*>(pickle.data()), pickle.size());
}
@@ -318,7 +223,6 @@ bool Histogram::DeserializeHistogramInfo(const string& histogram_info) {
uint32 range_checksum;
int histogram_type;
int pickle_flags;
- SampleSet sample;
PickleIterator iter(pickle);
if (!iter.ReadString(&histogram_name) ||
@@ -327,8 +231,7 @@ bool Histogram::DeserializeHistogramInfo(const string& histogram_info) {
!iter.ReadUInt64(&bucket_count) ||
!iter.ReadUInt32(&range_checksum) ||
!iter.ReadInt(&histogram_type) ||
- !iter.ReadInt(&pickle_flags) ||
- !sample.Histogram::SampleSet::Deserialize(&iter)) {
+ !iter.ReadInt(&pickle_flags)) {
DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
return false;
}
@@ -382,23 +285,21 @@ bool Histogram::DeserializeHistogramInfo(const string& histogram_info) {
if (render_histogram->flags() & kIPCSerializationSourceFlag) {
DVLOG(1) << "Single process mode, histogram observed and not copied: "
<< histogram_name;
- } else {
- DCHECK_EQ(flags & render_histogram->flags(), flags);
- render_histogram->AddSampleSet(sample);
+ return true;
}
- return true;
+ DCHECK_EQ(flags & render_histogram->flags(), flags);
+ return render_histogram->AddSamplesFromPickle(&iter);
}
+// static
+const int Histogram::kCommonRaceBasedCountMismatch = 5;
-// Validate a sample and related histogram.
Histogram::Inconsistencies Histogram::FindCorruption(
- const SampleSet& snapshot) const {
+ const HistogramSamples& samples) const {
int inconsistencies = NO_INCONSISTENCIES;
Sample previous_range = -1; // Bottom range is always 0.
- int64 count = 0;
for (size_t index = 0; index < bucket_count(); ++index) {
- count += snapshot.counts(index);
int new_range = ranges(index);
if (previous_range >= new_range)
inconsistencies |= BUCKET_ORDER_ERROR;
@@ -408,20 +309,11 @@ Histogram::Inconsistencies Histogram::FindCorruption(
if (!bucket_ranges()->HasValidChecksum())
inconsistencies |= RANGE_CHECKSUM_ERROR;
- int64 delta64 = snapshot.redundant_count() - count;
+ int64 delta64 = samples.redundant_count() - samples.TotalCount();
if (delta64 != 0) {
int delta = static_cast<int>(delta64);
if (delta != delta64)
delta = INT_MAX; // Flag all giant errors as INT_MAX.
- // Since snapshots of histograms are taken asynchronously relative to
- // sampling (and snapped from different threads), it is pretty likely that
- // we'll catch a redundant count that doesn't match the sample count. We
- // allow for a certain amount of slop before flagging this as an
- // inconsistency. Even with an inconsistency, we'll snapshot it again (for
- // UMA in about a half hour, so we'll eventually get the data, if it was
- // not the result of a corruption. If histograms show that 1 is "too tight"
- // then we may try to use 2 or 3 for this slop value.
- const int kCommonRaceBasedCountMismatch = 5;
if (delta > 0) {
UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
if (delta > kCommonRaceBasedCountMismatch)
@@ -448,11 +340,10 @@ size_t Histogram::bucket_count() const {
return bucket_count_;
}
-// Do a safe atomic snapshot of sample data.
-// This implementation assumes we are on a safe single thread.
-void Histogram::SnapshotSample(SampleSet* sample) const {
- // Note locking not done in this version!!!
- *sample = sample_;
+scoped_ptr<SampleVector> Histogram::SnapshotSamples() const {
+ scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges()));
+ samples->Add(*samples_);
+ return samples.Pass();
}
bool Histogram::HasConstructionArguments(Sample minimum,
@@ -471,8 +362,10 @@ Histogram::Histogram(const string& name,
bucket_ranges_(ranges),
declared_min_(minimum),
declared_max_(maximum),
- bucket_count_(bucket_count),
- sample_(bucket_count) {}
+ bucket_count_(bucket_count) {
+ if (ranges)
+ samples_.reset(new SampleVector(ranges));
+}
Histogram::~Histogram() {
if (StatisticsRecorder::dump_on_exit()) {
@@ -519,31 +412,6 @@ bool Histogram::PrintEmptyBucket(size_t index) const {
return true;
}
-size_t Histogram::BucketIndex(Sample value) const {
- // Use simple binary search. This is very general, but there are better
- // approaches if we knew that the buckets were linearly distributed.
- DCHECK_LE(ranges(0), value);
- DCHECK_GT(ranges(bucket_count()), value);
- size_t under = 0;
- size_t over = bucket_count();
- size_t mid;
-
- do {
- DCHECK_GE(over, under);
- mid = under + (over - under)/2;
- if (mid == under)
- break;
- if (ranges(mid) <= value)
- under = mid;
- else
- over = mid;
- } while (true);
-
- DCHECK_LE(ranges(mid), value);
- CHECK_GT(ranges(mid+1), value);
- return mid;
-}
-
// Use the actual bucket widths (like a linear histogram) until the widths get
// over some transition value, and then use that transition width. Exponentials
// get so big so fast (and we don't expect to see a lot of entries in the large
@@ -567,12 +435,6 @@ const string Histogram::GetAsciiBucketRange(size_t i) const {
return result;
}
-// Update histogram data with new sample.
-void Histogram::Accumulate(Sample value, Count count, size_t index) {
- // Note locking not done in this version!!!
- sample_.Accumulate(value, count, index);
-}
-
//------------------------------------------------------------------------------
// Private methods
@@ -581,22 +443,21 @@ void Histogram::WriteAsciiImpl(bool graph_it,
string* output) const {
// Get local (stack) copies of all effectively volatile class data so that we
// are consistent across our output activities.
- SampleSet snapshot;
- SnapshotSample(&snapshot);
- Count sample_count = snapshot.TotalCount();
+ scoped_ptr<SampleVector> snapshot = SnapshotSamples();
+ Count sample_count = snapshot->TotalCount();
- WriteAsciiHeader(snapshot, sample_count, output);
+ WriteAsciiHeader(*snapshot, sample_count, output);
output->append(newline);
// Prepare to normalize graphical rendering of bucket contents.
double max_size = 0;
if (graph_it)
- max_size = GetPeakBucketSize(snapshot);
+ max_size = GetPeakBucketSize(*snapshot);
// Calculate space needed to print bucket range numbers. Leave room to print
// nearly the largest bucket range without sliding over the histogram.
size_t largest_non_empty_bucket = bucket_count() - 1;
- while (0 == snapshot.counts(largest_non_empty_bucket)) {
+ while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) {
if (0 == largest_non_empty_bucket)
break; // All buckets are empty.
--largest_non_empty_bucket;
@@ -605,7 +466,7 @@ void Histogram::WriteAsciiImpl(bool graph_it,
// Calculate largest print width needed for any of our bucket range displays.
size_t print_width = 1;
for (size_t i = 0; i < bucket_count(); ++i) {
- if (snapshot.counts(i)) {
+ if (snapshot->GetCountAtIndex(i)) {
size_t width = GetAsciiBucketRange(i).size() + 1;
if (width > print_width)
print_width = width;
@@ -616,7 +477,7 @@ void Histogram::WriteAsciiImpl(bool graph_it,
int64 past = 0;
// Output the actual histogram graph.
for (size_t i = 0; i < bucket_count(); ++i) {
- Count current = snapshot.counts(i);
+ Count current = snapshot->GetCountAtIndex(i);
if (!current && !PrintEmptyBucket(i))
continue;
remaining -= current;
@@ -624,9 +485,12 @@ void Histogram::WriteAsciiImpl(bool graph_it,
output->append(range);
for (size_t j = 0; range.size() + j < print_width + 1; ++j)
output->push_back(' ');
- if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
- while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
+ if (0 == current && i < bucket_count() - 1 &&
+ 0 == snapshot->GetCountAtIndex(i + 1)) {
+ while (i < bucket_count() - 1 &&
+ 0 == snapshot->GetCountAtIndex(i + 1)) {
++i;
+ }
output->append("... ");
output->append(newline);
continue; // No reason to plot emptiness.
@@ -641,17 +505,17 @@ void Histogram::WriteAsciiImpl(bool graph_it,
DCHECK_EQ(sample_count, past);
}
-double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
+double Histogram::GetPeakBucketSize(const SampleVector& samples) const {
double max = 0;
for (size_t i = 0; i < bucket_count() ; ++i) {
- double current_size = GetBucketSize(snapshot.counts(i), i);
+ double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
if (current_size > max)
max = current_size;
}
return max;
}
-void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
+void Histogram::WriteAsciiHeader(const SampleVector& samples,
Count sample_count,
string* output) const {
StringAppendF(output,
@@ -659,9 +523,9 @@ void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
histogram_name().c_str(),
sample_count);
if (0 == sample_count) {
- DCHECK_EQ(snapshot.sum(), 0);
+ DCHECK_EQ(samples.sum(), 0);
} else {
- double average = static_cast<float>(snapshot.sum()) / sample_count;
+ double average = static_cast<float>(samples.sum()) / sample_count;
StringAppendF(output, ", average = %.1f", average);
}
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index 6320db6..d86a397 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -68,8 +68,11 @@
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/sample_vector.h"
#include "base/time.h"
class Pickle;
@@ -338,8 +341,10 @@ class Lock;
//------------------------------------------------------------------------------
-class BooleanHistogram;
class BucketRanges;
+class SampleVector;
+
+class BooleanHistogram;
class CustomHistogram;
class Histogram;
class LinearHistogram;
@@ -383,57 +388,6 @@ class BASE_EXPORT Histogram : public HistogramBase {
};
//----------------------------------------------------------------------------
- // Statistic values, developed over the life of the histogram.
-
- class BASE_EXPORT SampleSet {
- public:
- explicit SampleSet(size_t size);
- SampleSet();
- ~SampleSet();
-
- void Resize(size_t size);
-
- // Accessor for histogram to make routine additions.
- void Accumulate(Sample value, Count count, size_t index);
-
- // Accessor methods.
- size_t size() const { return counts_.size(); }
- Count counts(size_t i) const { return counts_[i]; }
- Count TotalCount() const;
- int64 sum() const { return sum_; }
- int64 redundant_count() const { return redundant_count_; }
-
- // Arithmetic manipulation of corresponding elements of the set.
- void Add(const SampleSet& other);
- void Subtract(const SampleSet& other);
-
- bool Serialize(Pickle* pickle) const;
- bool Deserialize(PickleIterator* iter);
-
- protected:
- // Actual histogram data is stored in buckets, showing the count of values
- // that fit into each bucket.
- Counts counts_;
-
- // Save simple stats locally. Note that this MIGHT get done in base class
- // without shared memory at some point.
- int64 sum_; // sum of samples.
-
- private:
- // Allow tests to corrupt our innards for testing purposes.
- FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
-
- // To help identify memory corruption, we reduntantly save the number of
- // samples we've accumulated into all of our buckets. We can compare this
- // count to the sum of the counts in all buckets, and detect problems. Note
- // that due to races in histogram accumulation (if a histogram is indeed
- // updated on several threads simultaneously), the tallies might mismatch,
- // and also the snapshotting code may asynchronously get a mismatch (though
- // generally either race based mismatch cause is VERY rare).
- int64 redundant_count_;
- };
-
- //----------------------------------------------------------------------------
// For a valid histogram, input should follow these restrictions:
// minimum > 0 (if a minimum below 1 is specified, it will implicitly be
// normalized up to 1)
@@ -473,7 +427,8 @@ class BASE_EXPORT Histogram : public HistogramBase {
Add(static_cast<int>(time.InMilliseconds()));
}
- void AddSampleSet(const SampleSet& sample);
+ void AddSamples(const HistogramSamples& samples);
+ bool AddSamplesFromPickle(PickleIterator* iter);
// This method is an interface, used only by LinearHistogram.
virtual void SetRangeDescriptions(const DescriptionPair descriptions[]);
@@ -491,19 +446,28 @@ class BASE_EXPORT Histogram : public HistogramBase {
// Serialize the given snapshot of a Histogram into a String. Uses
// Pickle class to flatten the object.
static std::string SerializeHistogramInfo(const Histogram& histogram,
- const SampleSet& snapshot);
+ const HistogramSamples& snapshot);
// The following method accepts a list of pickled histograms and
// builds a histogram and updates shadow copy of histogram data in the
// browser process.
static bool DeserializeHistogramInfo(const std::string& histogram_info);
+ // This constant if for FindCorruption. Since snapshots of histograms are
+ // taken asynchronously relative to sampling, and our counting code currently
+ // does not prevent race conditions, it is pretty likely that we'll catch a
+ // redundant count that doesn't match the sample count. We allow for a
+ // certain amount of slop before flagging this as an inconsistency. Even with
+ // an inconsistency, we'll snapshot it again (for UMA in about a half hour),
+ // so we'll eventually get the data, if it was not the result of a corruption.
+ static const int kCommonRaceBasedCountMismatch;
+
// Check to see if bucket ranges, counts and tallies in the snapshot are
// consistent with the bucket ranges and checksums in our histogram. This can
// produce a false-alarm if a race occurred in the reading of the data during
// a SnapShot process, but should otherwise be false at all times (unless we
// have memory over-writes, or DRAM failures).
- virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const;
+ virtual Inconsistencies FindCorruption(const HistogramSamples& samples) const;
//----------------------------------------------------------------------------
// Accessors for factory constuction, serialization and testing.
@@ -517,7 +481,7 @@ class BASE_EXPORT Histogram : public HistogramBase {
// Snapshot the current complete set of sample data.
// Override with atomic/locked snapshot if needed.
- virtual void SnapshotSample(SampleSet* sample) const;
+ virtual scoped_ptr<SampleVector> SnapshotSamples() const;
virtual bool HasConstructionArguments(Sample minimum,
Sample maximum,
@@ -553,11 +517,6 @@ class BASE_EXPORT Histogram : public HistogramBase {
// Method to override to skip the display of the i'th bucket if it's empty.
virtual bool PrintEmptyBucket(size_t index) const;
- //----------------------------------------------------------------------------
- // Methods to override to create histogram with different bucket widths.
- //----------------------------------------------------------------------------
- // Find bucket to increment for sample value.
- virtual size_t BucketIndex(Sample value) const;
// Get normalized size, relative to the ranges(i).
virtual double GetBucketSize(Count current, size_t i) const;
@@ -566,12 +525,6 @@ class BASE_EXPORT Histogram : public HistogramBase {
// be a name (or string description) given to the bucket.
virtual const std::string GetAsciiBucketRange(size_t it) const;
- //----------------------------------------------------------------------------
- // Methods to override to create thread safe histogram.
- //----------------------------------------------------------------------------
- // Update all our internal data, including histogram
- virtual void Accumulate(Sample value, Count count, size_t index);
-
private:
// Allow tests to corrupt our innards for testing purposes.
FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptBucketBounds);
@@ -589,12 +542,13 @@ class BASE_EXPORT Histogram : public HistogramBase {
const std::string& newline,
std::string* output) const;
- // Find out how large the (graphically) the largest bucket will appear to be.
- double GetPeakBucketSize(const SampleSet& snapshot) const;
+ // Find out how large (graphically) the largest bucket will appear to be.
+ double GetPeakBucketSize(const SampleVector& samples) const;
// Write a common header message describing this histogram.
- void WriteAsciiHeader(const SampleSet& snapshot,
- Count sample_count, std::string* output) const;
+ void WriteAsciiHeader(const SampleVector& samples,
+ Count sample_count,
+ std::string* output) const;
// Write information about previous, current, and next buckets.
// Information such as cumulative percentage, etc.
@@ -620,7 +574,7 @@ class BASE_EXPORT Histogram : public HistogramBase {
// Finally, provide the state that changes with the addition of each new
// sample.
- SampleSet sample_;
+ scoped_ptr<SampleVector> samples_;
DISALLOW_COPY_AND_ASSIGN(Histogram);
};
diff --git a/base/metrics/histogram_flattener.h b/base/metrics/histogram_flattener.h
index 01a49fc..6dd169e 100644
--- a/base/metrics/histogram_flattener.h
+++ b/base/metrics/histogram_flattener.h
@@ -14,14 +14,16 @@
namespace base {
+class HistogramSamples;
+
// HistogramFlattener is an interface used by HistogramSnapshotManager, which
// handles the logistics of gathering up available histograms for recording.
// The implementors handle the exact lower level recording mechanism, or
// error report mechanism.
class BASE_EXPORT HistogramFlattener {
public:
- virtual void RecordDelta(const base::Histogram& histogram,
- const base::Histogram::SampleSet& snapshot) = 0;
+ virtual void RecordDelta(const Histogram& histogram,
+ const HistogramSamples& snapshot) = 0;
// Will be called each time a type of Inconsistenies is seen on a histogram,
// during inspections done internally in HistogramSnapshotManager class.
diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc
new file mode 100644
index 0000000..e375952
--- /dev/null
+++ b/base/metrics/histogram_samples.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/histogram_samples.h"
+
+#include "base/compiler_specific.h"
+#include "base/pickle.h"
+
+namespace base {
+
+namespace {
+
+class SampleCountPickleIterator : public SampleCountIterator {
+ public:
+ SampleCountPickleIterator(PickleIterator* iter);
+
+ virtual bool Done() const OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const OVERRIDE;
+ private:
+ PickleIterator* const iter_;
+
+ HistogramBase::Sample min_;
+ HistogramBase::Sample max_;
+ HistogramBase::Count count_;
+ bool is_done_;
+};
+
+SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter)
+ : iter_(iter),
+ is_done_(false) {
+ Next();
+}
+
+bool SampleCountPickleIterator::Done() const {
+ return is_done_;
+}
+
+void SampleCountPickleIterator::Next() {
+ DCHECK(!Done());
+ if (!iter_->ReadInt(&min_) ||
+ !iter_->ReadInt(&max_) ||
+ !iter_->ReadInt(&count_))
+ is_done_ = true;
+}
+
+void SampleCountPickleIterator::Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const {
+ DCHECK(!Done());
+ *min = min_;
+ *max = max_;
+ *count = count_;
+}
+
+} // namespace
+
+HistogramSamples::HistogramSamples() : sum_(0), redundant_count_(0) {}
+
+HistogramSamples::~HistogramSamples() {}
+
+void HistogramSamples::Add(const HistogramSamples& other) {
+ sum_ += other.sum();
+ redundant_count_ += other.redundant_count();
+ bool success = AddSubtractImpl(other.Iterator().get(), ADD);
+ DCHECK(success);
+}
+
+bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
+ int64 sum;
+ HistogramBase::Count redundant_count;
+
+ if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
+ return false;
+ sum_ += sum;
+ redundant_count_ += redundant_count;
+
+ SampleCountPickleIterator pickle_iter(iter);
+ return AddSubtractImpl(&pickle_iter, ADD);
+}
+
+void HistogramSamples::Subtract(const HistogramSamples& other) {
+ sum_ -= other.sum();
+ redundant_count_ -= other.redundant_count();
+ bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT);
+ DCHECK(success);
+}
+
+bool HistogramSamples::Serialize(Pickle* pickle) const {
+ if (!pickle->WriteInt64(sum_) || !pickle->WriteInt(redundant_count_))
+ return false;
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ for (scoped_ptr<SampleCountIterator> it = Iterator();
+ !it->Done();
+ it->Next()) {
+ it->Get(&min, &max, &count);
+ if (!pickle->WriteInt(min) ||
+ !pickle->WriteInt(max) ||
+ !pickle->WriteInt(count))
+ return false;
+ }
+ return true;
+}
+
+void HistogramSamples::IncreaseSum(int64 diff) {
+ sum_ += diff;
+}
+
+void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) {
+ redundant_count_ += diff;
+}
+
+SampleCountIterator::~SampleCountIterator() {}
+
+bool SampleCountIterator::GetBucketIndex(size_t* index) const {
+ DCHECK(!Done());
+ return false;
+}
+
+} // namespace base
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h
new file mode 100644
index 0000000..778b11d
--- /dev/null
+++ b/base/metrics/histogram_samples.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_METRICS_HISTOGRAM_SAMPLES_H_
+#define BASE_METRICS_HISTOGRAM_SAMPLES_H_
+
+#include "base/basictypes.h"
+#include "base/metrics/histogram_base.h"
+#include "base/memory/scoped_ptr.h"
+
+class Pickle;
+class PickleIterator;
+
+namespace base {
+
+class SampleCountIterator;
+
+// HistogramSamples is a container storing all samples of a histogram.
+class BASE_EXPORT HistogramSamples {
+ public:
+ HistogramSamples();
+ virtual ~HistogramSamples();
+
+ virtual void Accumulate(HistogramBase::Sample value,
+ HistogramBase::Count count) = 0;
+ virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0;
+ virtual HistogramBase::Count TotalCount() const = 0;
+
+ virtual void Add(const HistogramSamples& other);
+
+ // Add from serialized samples.
+ virtual bool AddFromPickle(PickleIterator* iter);
+
+ virtual void Subtract(const HistogramSamples& other);
+
+ virtual scoped_ptr<SampleCountIterator> Iterator() const = 0;
+ virtual bool Serialize(Pickle* pickle) const;
+
+ // Accessor fuctions.
+ int64 sum() const { return sum_; }
+ HistogramBase::Count redundant_count() const { return redundant_count_; }
+
+ protected:
+ // Based on |instruction| type, add or subtract sample counts data from the
+ // iterator.
+ enum Instruction { ADD, SUBTRACT };
+ virtual bool AddSubtractImpl(SampleCountIterator* iter,
+ Instruction instruction) = 0;
+
+ void IncreaseSum(int64 diff);
+ void IncreaseRedundantCount(HistogramBase::Count diff);
+
+ private:
+ int64 sum_;
+
+ // |redundant_count_| helps identify memory corruption. It redundantly stores
+ // the total number of samples accumulated in the histogram. We can compare
+ // this count to the sum of the counts (TotalCount() function), and detect
+ // problems. Note, depending on the implementation of different histogram
+ // types, there might be races during histogram accumulation and snapshotting
+ // that we choose to accept. In this case, the tallies might mismatch even
+ // when no memory corruption has happened.
+ HistogramBase::Count redundant_count_;
+};
+
+class BASE_EXPORT SampleCountIterator {
+ public:
+ virtual ~SampleCountIterator();
+
+ virtual bool Done() const = 0;
+ virtual void Next() = 0;
+
+ // Get the sample and count at current position.
+ // |min| |max| and |count| can be NULL if the value is not of interest.
+ // Requires: !Done();
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const = 0;
+
+ // Get the index of current histogram bucket.
+ // For histograms that don't use predefined buckets, it returns false.
+ // Requires: !Done();
+ virtual bool GetBucketIndex(size_t* index) const;
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc
index b75932c..ad054b1 100644
--- a/base/metrics/histogram_snapshot_manager.cc
+++ b/base/metrics/histogram_snapshot_manager.cc
@@ -4,10 +4,14 @@
#include "base/metrics/histogram_snapshot_manager.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
+#include "base/stl_util.h"
-using base::Histogram;
-using base::StatisticsRecorder;
+using std::map;
+using std::string;
namespace base {
@@ -17,7 +21,9 @@ HistogramSnapshotManager::HistogramSnapshotManager(
DCHECK(histogram_flattener_);
}
-HistogramSnapshotManager::~HistogramSnapshotManager() {}
+HistogramSnapshotManager::~HistogramSnapshotManager() {
+ STLDeleteValues(&logged_samples_);
+}
void HistogramSnapshotManager::PrepareDeltas(Histogram::Flags flag_to_set,
bool record_only_uma) {
@@ -38,11 +44,10 @@ void HistogramSnapshotManager::PrepareDelta(const Histogram& histogram) {
DCHECK(histogram_flattener_);
// Get up-to-date snapshot of sample stats.
- Histogram::SampleSet snapshot;
- histogram.SnapshotSample(&snapshot);
+ scoped_ptr<HistogramSamples> snapshot(histogram.SnapshotSamples());
const std::string& histogram_name = histogram.histogram_name();
- int corruption = histogram.FindCorruption(snapshot);
+ int corruption = histogram.FindCorruption(*snapshot);
// Crash if we detect that our histograms have been overwritten. This may be
// a fair distance from the memory smasher, but we hope to correlate these
@@ -59,54 +64,54 @@ void HistogramSnapshotManager::PrepareDelta(const Histogram& histogram) {
// COUNT_LOW_ERROR and they never arise together, so we don't need to extract
// bits from corruption.
if (corruption) {
- NOTREACHED();
+ DLOG(ERROR) << "Histogram: " << histogram_name
+ << " has data corruption: " << corruption;
histogram_flattener_->InconsistencyDetected(
static_cast<Histogram::Inconsistencies>(corruption));
- // Don't record corrupt data to metrics survices.
- if (NULL == inconsistencies_.get())
- inconsistencies_.reset(new ProblemMap);
- int old_corruption = (*inconsistencies_)[histogram_name];
+ // Don't record corrupt data to metrics services.
+ int old_corruption = inconsistencies_[histogram_name];
if (old_corruption == (corruption | old_corruption))
return; // We've already seen this corruption for this histogram.
- (*inconsistencies_)[histogram_name] |= corruption;
+ inconsistencies_[histogram_name] |= corruption;
histogram_flattener_->UniqueInconsistencyDetected(
static_cast<Histogram::Inconsistencies>(corruption));
return;
}
- // Find the already recorded stats, or create an empty set. Remove from our
- // snapshot anything that we've already recorded.
- LoggedSampleMap::iterator it = logged_samples_.find(histogram_name);
- Histogram::SampleSet* already_logged;
- if (logged_samples_.end() == it) {
- // Add new entry
- already_logged = &logged_samples_[histogram.histogram_name()];
- // Complete initialization.
- already_logged->Resize(histogram.bucket_count());
+ HistogramSamples* to_log;
+ map<string, HistogramSamples*>::iterator it =
+ logged_samples_.find(histogram_name);
+ if (it == logged_samples_.end()) {
+ to_log = snapshot.release();
+
+ // This histogram has not been logged before, add a new entry.
+ logged_samples_[histogram_name] = to_log;
} else {
- already_logged = &(it->second);
- int64 discrepancy(already_logged->TotalCount() -
- already_logged->redundant_count());
- if (discrepancy) {
- NOTREACHED(); // Already_logged has become corrupt.
- int problem = static_cast<int>(discrepancy);
- if (problem != discrepancy)
- problem = INT_MAX;
- histogram_flattener_->InconsistencyDetectedInLoggedCount(problem);
- // With no valid baseline, we'll act like we've recorded everything in our
- // snapshot.
- already_logged->Subtract(*already_logged);
- already_logged->Add(snapshot);
- }
- // Deduct any stats we've already logged from our snapshot.
- snapshot.Subtract(*already_logged);
+ HistogramSamples* already_logged = it->second;
+ InspectLoggedSamplesInconsistency(*snapshot, already_logged);
+ snapshot->Subtract(*already_logged);
+ already_logged->Add(*snapshot);
+ to_log = snapshot.get();
}
- // Snapshot now contains only a delta to what we've already_logged.
- if (snapshot.redundant_count() > 0) {
- histogram_flattener_->RecordDelta(histogram, snapshot);
- // Add new data into our running total.
- already_logged->Add(snapshot);
+ if (to_log->redundant_count() > 0)
+ histogram_flattener_->RecordDelta(histogram, *to_log);
+}
+
+void HistogramSnapshotManager::InspectLoggedSamplesInconsistency(
+ const HistogramSamples& new_snapshot,
+ HistogramSamples* logged_samples) {
+ HistogramBase::Count discrepancy =
+ logged_samples->TotalCount() - logged_samples->redundant_count();
+ if (!discrepancy)
+ return;
+
+ histogram_flattener_->InconsistencyDetectedInLoggedCount(discrepancy);
+ if (discrepancy > Histogram::kCommonRaceBasedCountMismatch) {
+ // Fix logged_samples.
+ logged_samples->Subtract(*logged_samples);
+ logged_samples->Add(new_snapshot);
}
}
+
} // namespace base
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h
index a945ac7..0ec1ac3 100644
--- a/base/metrics/histogram_snapshot_manager.h
+++ b/base/metrics/histogram_snapshot_manager.h
@@ -9,12 +9,13 @@
#include <string>
#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_flattener.h"
namespace base {
+class HistogramSamples;
+class HistogramFlattener;
+
// HistogramSnapshotManager handles the logistics of gathering up available
// histograms for recording either to disk or for transmission (such as from
// renderer to browser, or from browser to UMA upload). Since histograms can sit
@@ -30,23 +31,23 @@ class BASE_EXPORT HistogramSnapshotManager {
// Snapshot all histograms, and ask |histogram_flattener_| to record the
// delta. The arguments allow selecting only a subset of histograms for
// recording, or to set a flag in each recorded histogram.
- void PrepareDeltas(base::Histogram::Flags flags_to_set, bool record_only_uma);
+ void PrepareDeltas(Histogram::Flags flags_to_set, bool record_only_uma);
private:
- // Maintain a map of histogram names to the sample stats we've recorded.
- typedef std::map<std::string, base::Histogram::SampleSet> LoggedSampleMap;
- // List of histograms names, and their encontered corruptions.
- typedef std::map<std::string, int> ProblemMap;
-
// Snapshot this histogram, and record the delta.
- void PrepareDelta(const base::Histogram& histogram);
+ void PrepareDelta(const Histogram& histogram);
+
+ // Try to detect and fix count inconsistency of logged samples.
+ void InspectLoggedSamplesInconsistency(
+ const HistogramSamples& new_snapshot,
+ HistogramSamples* logged_samples);
// For histograms, track what we've already recorded (as a sample for
// each histogram) so that we can record only the delta with the next log.
- LoggedSampleMap logged_samples_;
+ std::map<std::string, HistogramSamples*> logged_samples_;
- // List of histograms found corrupt to be corrupt, and their problems.
- scoped_ptr<ProblemMap> inconsistencies_;
+ // List of histograms found to be corrupt, and their problems.
+ std::map<std::string, int> inconsistencies_;
// |histogram_flattener_| handles the logistics of recording the histogram
// deltas.
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc
index 7f23ede..f016487 100644
--- a/base/metrics/histogram_unittest.cc
+++ b/base/metrics/histogram_unittest.cc
@@ -4,6 +4,7 @@
// Test of Histogram class
+#include <climits>
#include <algorithm>
#include <vector>
@@ -11,6 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -206,14 +208,13 @@ TEST(HistogramTest, BoundsTest) {
histogram->Add(10000);
// Verify they landed in the underflow, and overflow buckets.
- Histogram::SampleSet sample;
- histogram->SnapshotSample(&sample);
- EXPECT_EQ(2, sample.counts(0));
- EXPECT_EQ(0, sample.counts(1));
+ scoped_ptr<SampleVector> samples = histogram->SnapshotSamples();
+ EXPECT_EQ(2, samples->GetCountAtIndex(0));
+ EXPECT_EQ(0, samples->GetCountAtIndex(1));
size_t array_size = histogram->bucket_count();
EXPECT_EQ(kBucketCount, array_size);
- EXPECT_EQ(0, sample.counts(array_size - 2));
- EXPECT_EQ(2, sample.counts(array_size - 1));
+ EXPECT_EQ(0, samples->GetCountAtIndex(array_size - 2));
+ EXPECT_EQ(2, samples->GetCountAtIndex(array_size - 1));
vector<int> custom_ranges;
custom_ranges.push_back(10);
@@ -227,15 +228,16 @@ TEST(HistogramTest, BoundsTest) {
test_custom_histogram->Add(-50);
test_custom_histogram->Add(100);
test_custom_histogram->Add(1000);
+ test_custom_histogram->Add(INT_MAX);
// Verify they landed in the underflow, and overflow buckets.
- Histogram::SampleSet custom_sample;
- test_custom_histogram->SnapshotSample(&custom_sample);
- EXPECT_EQ(2, custom_sample.counts(0));
- EXPECT_EQ(0, custom_sample.counts(1));
- size_t custom_array_size = test_custom_histogram->bucket_count();
- EXPECT_EQ(0, custom_sample.counts(custom_array_size - 2));
- EXPECT_EQ(2, custom_sample.counts(custom_array_size - 1));
+ scoped_ptr<SampleVector> custom_samples =
+ test_custom_histogram->SnapshotSamples();
+ EXPECT_EQ(2, custom_samples->GetCountAtIndex(0));
+ EXPECT_EQ(0, custom_samples->GetCountAtIndex(1));
+ size_t bucket_count = test_custom_histogram->bucket_count();
+ EXPECT_EQ(0, custom_samples->GetCountAtIndex(bucket_count - 2));
+ EXPECT_EQ(3, custom_samples->GetCountAtIndex(bucket_count - 1));
}
// Check to be sure samples land as expected is "correct" buckets.
@@ -253,45 +255,45 @@ TEST(HistogramTest, BucketPlacementTest) {
}
// Check to see that the bucket counts reflect our additions.
- Histogram::SampleSet sample;
- histogram->SnapshotSample(&sample);
+ scoped_ptr<SampleVector> samples = histogram->SnapshotSamples();
for (int i = 0; i < 8; i++)
- EXPECT_EQ(i + 1, sample.counts(i));
+ EXPECT_EQ(i + 1, samples->GetCountAtIndex(i));
}
TEST(HistogramTest, CorruptSampleCounts) {
Histogram* histogram(Histogram::FactoryGet(
"Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
- EXPECT_EQ(0, histogram->sample_.redundant_count());
- histogram->Add(20); // Add some samples.
+ // Add some samples.
+ histogram->Add(20);
histogram->Add(40);
- EXPECT_EQ(2, histogram->sample_.redundant_count());
- Histogram::SampleSet snapshot;
- histogram->SnapshotSample(&snapshot);
- EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0);
- EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption.
- EXPECT_EQ(2, snapshot.redundant_count());
+ scoped_ptr<SampleVector> snapshot = histogram->SnapshotSamples();
+ EXPECT_EQ(Histogram::NO_INCONSISTENCIES,
+ histogram->FindCorruption(*snapshot));
+ EXPECT_EQ(2, snapshot->redundant_count());
+ EXPECT_EQ(2, snapshot->TotalCount());
- snapshot.counts_[3] += 100; // Sample count won't match redundant count.
- EXPECT_EQ(Histogram::COUNT_LOW_ERROR, histogram->FindCorruption(snapshot));
- snapshot.counts_[2] -= 200;
- EXPECT_EQ(Histogram::COUNT_HIGH_ERROR, histogram->FindCorruption(snapshot));
+ snapshot->counts_[3] += 100; // Sample count won't match redundant count.
+ EXPECT_EQ(Histogram::COUNT_LOW_ERROR,
+ histogram->FindCorruption(*snapshot));
+ snapshot->counts_[2] -= 200;
+ EXPECT_EQ(Histogram::COUNT_HIGH_ERROR,
+ histogram->FindCorruption(*snapshot));
// But we can't spot a corruption if it is compensated for.
- snapshot.counts_[1] += 100;
- EXPECT_EQ(0, histogram->FindCorruption(snapshot));
+ snapshot->counts_[1] += 100;
+ EXPECT_EQ(Histogram::NO_INCONSISTENCIES,
+ histogram->FindCorruption(*snapshot));
}
TEST(HistogramTest, CorruptBucketBounds) {
Histogram* histogram(Histogram::FactoryGet(
"Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
- Histogram::SampleSet snapshot;
- histogram->SnapshotSample(&snapshot);
- EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0);
- EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption.
+ scoped_ptr<SampleVector> snapshot = histogram->SnapshotSamples();
+ EXPECT_EQ(Histogram::NO_INCONSISTENCIES,
+ histogram->FindCorruption(*snapshot));
BucketRanges* bucket_ranges =
const_cast<BucketRanges*>(histogram->bucket_ranges());
@@ -299,20 +301,20 @@ TEST(HistogramTest, CorruptBucketBounds) {
bucket_ranges->set_range(1, bucket_ranges->range(2));
bucket_ranges->set_range(2, tmp);
EXPECT_EQ(Histogram::BUCKET_ORDER_ERROR | Histogram::RANGE_CHECKSUM_ERROR,
- histogram->FindCorruption(snapshot));
+ histogram->FindCorruption(*snapshot));
bucket_ranges->set_range(2, bucket_ranges->range(1));
bucket_ranges->set_range(1, tmp);
- EXPECT_EQ(0, histogram->FindCorruption(snapshot));
+ EXPECT_EQ(0, histogram->FindCorruption(*snapshot));
+ // Show that two simple changes don't offset each other
bucket_ranges->set_range(3, bucket_ranges->range(3) + 1);
EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR,
- histogram->FindCorruption(snapshot));
+ histogram->FindCorruption(*snapshot));
- // Show that two simple changes don't offset each other
bucket_ranges->set_range(4, bucket_ranges->range(4) - 1);
EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR,
- histogram->FindCorruption(snapshot));
+ histogram->FindCorruption(*snapshot));
// Repair histogram so that destructor won't DCHECK().
bucket_ranges->set_range(3, bucket_ranges->range(3) - 1);
diff --git a/base/metrics/sample_vector.cc b/base/metrics/sample_vector.cc
new file mode 100644
index 0000000..327a1f2
--- /dev/null
+++ b/base/metrics/sample_vector.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/sample_vector.h"
+
+#include "base/logging.h"
+#include "base/metrics/bucket_ranges.h"
+
+using std::vector;
+
+namespace base {
+
+typedef HistogramBase::Count Count;
+typedef HistogramBase::Sample Sample;
+
+SampleVector::SampleVector(const BucketRanges* bucket_ranges)
+ : counts_(bucket_ranges->size() - 1),
+ bucket_ranges_(bucket_ranges) {
+ CHECK_GE(bucket_ranges_->size(), 2u);
+}
+
+SampleVector::~SampleVector() {}
+
+void SampleVector::Accumulate(Sample value, Count count) {
+ size_t bucket_index = GetBucketIndex(value);
+ counts_[bucket_index] += count;
+ IncreaseSum(count * value);
+ IncreaseRedundantCount(count);
+}
+
+Count SampleVector::GetCount(Sample value) const {
+ size_t bucket_index = GetBucketIndex(value);
+ return counts_[bucket_index];
+}
+
+Count SampleVector::TotalCount() const {
+ Count count = 0;
+ for (size_t i = 0; i < counts_.size(); i++) {
+ count += counts_[i];
+ }
+ return count;
+}
+
+Count SampleVector::GetCountAtIndex(size_t bucket_index) const {
+ DCHECK(bucket_index >= 0 && bucket_index < counts_.size());
+ return counts_[bucket_index];
+}
+
+scoped_ptr<SampleCountIterator> SampleVector::Iterator() const {
+ return scoped_ptr<SampleCountIterator>(
+ new SampleVectorIterator(&counts_, bucket_ranges_));
+}
+
+bool SampleVector::AddSubtractImpl(SampleCountIterator* iter,
+ HistogramSamples::Instruction instruction) {
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+
+ // Go through the iterator and add the counts into correct bucket.
+ size_t index = 0;
+ while (index < counts_.size() && !iter->Done()) {
+ iter->Get(&min, &max, &count);
+ if (min == bucket_ranges_->range(index) &&
+ max == bucket_ranges_->range(index + 1)) {
+ // Sample matches this bucket!
+ counts_[index] +=
+ (instruction == HistogramSamples::ADD) ? count : -count;
+ iter->Next();
+ } else if (min > bucket_ranges_->range(index)) {
+ // Sample is larger than current bucket range. Try next.
+ index++;
+ } else {
+ // Sample is smaller than current bucket range. We scan buckets from
+ // smallest to largest, so the sample value must be invalid.
+ return false;
+ }
+ }
+
+ return iter->Done();
+}
+
+// Use simple binary search. This is very general, but there are better
+// approaches if we knew that the buckets were linearly distributed.
+size_t SampleVector::GetBucketIndex(Sample value) const {
+ size_t bucket_count = bucket_ranges_->size() - 1;
+ CHECK_GE(bucket_count, 1u);
+ CHECK_GE(value, bucket_ranges_->range(0));
+ CHECK_LT(value, bucket_ranges_->range(bucket_count));
+
+ size_t under = 0;
+ size_t over = bucket_count;
+ size_t mid;
+ do {
+ DCHECK_GE(over, under);
+ mid = under + (over - under)/2;
+ if (mid == under)
+ break;
+ if (bucket_ranges_->range(mid) <= value)
+ under = mid;
+ else
+ over = mid;
+ } while (true);
+
+ DCHECK_LE(bucket_ranges_->range(mid), value);
+ CHECK_GT(bucket_ranges_->range(mid + 1), value);
+ return mid;
+}
+
+SampleVectorIterator::SampleVectorIterator(const vector<Count>* counts,
+ const BucketRanges* bucket_ranges)
+ : counts_(counts),
+ bucket_ranges_(bucket_ranges),
+ index_(0) {
+ CHECK_GT(bucket_ranges_->size(), counts_->size());
+ SkipEmptyBuckets();
+}
+
+bool SampleVectorIterator::Done() const {
+ return index_ >= counts_->size();
+}
+
+void SampleVectorIterator::Next() {
+ DCHECK(!Done());
+ index_++;
+ SkipEmptyBuckets();
+}
+
+void SampleVectorIterator::Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const {
+ DCHECK(!Done());
+ if (min != NULL)
+ *min = bucket_ranges_->range(index_);
+ if (max != NULL)
+ *max = bucket_ranges_->range(index_ + 1);
+ if (count != NULL)
+ *count = (*counts_)[index_];
+}
+
+bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
+ DCHECK(!Done());
+ if (index != NULL)
+ *index = index_;
+ return true;
+}
+
+void SampleVectorIterator::SkipEmptyBuckets() {
+ if (Done())
+ return;
+
+ while (index_ < counts_->size()) {
+ if ((*counts_)[index_] != 0)
+ return;
+ index_++;
+ }
+}
+
+} // namespace base
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h
new file mode 100644
index 0000000..8938d36
--- /dev/null
+++ b/base/metrics/sample_vector.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// SampleVector implements HistogramSamples interface. It is used by all
+// Histogram based classes to store samples.
+
+#ifndef BASE_METRICS_SAMPLE_VECTOR_H_
+#define BASE_METRICS_SAMPLE_VECTOR_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+
+namespace base {
+
+class BucketRanges;
+
+class BASE_EXPORT_PRIVATE SampleVector : public HistogramSamples {
+ public:
+ explicit SampleVector(const BucketRanges* bucket_ranges);
+ virtual ~SampleVector();
+
+ // HistogramSamples implementation:
+ virtual void Accumulate(HistogramBase::Sample value,
+ HistogramBase::Count count) OVERRIDE;
+ virtual HistogramBase::Count GetCount(
+ HistogramBase::Sample value) const OVERRIDE;
+ virtual HistogramBase::Count TotalCount() const OVERRIDE;
+ virtual scoped_ptr<SampleCountIterator> Iterator() const OVERRIDE;
+
+ // Get count of a specific bucket.
+ HistogramBase::Count GetCountAtIndex(size_t bucket_index) const;
+
+ protected:
+ virtual bool AddSubtractImpl(
+ SampleCountIterator* iter,
+ HistogramSamples::Instruction instruction) OVERRIDE;
+
+ virtual size_t GetBucketIndex(HistogramBase::Sample value) const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
+
+ std::vector<HistogramBase::Count> counts_;
+
+ // Shares the same BucketRanges with Histogram object.
+ const BucketRanges* const bucket_ranges_;
+
+ DISALLOW_COPY_AND_ASSIGN(SampleVector);
+};
+
+class BASE_EXPORT_PRIVATE SampleVectorIterator : public SampleCountIterator {
+ public:
+ SampleVectorIterator(const std::vector<HistogramBase::Count>* counts,
+ const BucketRanges* bucket_ranges);
+
+ // SampleCountIterator implementation:
+ virtual bool Done() const OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const OVERRIDE;
+ virtual bool GetBucketIndex(size_t* index) const OVERRIDE;
+
+ private:
+ void SkipEmptyBuckets();
+
+ const std::vector<HistogramBase::Count>* counts_;
+ const BucketRanges* bucket_ranges_;
+
+ size_t index_;
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_SAMPLE_VECTOR_H_
diff --git a/base/metrics/sample_vector_unittest.cc b/base/metrics/sample_vector_unittest.cc
new file mode 100644
index 0000000..13e5e8b
--- /dev/null
+++ b/base/metrics/sample_vector_unittest.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sample_vector.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::vector;
+
+namespace base {
+namespace {
+
+TEST(SampleVectorTest, AccumulateTest) {
+ // Custom buckets: [1, 5) [5, 10)
+ BucketRanges ranges(3);
+ ranges.set_range(0, 1);
+ ranges.set_range(1, 5);
+ ranges.set_range(2, 10);
+ SampleVector samples(&ranges);
+
+ samples.Accumulate(1, 200);
+ samples.Accumulate(2, -300);
+ EXPECT_EQ(-100, samples.GetCountAtIndex(0));
+
+ samples.Accumulate(5, 200);
+ EXPECT_EQ(200, samples.GetCountAtIndex(1));
+
+ EXPECT_EQ(600, samples.sum());
+ EXPECT_EQ(100, samples.redundant_count());
+ EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
+
+ samples.Accumulate(5, -100);
+ EXPECT_EQ(100, samples.GetCountAtIndex(1));
+
+ EXPECT_EQ(100, samples.sum());
+ EXPECT_EQ(0, samples.redundant_count());
+ EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
+}
+
+TEST(SampleVectorTest, AddSubtractTest) {
+ // Custom buckets: [0, 1) [1, 2) [2, 3) [3, INT_MAX)
+ BucketRanges ranges(5);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+ ranges.set_range(3, 3);
+ ranges.set_range(4, INT_MAX);
+
+ SampleVector samples1(&ranges);
+ samples1.Accumulate(0, 100);
+ samples1.Accumulate(2, 100);
+ samples1.Accumulate(4, 100);
+ EXPECT_EQ(600, samples1.sum());
+ EXPECT_EQ(300, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+
+ SampleVector samples2(&ranges);
+ samples2.Accumulate(1, 200);
+ samples2.Accumulate(2, 200);
+ samples2.Accumulate(4, 200);
+ EXPECT_EQ(1400, samples2.sum());
+ EXPECT_EQ(600, samples2.TotalCount());
+ EXPECT_EQ(samples2.redundant_count(), samples2.TotalCount());
+
+ samples1.Add(samples2);
+ EXPECT_EQ(100, samples1.GetCountAtIndex(0));
+ EXPECT_EQ(200, samples1.GetCountAtIndex(1));
+ EXPECT_EQ(300, samples1.GetCountAtIndex(2));
+ EXPECT_EQ(300, samples1.GetCountAtIndex(3));
+ EXPECT_EQ(2000, samples1.sum());
+ EXPECT_EQ(900, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+
+ samples1.Subtract(samples2);
+ EXPECT_EQ(100, samples1.GetCountAtIndex(0));
+ EXPECT_EQ(0, samples1.GetCountAtIndex(1));
+ EXPECT_EQ(100, samples1.GetCountAtIndex(2));
+ EXPECT_EQ(100, samples1.GetCountAtIndex(3));
+ EXPECT_EQ(600, samples1.sum());
+ EXPECT_EQ(300, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+}
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+TEST(SampleVectorDeathTest, BucketIndexTest) {
+ // 8 buckets with exponential layout:
+ // [0, 1) [1, 2) [2, 4) [4, 8) [8, 16) [16, 32) [32, 64) [64, INT_MAX)
+ BucketRanges ranges(9);
+ Histogram::InitializeBucketRanges(1, 64, 8, &ranges);
+ SampleVector samples(&ranges);
+
+ // Normal case
+ samples.Accumulate(0, 1);
+ samples.Accumulate(3, 2);
+ samples.Accumulate(64, 3);
+ EXPECT_EQ(1, samples.GetCount(0));
+ EXPECT_EQ(2, samples.GetCount(2));
+ EXPECT_EQ(3, samples.GetCount(65));
+
+ // Extreme case.
+ EXPECT_DEATH(samples.Accumulate(INT_MIN, 100), "");
+ EXPECT_DEATH(samples.Accumulate(-1, 100), "");
+ EXPECT_DEATH(samples.Accumulate(INT_MAX, 100), "");
+
+ // Custom buckets: [1, 5) [5, 10)
+ // Note, this is not a valid BucketRanges for Histogram because it does not
+ // have overflow buckets.
+ BucketRanges ranges2(3);
+ ranges2.set_range(0, 1);
+ ranges2.set_range(1, 5);
+ ranges2.set_range(2, 10);
+ SampleVector samples2(&ranges2);
+
+ // Normal case.
+ samples2.Accumulate(1, 1);
+ samples2.Accumulate(4, 1);
+ samples2.Accumulate(5, 2);
+ samples2.Accumulate(9, 2);
+ EXPECT_EQ(2, samples2.GetCount(1));
+ EXPECT_EQ(4, samples2.GetCount(5));
+
+ // Extreme case.
+ EXPECT_DEATH(samples2.Accumulate(0, 100), "");
+ EXPECT_DEATH(samples2.Accumulate(10, 100), "");
+}
+
+TEST(SampleVectorDeathTest, AddSubtractBucketNotMatchTest) {
+ // Custom buckets 1: [1, 3) [3, 5)
+ BucketRanges ranges1(3);
+ ranges1.set_range(0, 1);
+ ranges1.set_range(1, 3);
+ ranges1.set_range(2, 5);
+ SampleVector samples1(&ranges1);
+
+ // Custom buckets 2: [0, 1) [1, 3) [3, 6) [6, 7)
+ BucketRanges ranges2(5);
+ ranges2.set_range(0, 0);
+ ranges2.set_range(1, 1);
+ ranges2.set_range(2, 3);
+ ranges2.set_range(3, 6);
+ ranges2.set_range(4, 7);
+ SampleVector samples2(&ranges2);
+
+ samples2.Accumulate(1, 100);
+ samples1.Add(samples2);
+ EXPECT_EQ(100, samples1.GetCountAtIndex(0));
+
+ // Extra bucket in the beginning.
+ samples2.Accumulate(0, 100);
+ EXPECT_DEATH(samples1.Add(samples2), "");
+ EXPECT_DEATH(samples1.Subtract(samples2), "");
+
+ // Extra bucket in the end.
+ samples2.Accumulate(0, -100);
+ samples2.Accumulate(6, 100);
+ EXPECT_DEATH(samples1.Add(samples2), "");
+ EXPECT_DEATH(samples1.Subtract(samples2), "");
+
+ // Bucket not match: [3, 5) VS [3, 6)
+ samples2.Accumulate(6, -100);
+ samples2.Accumulate(3, 100);
+ EXPECT_DEATH(samples1.Add(samples2), "");
+ EXPECT_DEATH(samples1.Subtract(samples2), "");
+}
+
+// (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+#endif
+
+TEST(SampleVectorIteratorTest, IterateTest) {
+ BucketRanges ranges(5);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+ ranges.set_range(3, 3);
+ ranges.set_range(4, 4);
+
+ vector<HistogramBase::Count> counts(3);
+ counts[0] = 1;
+ counts[1] = 0; // Iterator will bypass this empty bucket.
+ counts[2] = 2;
+
+ // BucketRanges can have larger size than counts.
+ SampleVectorIterator it(&counts, &ranges);
+ size_t index;
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ it.Get(&min, &max, &count);
+ EXPECT_EQ(0, min);
+ EXPECT_EQ(1, max);
+ EXPECT_EQ(1, count);
+ EXPECT_TRUE(it.GetBucketIndex(&index));
+ EXPECT_EQ(0u, index);
+
+ it.Next();
+ it.Get(&min, &max, &count);
+ EXPECT_EQ(2, min);
+ EXPECT_EQ(3, max);
+ EXPECT_EQ(2, count);
+ EXPECT_TRUE(it.GetBucketIndex(&index));
+ EXPECT_EQ(2u, index);
+
+ it.Next();
+ EXPECT_TRUE(it.Done());
+
+ // Create iterator from SampleVector.
+ SampleVector samples(&ranges);
+ samples.Accumulate(0, 0);
+ samples.Accumulate(1, 1);
+ samples.Accumulate(2, 2);
+ samples.Accumulate(3, 3);
+ scoped_ptr<SampleCountIterator> it2 = samples.Iterator();
+
+ int i;
+ for (i = 1; !it2->Done(); i++, it2->Next()) {
+ it2->Get(&min, &max, &count);
+ EXPECT_EQ(i, min);
+ EXPECT_EQ(i + 1, max);
+ EXPECT_EQ(i, count);
+
+ size_t index;
+ EXPECT_TRUE(it2->GetBucketIndex(&index));
+ EXPECT_EQ(static_cast<size_t>(i), index);
+ }
+ EXPECT_EQ(4, i);
+}
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+TEST(SampleVectorIteratorDeathTest, IterateDoneTest) {
+ BucketRanges ranges(5);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+ ranges.set_range(3, 3);
+ ranges.set_range(4, INT_MAX);
+ SampleVector samples(&ranges);
+
+ scoped_ptr<SampleCountIterator> it = samples.Iterator();
+
+ EXPECT_TRUE(it->Done());
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ EXPECT_DEATH(it->Get(&min, &max, &count), "");
+
+ EXPECT_DEATH(it->Next(), "");
+
+ samples.Accumulate(2, 100);
+ it = samples.Iterator();
+ EXPECT_FALSE(it->Done());
+}
+
+// (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+#endif
+
+} // namespace
+} // namespace base
diff --git a/chrome/browser/chrome_browser_application_mac_unittest.mm b/chrome/browser/chrome_browser_application_mac_unittest.mm
index 982cf52..98141674 100644
--- a/chrome/browser/chrome_browser_application_mac_unittest.mm
+++ b/chrome/browser/chrome_browser_application_mac_unittest.mm
@@ -5,12 +5,15 @@
#import <Cocoa/Cocoa.h>
#import "base/mac/scoped_nsexception_enabler.h"
+#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
#import "chrome/browser/chrome_browser_application_mac.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::Histogram;
+using base::HistogramSamples;
using base::StatisticsRecorder;
namespace chrome_browser_application_mac {
@@ -74,16 +77,16 @@ TEST(ChromeApplicationMacTest, RecordException) {
StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
EXPECT_EQ(1U, histograms.size());
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histograms[0]->flags());
- Histogram::SampleSet sample;
- histograms[0]->SnapshotSample(&sample);
- EXPECT_EQ(4, sample.counts(0));
- EXPECT_EQ(1, sample.counts(1));
- EXPECT_EQ(3, sample.counts(2));
- EXPECT_EQ(2, sample.counts(3));
+
+ scoped_ptr<HistogramSamples> samples(histograms[0]->SnapshotSamples());
+ EXPECT_EQ(4, samples->GetCount(0));
+ EXPECT_EQ(1, samples->GetCount(1));
+ EXPECT_EQ(3, samples->GetCount(2));
+ EXPECT_EQ(2, samples->GetCount(3));
// The unknown exceptions should end up in the overflow bucket.
EXPECT_EQ(kUnknownNSException + 1, histograms[0]->bucket_count());
- EXPECT_EQ(4, sample.counts(kUnknownNSException));
+ EXPECT_EQ(4, samples->GetCount(kUnknownNSException));
}
} // chrome_browser_application_mac
diff --git a/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc b/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc
index 1ee21b7ac..68ea455 100644
--- a/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc
+++ b/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc
@@ -9,8 +9,10 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
@@ -23,6 +25,9 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::Histogram;
+using base::HistogramSamples;
+
namespace chrome_browser_net {
namespace {
@@ -95,9 +100,9 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
const char* name = kHistogramNames[i];
- base::Histogram::SampleSet sample = GetHistogram(name);
- if (sample.TotalCount() > 0) {
- original_samples_[name] = sample;
+ scoped_ptr<HistogramSamples> samples = GetHistogram(name);
+ if (samples.get() && samples->TotalCount() > 0) {
+ original_samples_[name] = samples.release();
}
}
}
@@ -105,6 +110,7 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
virtual void TearDown() OVERRIDE {
BrowserThread::ReleaseSoon(BrowserThread::IO, FROM_HERE, context_);
message_loop_.RunAllPending();
+ STLDeleteValues(&original_samples_);
}
void RunTest(
@@ -118,7 +124,8 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
callback.WaitForResult();
}
- void ExpectHistogramCount(int expected_count, int expected_value,
+ void ExpectHistogramCount(int expected_count,
+ int expected_value,
HistogramField field) {
const char* name;
@@ -143,19 +150,24 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
FAIL() << "Unexpected field: " << field;
}
- base::Histogram::SampleSet sample = GetHistogram(name);
+ scoped_ptr<HistogramSamples> samples = GetHistogram(name);
+ if (!samples.get())
+ return;
+
if (ContainsKey(original_samples_, name)) {
- sample.Subtract(original_samples_[name]);
+ samples->Subtract((*original_samples_[name]));
}
- EXPECT_EQ(expected_count, sample.TotalCount()) << name;
+ EXPECT_EQ(expected_count, samples->TotalCount()) << name;
if (expected_count > 0) {
- EXPECT_EQ(expected_count, sample.counts(expected_value)) << name;
+ EXPECT_EQ(expected_count, samples->GetCount(expected_value)) << name;
}
}
- void ExpectRequestHistogramCount(int expected_count, int expected_value,
- int request_id, HistogramField field) {
+ void ExpectRequestHistogramCount(int expected_count,
+ int expected_value,
+ int request_id,
+ HistogramField field) {
const char* field_str = "";
switch (field) {
case FIELD_STATUS:
@@ -176,14 +188,17 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
std::string name = base::StringPrintf("NetConnectivity.Pipeline.%d.%s",
request_id, field_str);
- base::Histogram::SampleSet sample = GetHistogram(name.c_str());
+ scoped_ptr<HistogramSamples> samples = GetHistogram(name.c_str());
+ if (!samples.get())
+ return;
+
if (ContainsKey(original_samples_, name)) {
- sample.Subtract(original_samples_[name]);
+ samples->Subtract(*(original_samples_[name]));
}
- EXPECT_EQ(expected_count, sample.TotalCount()) << name;
+ EXPECT_EQ(expected_count, samples->TotalCount()) << name;
if (expected_count > 0) {
- EXPECT_EQ(expected_count, sample.counts(expected_value)) << name;
+ EXPECT_EQ(expected_count, samples->GetCount(expected_value)) << name;
}
}
@@ -193,10 +208,10 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
content::TestBrowserThread io_thread_;
private:
- base::Histogram::SampleSet GetHistogram(const char* name) {
- base::Histogram::SampleSet sample;
- base::Histogram* cached_histogram = NULL;
- base::Histogram* current_histogram =
+ scoped_ptr<HistogramSamples> GetHistogram(const char* name) {
+ scoped_ptr<HistogramSamples> samples;
+ Histogram* cached_histogram = NULL;
+ Histogram* current_histogram =
base::StatisticsRecorder::FindHistogram(name);
if (ContainsKey(histograms_, name)) {
cached_histogram = histograms_[name];
@@ -209,29 +224,26 @@ class HttpPipeliningCompatibilityClientTest : public testing::Test {
// last used Histogram and then update the cache if it's different than the
// current Histogram.
if (cached_histogram && current_histogram) {
- cached_histogram->SnapshotSample(&sample);
+ samples = cached_histogram->SnapshotSamples();
if (cached_histogram != current_histogram) {
- base::Histogram::SampleSet current_sample;
- current_histogram->SnapshotSample(&current_sample);
- sample.Add(current_sample);
+ samples->Add(*(current_histogram->SnapshotSamples()));
histograms_[name] = current_histogram;
}
} else if (current_histogram) {
- current_histogram->SnapshotSample(&sample);
+ samples = current_histogram->SnapshotSamples();
histograms_[name] = current_histogram;
} else if (cached_histogram) {
- cached_histogram->SnapshotSample(&sample);
+ samples = cached_histogram->SnapshotSamples();
}
- return sample;
+ return samples.Pass();
}
- static std::map<std::string, base::Histogram*> histograms_;
- std::map<std::string, base::Histogram::SampleSet> samples_;
- std::map<std::string, base::Histogram::SampleSet> original_samples_;
+ static std::map<std::string, Histogram*> histograms_;
+ std::map<std::string, HistogramSamples*> original_samples_;
};
// static
-std::map<std::string, base::Histogram*>
+std::map<std::string, Histogram*>
HttpPipeliningCompatibilityClientTest::histograms_;
TEST_F(HttpPipeliningCompatibilityClientTest, Success) {
diff --git a/chrome/browser/protector/default_search_provider_change_browsertest.cc b/chrome/browser/protector/default_search_provider_change_browsertest.cc
index 321491b..a05b98c 100644
--- a/chrome/browser/protector/default_search_provider_change_browsertest.cc
+++ b/chrome/browser/protector/default_search_provider_change_browsertest.cc
@@ -4,6 +4,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
@@ -130,10 +131,9 @@ class DefaultSearchProviderChangeTest : public InProcessBrowserTest {
base::Histogram::Count count) {
base::Histogram* histogram = base::StatisticsRecorder::FindHistogram(name);
EXPECT_TRUE(histogram != NULL);
- base::Histogram::SampleSet sample;
- histogram->SnapshotSample(&sample);
- EXPECT_EQ(count, sample.counts(bucket)) <<
- "Invalid " << name << " value for bucket " << bucket;
+ scoped_ptr<base::SampleVector> samples = histogram->SnapshotSamples();
+ EXPECT_EQ(count, samples->GetCountAtIndex(bucket))
+ << "Invalid " << name << " value for bucket " << bucket;
}
protected:
diff --git a/chrome/common/metrics/metrics_log_base.cc b/chrome/common/metrics/metrics_log_base.cc
index 5b12cfd..938828f 100644
--- a/chrome/common/metrics/metrics_log_base.cc
+++ b/chrome/common/metrics/metrics_log_base.cc
@@ -7,6 +7,8 @@
#include "base/base64.h"
#include "base/basictypes.h"
#include "base/md5.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
#include "base/perftimer.h"
#include "base/string_number_conversions.h"
#include "base/sys_byteorder.h"
@@ -23,6 +25,9 @@
#define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name)
using base::Histogram;
+using base::HistogramBase;
+using base::HistogramSamples;
+using base::SampleCountIterator;
using base::Time;
using base::TimeDelta;
using metrics::HistogramEventProto;
@@ -449,10 +454,9 @@ void MetricsLogBase::EndElement() {
// the same infrastructure for logging StatsCounters, RatesCounters, etc.
void MetricsLogBase::RecordHistogramDelta(
const Histogram& histogram,
- const Histogram::SampleSet& snapshot) {
+ const HistogramSamples& snapshot) {
DCHECK(!locked_);
DCHECK_NE(0, snapshot.TotalCount());
- DCHECK_EQ(histogram.bucket_count(), snapshot.size());
// We will ignore the MAX_INT/infinite value in the last element of range[].
@@ -471,13 +475,17 @@ void MetricsLogBase::RecordHistogramDelta(
// TODO(jar): Remove sumsquares when protobuffer accepts this as optional.
WriteInt64Attribute("sumsquares", 0);
- for (size_t i = 0; i < histogram.bucket_count(); i++) {
- if (snapshot.counts(i)) {
- OPEN_ELEMENT_FOR_SCOPE("histogrambucket");
- WriteIntAttribute("min", histogram.ranges(i));
- WriteIntAttribute("max", histogram.ranges(i + 1));
- WriteIntAttribute("count", snapshot.counts(i));
- }
+ for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator();
+ !it->Done();
+ it->Next()) {
+ OPEN_ELEMENT_FOR_SCOPE("histogrambucket");
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ it->Get(&min, &max, &count);
+ WriteIntAttribute("min", min);
+ WriteIntAttribute("max", max);
+ WriteIntAttribute("count", count);
}
// Write the protobuf version.
@@ -485,13 +493,20 @@ void MetricsLogBase::RecordHistogramDelta(
histogram_proto->set_name_hash(numeric_name_hash);
histogram_proto->set_sum(snapshot.sum());
- for (size_t i = 0; i < histogram.bucket_count(); ++i) {
- if (snapshot.counts(i)) {
- HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
- bucket->set_min(histogram.ranges(i));
- bucket->set_max(histogram.ranges(i + 1));
- bucket->set_bucket_index(i);
- bucket->set_count(snapshot.counts(i));
- }
+ for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator();
+ !it->Done();
+ it->Next()) {
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ it->Get(&min, &max, &count);
+ HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
+ bucket->set_min(min);
+ bucket->set_max(max);
+ bucket->set_count(count);
+
+ size_t index;
+ if (it->GetBucketIndex(&index))
+ bucket->set_bucket_index(index);
}
}
diff --git a/chrome/common/metrics/metrics_log_base.h b/chrome/common/metrics/metrics_log_base.h
index 0fb43e9..b11f032 100644
--- a/chrome/common/metrics/metrics_log_base.h
+++ b/chrome/common/metrics/metrics_log_base.h
@@ -18,6 +18,10 @@
class GURL;
+namespace base {
+class HistogramSamples;
+} // namespace base
+
// This class provides base functionality for logging metrics data.
class MetricsLogBase {
public:
@@ -72,7 +76,7 @@ class MetricsLogBase {
// Record any changes in a given histogram for transmission.
void RecordHistogramDelta(const base::Histogram& histogram,
- const base::Histogram::SampleSet& snapshot);
+ const base::HistogramSamples& snapshot);
// Stop writing to this record and generate the encoded representation.
// None of the Record* methods can be called after this is called.
diff --git a/chrome/common/metrics/metrics_service_base.cc b/chrome/common/metrics/metrics_service_base.cc
index bffe971..84410c4 100644
--- a/chrome/common/metrics/metrics_service_base.cc
+++ b/chrome/common/metrics/metrics_service_base.cc
@@ -37,7 +37,7 @@ void MetricsServiceBase::RecordCurrentHistograms() {
void MetricsServiceBase::RecordDelta(
const base::Histogram& histogram,
- const base::Histogram::SampleSet& snapshot) {
+ const base::HistogramSamples& snapshot) {
log_manager_.current_log()->RecordHistogramDelta(histogram, snapshot);
}
diff --git a/chrome/common/metrics/metrics_service_base.h b/chrome/common/metrics/metrics_service_base.h
index 753cc26..fb72377 100644
--- a/chrome/common/metrics/metrics_service_base.h
+++ b/chrome/common/metrics/metrics_service_base.h
@@ -11,6 +11,10 @@
#include "base/metrics/histogram_snapshot_manager.h"
#include "chrome/common/metrics/metrics_log_manager.h"
+namespace base {
+class HistogramSamples;
+} // namespace base
+
// This class provides base functionality for logging metrics data.
// TODO(ananta): Factor out more common code from chrome and chrome frame
// metrics service into this class.
@@ -18,7 +22,7 @@ class MetricsServiceBase : public base::HistogramFlattener {
public:
// HistogramFlattener interface (override) methods.
virtual void RecordDelta(const base::Histogram& histogram,
- const base::Histogram::SampleSet& snapshot) OVERRIDE;
+ const base::HistogramSamples& snapshot) OVERRIDE;
virtual void InconsistencyDetected(
base::Histogram::Inconsistencies problem) OVERRIDE;
virtual void UniqueInconsistencyDetected(
diff --git a/chrome/test/base/uma_histogram_helper.cc b/chrome/test/base/uma_histogram_helper.cc
index 75aad62..8b4ff41 100644
--- a/chrome/test/base/uma_histogram_helper.cc
+++ b/chrome/test/base/uma_histogram_helper.cc
@@ -30,30 +30,29 @@ void UMAHistogramHelper::Fetch() {
void UMAHistogramHelper::ExpectUniqueSample(
const std::string& name,
- size_t bucket_id,
- base::Histogram::Count expected_count) {
+ base::HistogramBase::Sample sample,
+ base::HistogramBase::Count expected_count) {
base::Histogram* histogram = base::StatisticsRecorder::FindHistogram(name);
- EXPECT_NE(static_cast<base::Histogram*>(NULL), histogram) <<
- "Histogram \"" << name << "\" does not exist.";
+ EXPECT_NE(static_cast<base::Histogram*>(NULL), histogram)
+ << "Histogram \"" << name << "\" does not exist.";
if (histogram) {
- base::Histogram::SampleSet samples;
- histogram->SnapshotSample(&samples);
- CheckBucketCount(name, bucket_id, expected_count, samples);
- CheckTotalCount(name, expected_count, samples);
+ scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
+ CheckBucketCount(name, sample, expected_count, *samples);
+ CheckTotalCount(name, expected_count, *samples);
}
}
-void UMAHistogramHelper::ExpectTotalCount(const std::string& name,
- base::Histogram::Count count) {
+void UMAHistogramHelper::ExpectTotalCount(
+ const std::string& name,
+ base::HistogramBase::Count count) {
base::Histogram* histogram = base::StatisticsRecorder::FindHistogram(name);
- EXPECT_NE((base::Histogram*)NULL, histogram) << "Histogram \"" << name <<
- "\" does not exist.";
+ EXPECT_NE(static_cast<base::Histogram*>(NULL), histogram)
+ << "Histogram \"" << name << "\" does not exist.";
if (histogram) {
- base::Histogram::SampleSet samples;
- histogram->SnapshotSample(&samples);
- CheckTotalCount(name, count, samples);
+ scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
+ CheckTotalCount(name, count, *samples);
}
}
@@ -61,19 +60,23 @@ void UMAHistogramHelper::FetchCallback() {
MessageLoopForUI::current()->Quit();
}
-void UMAHistogramHelper::CheckBucketCount(const std::string& name,
- size_t bucket_id,
- base::Histogram::Count expected_count,
- base::Histogram::SampleSet& samples) {
- EXPECT_EQ(expected_count, samples.counts(bucket_id)) << "Histogram \"" <<
- name << "\" does not have the right number of samples (" <<
- expected_count << ") in the expected bucket (" << bucket_id << ").";
+void UMAHistogramHelper::CheckBucketCount(
+ const std::string& name,
+ base::HistogramBase::Sample sample,
+ base::HistogramBase::Count expected_count,
+ base::HistogramSamples& samples) {
+ EXPECT_EQ(expected_count, samples.GetCount(sample))
+ << "Histogram \"" << name
+ << "\" does not have the right number of samples (" << expected_count
+ << ") in the expected bucket (" << sample << ").";
}
-void UMAHistogramHelper::CheckTotalCount(const std::string& name,
- base::Histogram::Count expected_count,
- base::Histogram::SampleSet& samples) {
- EXPECT_EQ(expected_count, samples.TotalCount()) << "Histogram \"" << name <<
- "\" does not have the right total number of samples (" <<
- expected_count << ").";
+void UMAHistogramHelper::CheckTotalCount(
+ const std::string& name,
+ base::HistogramBase::Count expected_count,
+ base::HistogramSamples& samples) {
+ EXPECT_EQ(expected_count, samples.TotalCount())
+ << "Histogram \"" << name
+ << "\" does not have the right total number of samples ("
+ << expected_count << ").";
}
diff --git a/chrome/test/base/uma_histogram_helper.h b/chrome/test/base/uma_histogram_helper.h
index f56e492..b04178c 100644
--- a/chrome/test/base/uma_histogram_helper.h
+++ b/chrome/test/base/uma_histogram_helper.h
@@ -6,6 +6,8 @@
#define CHROME_TEST_BASE_UMA_HISTOGRAM_HELPER_H_
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
// UMAHistogramHelper provides a simple interface for examining UMA histograms.
// Tests can use this interface to verify that UMA data is getting logged as
@@ -20,22 +22,25 @@ class UMAHistogramHelper {
// We know the exact number of samples in a bucket, and that no other bucket
// should have samples.
- void ExpectUniqueSample(const std::string& name, size_t bucket_id,
- base::Histogram::Count expected_count);
+ void ExpectUniqueSample(const std::string& name,
+ base::HistogramBase::Sample sample,
+ base::HistogramBase::Count expected_count);
// We don't know the values of the samples, but we know how many there are.
- void ExpectTotalCount(const std::string& name, base::Histogram::Count count);
+ void ExpectTotalCount(const std::string& name,
+ base::HistogramBase::Count count);
private:
void FetchCallback();
- void CheckBucketCount(const std::string& name, size_t bucket_id,
+ void CheckBucketCount(const std::string& name,
+ base::HistogramBase::Sample sample,
base::Histogram::Count expected_count,
- base::Histogram::SampleSet& samples);
+ base::HistogramSamples& samples);
void CheckTotalCount(const std::string& name,
base::Histogram::Count expected_count,
- base::Histogram::SampleSet& samples);
+ base::HistogramSamples& samples);
DISALLOW_COPY_AND_ASSIGN(UMAHistogramHelper);
};
diff --git a/chrome_frame/metrics_service.h b/chrome_frame/metrics_service.h
index dbd6afc..7ce1db7 100644
--- a/chrome_frame/metrics_service.h
+++ b/chrome_frame/metrics_service.h
@@ -50,9 +50,6 @@ class MetricsService : public MetricsServiceBase {
STOPPED, // Service has stopped
};
- // Maintain a map of histogram names to the sample stats we've sent.
- typedef std::map<std::string, base::Histogram::SampleSet> LoggedSampleMap;
-
// Sets and gets whether metrics recording is active.
// SetRecording(false) also forces a persistent save of logging state (if
// anything has been recorded, or transmitted).
diff --git a/content/common/child_histogram_message_filter.cc b/content/common/child_histogram_message_filter.cc
index a0ca7b9..111f7a9 100644
--- a/content/common/child_histogram_message_filter.cc
+++ b/content/common/child_histogram_message_filter.cc
@@ -71,10 +71,8 @@ void ChildHistogramMessageFilter::UploadAllHistrograms(int sequence_number) {
void ChildHistogramMessageFilter::RecordDelta(
const base::Histogram& histogram,
- const base::Histogram::SampleSet& snapshot) {
+ const base::HistogramSamples& snapshot) {
DCHECK_NE(0, snapshot.TotalCount());
- DCHECK_EQ(histogram.bucket_count(), snapshot.size());
-
std::string histogram_info =
base::Histogram::SerializeHistogramInfo(histogram, snapshot);
diff --git a/content/common/child_histogram_message_filter.h b/content/common/child_histogram_message_filter.h
index c28fb8e..cda7830 100644
--- a/content/common/child_histogram_message_filter.h
+++ b/content/common/child_histogram_message_filter.h
@@ -16,6 +16,10 @@
class MessageLoop;
+namespace base {
+class HistogramSamples;
+} // namespace base
+
namespace content {
class ChildHistogramMessageFilter : public base::HistogramFlattener,
@@ -32,7 +36,7 @@ class ChildHistogramMessageFilter : public base::HistogramFlattener,
// HistogramFlattener interface (override) methods.
virtual void RecordDelta(const base::Histogram& histogram,
- const base::Histogram::SampleSet& snapshot) OVERRIDE;
+ const base::HistogramSamples& snapshot) OVERRIDE;
virtual void InconsistencyDetected(
base::Histogram::Inconsistencies problem) OVERRIDE;
virtual void UniqueInconsistencyDetected(
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 69197e4..af9d316 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -12,6 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
#include "base/stringprintf.h"
#include "base/string_tokenizer.h"
#include "base/threading/thread.h"
@@ -1928,23 +1929,23 @@ TEST_F(CookieMonsterTest, HistogramCheck) {
"Cookie.ExpirationDurationMinutes", 1, 10 * 365 * 24 * 60, 50,
base::Histogram::kUmaTargetedHistogramFlag);
- base::Histogram::SampleSet histogram_set_1;
- expired_histogram->SnapshotSample(&histogram_set_1);
+ scoped_ptr<base::HistogramSamples> samples1(
+ expired_histogram->SnapshotSamples());
ASSERT_TRUE(SetCookieWithDetails(
cm, GURL("http://fake.a.url"), "a", "b", "a.url", "/",
base::Time::Now() + base::TimeDelta::FromMinutes(59),
false, false));
- base::Histogram::SampleSet histogram_set_2;
- expired_histogram->SnapshotSample(&histogram_set_2);
- EXPECT_EQ(histogram_set_1.TotalCount() + 1,
- histogram_set_2.TotalCount());
+ scoped_ptr<base::HistogramSamples> samples2(
+ expired_histogram->SnapshotSamples());
+ EXPECT_EQ(samples1->TotalCount() + 1, samples2->TotalCount());
// kValidCookieLine creates a session cookie.
ASSERT_TRUE(SetCookie(cm, url_google_, kValidCookieLine));
- expired_histogram->SnapshotSample(&histogram_set_1);
- EXPECT_EQ(histogram_set_2.TotalCount(),
- histogram_set_1.TotalCount());
+
+ scoped_ptr<base::HistogramSamples> samples3(
+ expired_histogram->SnapshotSamples());
+ EXPECT_EQ(samples2->TotalCount(), samples3->TotalCount());
}
namespace {
diff --git a/net/disk_cache/stats.cc b/net/disk_cache/stats.cc
index af3b941..62d228c 100644
--- a/net/disk_cache/stats.cc
+++ b/net/disk_cache/stats.cc
@@ -6,6 +6,7 @@
#include "base/format_macros.h"
#include "base/logging.h"
+#include "base/metrics/histogram_samples.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "net/disk_cache/backend_impl.h"
@@ -150,9 +151,7 @@ bool Stats::Init(BackendImpl* backend, uint32* storage_addr) {
backend->ShouldReportAgain()) {
// Stats may be reused when the cache is re-created, but we want only one
// histogram at any given time.
- size_histogram_ =
- StatsHistogram::FactoryGet("DiskCache.SizeStats");
- size_histogram_->Init(this);
+ size_histogram_ = StatsHistogram::FactoryGet("DiskCache.SizeStats", this);
}
}
@@ -264,13 +263,12 @@ int Stats::GetBucketRange(size_t i) const {
return n;
}
-void Stats::Snapshot(StatsHistogram::StatsSamples* samples) const {
- samples->GetCounts()->resize(kDataSizesLength);
+void Stats::Snapshot(base::HistogramSamples* samples) const {
for (int i = 0; i < kDataSizesLength; i++) {
int count = data_sizes_[i];
if (count < 0)
count = 0;
- samples->GetCounts()->at(i) = count;
+ samples->Accumulate(GetBucketRange(i), count);
}
}
diff --git a/net/disk_cache/stats.h b/net/disk_cache/stats.h
index 643c832..94b9d68 100644
--- a/net/disk_cache/stats.h
+++ b/net/disk_cache/stats.h
@@ -11,6 +11,10 @@
#include "base/basictypes.h"
#include "net/disk_cache/stats_histogram.h"
+namespace base {
+class HistogramSamples;
+} // namespace base
+
namespace disk_cache {
class BackendImpl;
@@ -74,7 +78,7 @@ class Stats {
// Support for StatsHistograms. Together, these methods allow StatsHistograms
// to take a snapshot of the data_sizes_ as the histogram data.
int GetBucketRange(size_t i) const;
- void Snapshot(StatsHistogram::StatsSamples* samples) const;
+ void Snapshot(base::HistogramSamples* samples) const;
private:
int GetStatsBucket(int32 size);
diff --git a/net/disk_cache/stats_histogram.cc b/net/disk_cache/stats_histogram.cc
index d3bab34..6bd7a070 100644
--- a/net/disk_cache/stats_histogram.cc
+++ b/net/disk_cache/stats_histogram.cc
@@ -6,33 +6,59 @@
#include "base/debug/leak_annotations.h"
#include "base/logging.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "net/disk_cache/stats.h"
namespace disk_cache {
+using base::BucketRanges;
using base::Histogram;
+using base::HistogramSamples;
+using base::SampleVector;
using base::StatisticsRecorder;
-// Static.
-const Stats* StatsHistogram::stats_ = NULL;
-
-StatsHistogram::~StatsHistogram() {
- // Only cleanup what we set.
- if (init_)
- stats_ = NULL;
+StatsHistogram::StatsHistogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ const BucketRanges* ranges,
+ const Stats* stats)
+ : Histogram(name, minimum, maximum, bucket_count, ranges),
+ stats_(stats) {}
+
+StatsHistogram::~StatsHistogram() {}
+
+// static
+void StatsHistogram::InitializeBucketRanges(const Stats* stats,
+ BucketRanges* ranges) {
+ for (size_t i = 0; i < ranges->size(); i++) {
+ ranges->set_range(i, stats->GetBucketRange(i));
+ }
+ ranges->ResetChecksum();
}
-StatsHistogram* StatsHistogram::FactoryGet(const std::string& name) {
+StatsHistogram* StatsHistogram::FactoryGet(const std::string& name,
+ const Stats* stats) {
Sample minimum = 1;
Sample maximum = disk_cache::Stats::kDataSizesLength - 1;
size_t bucket_count = disk_cache::Stats::kDataSizesLength;
-
Histogram* histogram = StatisticsRecorder::FindHistogram(name);
if (!histogram) {
+ DCHECK(stats);
+
+ // To avoid racy destruction at shutdown, the following will be leaked.
+ BucketRanges* ranges = new BucketRanges(bucket_count + 1);
+ InitializeBucketRanges(stats, ranges);
+ const BucketRanges* registered_ranges =
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+
// To avoid racy destruction at shutdown, the following will be leaked.
StatsHistogram* stats_histogram =
- new StatsHistogram(name, minimum, maximum, bucket_count);
+ new StatsHistogram(name, minimum, maximum, bucket_count,
+ registered_ranges, stats);
stats_histogram->SetFlags(kUmaTargetedHistogramFlag);
histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(stats_histogram);
}
@@ -46,40 +72,19 @@ StatsHistogram* StatsHistogram::FactoryGet(const std::string& name) {
return return_histogram;
}
-bool StatsHistogram::Init(const Stats* stats) {
- DCHECK(stats);
- if (stats_)
- return false;
-
- // We support statistics report for only one cache.
- init_ = true;
- stats_ = stats;
- return true;
-}
-
-Histogram::Sample StatsHistogram::ranges(size_t i) const {
- DCHECK(stats_);
- return stats_->GetBucketRange(i);
-}
-
-size_t StatsHistogram::bucket_count() const {
- return disk_cache::Stats::kDataSizesLength;
-}
-
-void StatsHistogram::SnapshotSample(SampleSet* sample) const {
- DCHECK(stats_);
- StatsSamples my_sample;
- stats_->Snapshot(&my_sample);
-
- *sample = my_sample;
+scoped_ptr<SampleVector> StatsHistogram::SnapshotSamples() const {
+ scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges()));
+ stats_->Snapshot(samples.get());
// Only report UMA data once.
StatsHistogram* mutable_me = const_cast<StatsHistogram*>(this);
mutable_me->ClearFlags(kUmaTargetedHistogramFlag);
+
+ return samples.Pass();
}
Histogram::Inconsistencies StatsHistogram::FindCorruption(
- const SampleSet& snapshot) const {
+ const HistogramSamples& samples) const {
return NO_INCONSISTENCIES; // This class won't monitor inconsistencies.
}
diff --git a/net/disk_cache/stats_histogram.h b/net/disk_cache/stats_histogram.h
index f3af304..0f7685f 100644
--- a/net/disk_cache/stats_histogram.h
+++ b/net/disk_cache/stats_histogram.h
@@ -7,49 +7,47 @@
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+namespace base {
+class BucketRanges;
+class HistogramSamples;
+class SampleVector;
+} // namespace base
+
namespace disk_cache {
class Stats;
// This class provides support for sending the disk cache size stats as a UMA
// histogram. We'll provide our own storage and management for the data, and a
-// SampleSet with a copy of our data.
+// SampleVector with a copy of our data.
//
// Class derivation of Histogram "deprecated," and should not be copied, and
// may eventually go away.
//
class StatsHistogram : public base::Histogram {
public:
- class StatsSamples : public SampleSet {
- public:
- Counts* GetCounts() {
- return &counts_;
- }
- };
-
StatsHistogram(const std::string& name,
Sample minimum,
Sample maximum,
- size_t bucket_count)
- : Histogram(name, minimum, maximum, bucket_count, NULL), init_(false) {}
+ size_t bucket_count,
+ const base::BucketRanges* ranges,
+ const Stats* stats);
virtual ~StatsHistogram();
- static StatsHistogram* FactoryGet(const std::string& name);
-
- // We'll be reporting data from the given set of cache stats.
- bool Init(const Stats* stats);
+ static void InitializeBucketRanges(const Stats* stats,
+ base::BucketRanges* ranges);
+ static StatsHistogram* FactoryGet(const std::string& name,
+ const Stats* stats);
- virtual Sample ranges(size_t i) const OVERRIDE;
- virtual size_t bucket_count() const OVERRIDE;
- virtual void SnapshotSample(SampleSet* sample) const OVERRIDE;
+ virtual scoped_ptr<base::SampleVector> SnapshotSamples() const OVERRIDE;
virtual Inconsistencies FindCorruption(
- const SampleSet& snapshot) const OVERRIDE;
+ const base::HistogramSamples& samples) const OVERRIDE;
private:
- bool init_;
- static const Stats* stats_;
+ const Stats* stats_;
DISALLOW_COPY_AND_ASSIGN(StatsHistogram);
};
diff --git a/net/socket_stream/socket_stream_metrics_unittest.cc b/net/socket_stream/socket_stream_metrics_unittest.cc
index f644f8d..76ad495 100644
--- a/net/socket_stream/socket_stream_metrics_unittest.cc
+++ b/net/socket_stream/socket_stream_metrics_unittest.cc
@@ -5,13 +5,16 @@
#include "net/socket_stream/socket_stream_metrics.h"
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
using base::Histogram;
+using base::HistogramSamples;
using base::StatisticsRecorder;
namespace net {
@@ -20,11 +23,11 @@ TEST(SocketStreamMetricsTest, ProtocolType) {
// First we'll preserve the original values. We need to do this
// as histograms can get affected by other tests. In particular,
// SocketStreamTest and WebSocketTest can affect the histograms.
- Histogram::SampleSet original;
+ scoped_ptr<HistogramSamples> original;
Histogram* histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.ProtocolType");
if (histogram) {
- histogram->SnapshotSample(&original);
+ original = histogram->SnapshotSamples();
}
SocketStreamMetrics unknown(GURL("unknown://www.example.com/"));
@@ -39,22 +42,23 @@ TEST(SocketStreamMetricsTest, ProtocolType) {
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- Histogram::SampleSet sample;
- histogram->SnapshotSample(&sample);
- original.Resize(histogram->bucket_count());
- sample.Subtract(original); // Cancel the original values.
- EXPECT_EQ(1, sample.counts(SocketStreamMetrics::PROTOCOL_UNKNOWN));
- EXPECT_EQ(2, sample.counts(SocketStreamMetrics::PROTOCOL_WEBSOCKET));
- EXPECT_EQ(3, sample.counts(SocketStreamMetrics::PROTOCOL_WEBSOCKET_SECURE));
+ scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples());
+ if (original.get()) {
+ samples->Subtract(*original); // Cancel the original values.
+ }
+ EXPECT_EQ(1, samples->GetCount(SocketStreamMetrics::PROTOCOL_UNKNOWN));
+ EXPECT_EQ(2, samples->GetCount(SocketStreamMetrics::PROTOCOL_WEBSOCKET));
+ EXPECT_EQ(3,
+ samples->GetCount(SocketStreamMetrics::PROTOCOL_WEBSOCKET_SECURE));
}
TEST(SocketStreamMetricsTest, ConnectionType) {
// First we'll preserve the original values.
- Histogram::SampleSet original;
+ scoped_ptr<HistogramSamples> original;
Histogram* histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.ConnectionType");
if (histogram) {
- histogram->SnapshotSample(&original);
+ original = histogram->SnapshotSamples();
}
SocketStreamMetrics metrics(GURL("ws://www.example.com/"));
@@ -73,23 +77,23 @@ TEST(SocketStreamMetricsTest, ConnectionType) {
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- Histogram::SampleSet sample;
- histogram->SnapshotSample(&sample);
- original.Resize(histogram->bucket_count());
- sample.Subtract(original);
- EXPECT_EQ(1, sample.counts(SocketStreamMetrics::ALL_CONNECTIONS));
- EXPECT_EQ(2, sample.counts(SocketStreamMetrics::TUNNEL_CONNECTION));
- EXPECT_EQ(3, sample.counts(SocketStreamMetrics::SOCKS_CONNECTION));
- EXPECT_EQ(4, sample.counts(SocketStreamMetrics::SSL_CONNECTION));
+ scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples());
+ if (original.get()) {
+ samples->Subtract(*original); // Cancel the original values.
+ }
+ EXPECT_EQ(1, samples->GetCount(SocketStreamMetrics::ALL_CONNECTIONS));
+ EXPECT_EQ(2, samples->GetCount(SocketStreamMetrics::TUNNEL_CONNECTION));
+ EXPECT_EQ(3, samples->GetCount(SocketStreamMetrics::SOCKS_CONNECTION));
+ EXPECT_EQ(4, samples->GetCount(SocketStreamMetrics::SSL_CONNECTION));
}
TEST(SocketStreamMetricsTest, WireProtocolType) {
// First we'll preserve the original values.
- Histogram::SampleSet original;
+ scoped_ptr<HistogramSamples> original;
Histogram* histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.WireProtocolType");
if (histogram) {
- histogram->SnapshotSample(&original);
+ original = histogram->SnapshotSamples();
}
SocketStreamMetrics metrics(GURL("ws://www.example.com/"));
@@ -104,12 +108,12 @@ TEST(SocketStreamMetricsTest, WireProtocolType) {
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- Histogram::SampleSet sample;
- histogram->SnapshotSample(&sample);
- original.Resize(histogram->bucket_count());
- sample.Subtract(original);
- EXPECT_EQ(3, sample.counts(SocketStreamMetrics::WIRE_PROTOCOL_WEBSOCKET));
- EXPECT_EQ(7, sample.counts(SocketStreamMetrics::WIRE_PROTOCOL_SPDY));
+ scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples());
+ if (original.get()) {
+ samples->Subtract(*original); // Cancel the original values.
+ }
+ EXPECT_EQ(3, samples->GetCount(SocketStreamMetrics::WIRE_PROTOCOL_WEBSOCKET));
+ EXPECT_EQ(7, samples->GetCount(SocketStreamMetrics::WIRE_PROTOCOL_SPDY));
}
TEST(SocketStreamMetricsTest, OtherNumbers) {
@@ -119,31 +123,31 @@ TEST(SocketStreamMetricsTest, OtherNumbers) {
int64 original_sent_bytes = 0;
int64 original_sent_counts = 0;
- Histogram::SampleSet original;
+ scoped_ptr<HistogramSamples> original;
Histogram* histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedBytes");
if (histogram) {
- histogram->SnapshotSample(&original);
- original_received_bytes = original.sum();
+ original = histogram->SnapshotSamples();
+ original_received_bytes = original->sum();
}
histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedCounts");
if (histogram) {
- histogram->SnapshotSample(&original);
- original_received_counts = original.sum();
+ original = histogram->SnapshotSamples();
+ original_received_counts = original->sum();
}
histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.SentBytes");
if (histogram) {
- histogram->SnapshotSample(&original);
- original_sent_bytes = original.sum();
+ original = histogram->SnapshotSamples();
+ original_sent_bytes = original->sum();
}
histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.SentCounts");
if (histogram) {
- histogram->SnapshotSample(&original);
- original_sent_counts = original.sum();
+ original = histogram->SnapshotSamples();
+ original_sent_counts = original->sum();
}
SocketStreamMetrics metrics(GURL("ws://www.example.com/"));
@@ -157,7 +161,7 @@ TEST(SocketStreamMetricsTest, OtherNumbers) {
metrics.OnWrite(200);
metrics.OnClose();
- Histogram::SampleSet sample;
+ scoped_ptr<HistogramSamples> samples;
// ConnectionLatency.
histogram =
@@ -185,32 +189,32 @@ TEST(SocketStreamMetricsTest, OtherNumbers) {
StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedBytes");
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- histogram->SnapshotSample(&sample);
- EXPECT_EQ(11, sample.sum() - original_received_bytes); // 11 bytes read.
+ samples = histogram->SnapshotSamples();
+ EXPECT_EQ(11, samples->sum() - original_received_bytes); // 11 bytes read.
// ReceivedCounts.
histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedCounts");
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- histogram->SnapshotSample(&sample);
- EXPECT_EQ(2, sample.sum() - original_received_counts); // 2 read requests.
+ samples = histogram->SnapshotSamples();
+ EXPECT_EQ(2, samples->sum() - original_received_counts); // 2 read requests.
// SentBytes.
histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.SentBytes");
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- histogram->SnapshotSample(&sample);
- EXPECT_EQ(222, sample.sum() - original_sent_bytes); // 222 bytes sent.
+ samples = histogram->SnapshotSamples();
+ EXPECT_EQ(222, samples->sum() - original_sent_bytes); // 222 bytes sent.
// SentCounts.
histogram =
StatisticsRecorder::FindHistogram("Net.SocketStream.SentCounts");
ASSERT_TRUE(histogram != NULL);
EXPECT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- histogram->SnapshotSample(&sample);
- EXPECT_EQ(3, sample.sum() - original_sent_counts); // 3 write requests.
+ samples = histogram->SnapshotSamples();
+ EXPECT_EQ(3, samples->sum() - original_sent_counts); // 3 write requests.
}
} // namespace net
diff --git a/net/url_request/url_request_throttler_unittest.cc b/net/url_request/url_request_throttler_unittest.cc
index bc71a4b..221d0c8 100644
--- a/net/url_request/url_request_throttler_unittest.cc
+++ b/net/url_request/url_request_throttler_unittest.cc
@@ -6,8 +6,10 @@
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
+#include "base/stl_util.h"
#include "base/stringprintf.h"
#include "base/string_number_conversions.h"
#include "base/time.h"
@@ -27,6 +29,7 @@ namespace net {
namespace {
using base::Histogram;
+using base::HistogramSamples;
using base::StatisticsRecorder;
class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry {
@@ -172,6 +175,7 @@ class URLRequestThrottlerEntryTest : public testing::Test {
}
virtual void SetUp();
+ virtual void TearDown();
// After calling this function, histogram snapshots in |samples_| contain
// only the delta caused by the test case currently running.
@@ -181,8 +185,8 @@ class URLRequestThrottlerEntryTest : public testing::Test {
MockURLRequestThrottlerManager manager_; // Dummy object, not used.
scoped_refptr<MockURLRequestThrottlerEntry> entry_;
- std::map<std::string, Histogram::SampleSet> original_samples_;
- std::map<std::string, Histogram::SampleSet> samples_;
+ std::map<std::string, HistogramSamples*> original_samples_;
+ std::map<std::string, HistogramSamples*> samples_;
TestURLRequestContext context_;
TestURLRequest request_;
@@ -207,33 +211,38 @@ void URLRequestThrottlerEntryTest::SetUp() {
// Must retrieve original samples for each histogram for comparison
// as other tests may affect them.
const char* name = kHistogramNames[i];
- Histogram::SampleSet& original = original_samples_[name];
Histogram* histogram = StatisticsRecorder::FindHistogram(name);
if (histogram) {
- histogram->SnapshotSample(&original);
+ original_samples_[name] = histogram->SnapshotSamples().release();
+ } else {
+ original_samples_[name] = NULL;
}
}
}
+void URLRequestThrottlerEntryTest::TearDown() {
+ STLDeleteValues(&original_samples_);
+ STLDeleteValues(&samples_);
+}
+
void URLRequestThrottlerEntryTest::CalculateHistogramDeltas() {
for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
const char* name = kHistogramNames[i];
- Histogram::SampleSet& original = original_samples_[name];
- Histogram::SampleSet& sample = samples_[name];
+ HistogramSamples* original = original_samples_[name];
Histogram* histogram = StatisticsRecorder::FindHistogram(name);
if (histogram) {
ASSERT_EQ(Histogram::kUmaTargetedHistogramFlag, histogram->flags());
- histogram->SnapshotSample(&sample);
- // Ensure |original| size is same as |sample|, then subtract original
- // values.
- original.Resize(histogram->bucket_count());
- sample.Subtract(original);
+ scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples());
+ if (original)
+ samples->Subtract(*original);
+ samples_[name] = samples.release();
}
}
// Ensure we don't accidentally use the originals in our tests.
+ STLDeleteValues(&original_samples_);
original_samples_.clear();
}
@@ -251,8 +260,8 @@ TEST_F(URLRequestThrottlerEntryTest, InterfaceDuringExponentialBackoff) {
EXPECT_FALSE(entry_->ShouldRejectRequest(request_));
CalculateHistogramDeltas();
- ASSERT_EQ(1, samples_["Throttling.RequestThrottled"].counts(0));
- ASSERT_EQ(1, samples_["Throttling.RequestThrottled"].counts(1));
+ ASSERT_EQ(1, samples_["Throttling.RequestThrottled"]->GetCount(0));
+ ASSERT_EQ(1, samples_["Throttling.RequestThrottled"]->GetCount(1));
}
TEST_F(URLRequestThrottlerEntryTest, InterfaceNotDuringExponentialBackoff) {
@@ -263,8 +272,8 @@ TEST_F(URLRequestThrottlerEntryTest, InterfaceNotDuringExponentialBackoff) {
EXPECT_FALSE(entry_->ShouldRejectRequest(request_));
CalculateHistogramDeltas();
- ASSERT_EQ(2, samples_["Throttling.RequestThrottled"].counts(0));
- ASSERT_EQ(0, samples_["Throttling.RequestThrottled"].counts(1));
+ ASSERT_EQ(2, samples_["Throttling.RequestThrottled"]->GetCount(0));
+ ASSERT_EQ(0, samples_["Throttling.RequestThrottled"]->GetCount(1));
}
TEST_F(URLRequestThrottlerEntryTest, InterfaceUpdateFailure) {