diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-22 03:42:12 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-22 03:42:12 +0000 |
commit | 2f7d9cdf62cb6729838a9c67e1a6efea7c739302 (patch) | |
tree | 01b27817539910ecffacfceb0f3b27b6d7deb092 | |
parent | 00b8ae882e96f41c41c27d3070897f02dd75d30b (diff) | |
download | chromium_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
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(¤t_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) { |