diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-01 21:34:08 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-01 21:34:08 +0000 |
commit | 34d062327516ae2496646d4c4d644e0e12386a3d (patch) | |
tree | d92e5703447f10aea57b1bdcfea060600252be9b | |
parent | ba125e774ed899bd119bcd9abd47dab68857315b (diff) | |
download | chromium_src-34d062327516ae2496646d4c4d644e0e12386a3d.zip chromium_src-34d062327516ae2496646d4c4d644e0e12386a3d.tar.gz chromium_src-34d062327516ae2496646d4c4d644e0e12386a3d.tar.bz2 |
This is a major refactor of Histogram related code:
1. Remove duplicated code from histogram.h/.cc,
including validating related code and BucketRanges related.
2. Constness of BucketRanges from Histograms, to prevent accidentally modification and provide a simpler interface.
3. Add/move tests.
Review URL: https://chromiumcodereview.appspot.com/10834011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149495 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/base.gyp | 1 | ||||
-rw-r--r-- | base/metrics/bucket_ranges.cc | 6 | ||||
-rw-r--r-- | base/metrics/bucket_ranges.h | 8 | ||||
-rw-r--r-- | base/metrics/bucket_ranges_unittest.cc | 16 | ||||
-rw-r--r-- | base/metrics/histogram.cc | 870 | ||||
-rw-r--r-- | base/metrics/histogram.h | 178 | ||||
-rw-r--r-- | base/metrics/histogram_base.cc | 2 | ||||
-rw-r--r-- | base/metrics/histogram_base.h | 2 | ||||
-rw-r--r-- | base/metrics/histogram_snapshot_manager.cc | 3 | ||||
-rw-r--r-- | base/metrics/histogram_unittest.cc | 408 | ||||
-rw-r--r-- | base/metrics/statistics_recorder.cc | 242 | ||||
-rw-r--r-- | base/metrics/statistics_recorder.h | 40 | ||||
-rw-r--r-- | base/metrics/statistics_recorder_unittest.cc | 256 | ||||
-rw-r--r-- | chrome/common/metrics/metrics_log_base.cc | 2 | ||||
-rw-r--r-- | content/common/child_histogram_message_filter.cc | 2 | ||||
-rw-r--r-- | net/disk_cache/stats_histogram.cc | 12 | ||||
-rw-r--r-- | net/disk_cache/stats_histogram.h | 11 | ||||
-rw-r--r-- | net/socket_stream/socket_stream_metrics_unittest.cc | 6 | ||||
-rw-r--r-- | net/url_request/url_request_throttler_unittest.cc | 2 |
19 files changed, 1065 insertions, 1002 deletions
diff --git a/base/base.gyp b/base/base.gyp index 1224805..49fa15f 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -438,6 +438,7 @@ 'metrics/field_trial_unittest.cc', 'metrics/histogram_unittest.cc', 'metrics/stats_table_unittest.cc', + 'metrics/statistics_recorder_unittest.cc', 'observer_list_unittest.cc', 'os_compat_android_unittest.cc', 'path_service_unittest.cc', diff --git a/base/metrics/bucket_ranges.cc b/base/metrics/bucket_ranges.cc index 06b2a43..748abc6 100644 --- a/base/metrics/bucket_ranges.cc +++ b/base/metrics/bucket_ranges.cc @@ -11,7 +11,7 @@ namespace base { // Static table of checksums for all possible 8 bit bytes. -static const uint32 kCrcTable[256] = { 0x0, 0x77073096L, 0xee0e612cL, +const uint32 kCrcTable[256] = { 0x0, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, @@ -107,7 +107,7 @@ void BucketRanges::set_range(size_t i, HistogramBase::Sample value) { ranges_[i] = value; } -uint32 BucketRanges::CalculateChecksum() { +uint32 BucketRanges::CalculateChecksum() const { // Seed checksum. uint32 checksum = static_cast<uint32>(ranges_.size()); @@ -116,7 +116,7 @@ uint32 BucketRanges::CalculateChecksum() { return checksum; } -bool BucketRanges::HasValidChecksum() { +bool BucketRanges::HasValidChecksum() const { return CalculateChecksum() == checksum_; } diff --git a/base/metrics/bucket_ranges.h b/base/metrics/bucket_ranges.h index 06429bd..b77f52e 100644 --- a/base/metrics/bucket_ranges.h +++ b/base/metrics/bucket_ranges.h @@ -41,8 +41,8 @@ class BASE_EXPORT_PRIVATE BucketRanges { // Checksum methods to verify whether the ranges are corrupted (e.g. bad // memory access). - uint32 CalculateChecksum(); - bool HasValidChecksum(); + uint32 CalculateChecksum() const; + bool HasValidChecksum() const; void ResetChecksum(); // Return true iff |other| object has same ranges_ as |this| object's ranges_. @@ -64,6 +64,10 @@ class BASE_EXPORT_PRIVATE BucketRanges { DISALLOW_COPY_AND_ASSIGN(BucketRanges); }; +////////////////////////////////////////////////////////////////////////////// +// Expose only for test. +BASE_EXPORT_PRIVATE extern const uint32 kCrcTable[256]; + } // namespace base #endif // BASE_METRICS_BUCKET_RANGES_H_ diff --git a/base/metrics/bucket_ranges_unittest.cc b/base/metrics/bucket_ranges_unittest.cc index 97e164a..020154e 100644 --- a/base/metrics/bucket_ranges_unittest.cc +++ b/base/metrics/bucket_ranges_unittest.cc @@ -71,5 +71,21 @@ TEST(BucketRangesTest, Checksum) { EXPECT_TRUE(ranges.HasValidChecksum()); } +// Table was generated similarly to sample code for CRC-32 given on: +// http://www.w3.org/TR/PNG/#D-CRCAppendix. +TEST(HistogramTest, Crc32TableTest) { + for (int i = 0; i < 256; ++i) { + uint32 checksum = i; + for (int j = 0; j < 8; ++j) { + const uint32 kReversedPolynomial = 0xedb88320L; + if (checksum & 1) + checksum = kReversedPolynomial ^ (checksum >> 1); + else + checksum >>= 1; + } + EXPECT_EQ(kCrcTable[i], checksum); + } +} + } // namespace } // namespace base diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index d1fcb56..40f5c6d 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc @@ -20,95 +20,143 @@ #include "base/stringprintf.h" #include "base/synchronization/lock.h" +using std::string; +using std::vector; + namespace base { -// Static table of checksums for all possible 8 bit bytes. -const uint32 Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, -0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, -0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, -0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, -0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, -0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, -0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, -0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, -0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, -0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, -0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, -0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, -0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL, -0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, -0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, -0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, -0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, -0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, -0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, -0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, -0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, -0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, -0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L, -0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, -0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, -0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, -0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, -0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, -0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, -0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, -0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, -0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, -0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, -0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L, -0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, -0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, -0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, -0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, -0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, -0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, -0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, -0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, -0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, -0x2d02ef8dL, -}; - -typedef Histogram::Count Count; +typedef HistogramBase::Count Count; +typedef HistogramBase::Sample Sample; // static const size_t Histogram::kBucketCount_MAX = 16384u; -Histogram* Histogram::FactoryGet(const std::string& name, +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_; +} + +Histogram* Histogram::FactoryGet(const string& name, Sample minimum, Sample maximum, size_t bucket_count, Flags flags) { - // Defensive code. - if (minimum < 1) - minimum = 1; - if (maximum > kSampleType_MAX - 1) - maximum = kSampleType_MAX - 1; - - DCHECK_GT(maximum, minimum); - DCHECK_GT((Sample) bucket_count, 2); - DCHECK_LE((Sample) bucket_count, maximum - minimum + 2); + CHECK(InspectConstructionArguments(name, &minimum, &maximum, &bucket_count)); Histogram* histogram = StatisticsRecorder::FindHistogram(name); if (!histogram) { - // Extra variable is not needed... but this keeps this section basically - // identical to other derived classes in this file (and compiler will - // optimize away the extra variable. // To avoid racy destruction at shutdown, the following will be leaked. + BucketRanges* ranges = new BucketRanges(bucket_count + 1); + InitializeBucketRanges(minimum, maximum, bucket_count, ranges); + const BucketRanges* registered_ranges = + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + Histogram* tentative_histogram = - new Histogram(name, minimum, maximum, bucket_count); - tentative_histogram->InitializeBucketRange(); + new Histogram(name, minimum, maximum, bucket_count, registered_ranges); tentative_histogram->SetFlags(flags); histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); } - DCHECK_EQ(HISTOGRAM, histogram->histogram_type()); - DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + CHECK_EQ(HISTOGRAM, histogram->histogram_type()); + CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count)); return histogram; } -Histogram* Histogram::FactoryTimeGet(const std::string& name, +Histogram* Histogram::FactoryTimeGet(const string& name, TimeDelta minimum, TimeDelta maximum, size_t bucket_count, @@ -125,6 +173,47 @@ TimeTicks Histogram::DebugNow() { #endif } +// Calculate what range of values are held in each bucket. +// We have to be careful that we don't pick a ratio between starting points in +// consecutive buckets that is sooo small, that the integer bounds are the same +// (effectively making one bucket get no values). We need to avoid: +// ranges(i) == ranges(i + 1) +// To avoid that, we just do a fine-grained bucket width as far as we need to +// until we get a ratio that moves us along at least 2 units at a time. From +// that bucket onward we do use the exponential growth of buckets. +// +// static +void Histogram::InitializeBucketRanges(Sample minimum, + Sample maximum, + size_t bucket_count, + BucketRanges* ranges) { + DCHECK_EQ(ranges->size(), bucket_count + 1); + double log_max = log(static_cast<double>(maximum)); + double log_ratio; + double log_next; + size_t bucket_index = 1; + Sample current = minimum; + ranges->set_range(bucket_index, current); + while (bucket_count > ++bucket_index) { + double log_current; + log_current = log(static_cast<double>(current)); + // Calculate the count'th root of the range. + log_ratio = (log_max - log_current) / (bucket_count - bucket_index); + // See where the next bucket would start. + log_next = log_current + log_ratio; + Sample next; + next = static_cast<int>(floor(exp(log_next) + 0.5)); + if (next > current) + current = next; + else + ++current; // Just do a narrow bucket, and keep trying. + ranges->set_range(bucket_index, current); + } + ranges->set_range(ranges->size() - 1, HistogramBase::kSampleType_MAX); + ranges->ResetChecksum(); +} + +// static void Histogram::Add(int value) { if (value > kSampleType_MAX - 1) value = kSampleType_MAX - 1; @@ -149,93 +238,29 @@ void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { } // The following methods provide a graphical histogram display. -void Histogram::WriteHTMLGraph(std::string* output) const { +void Histogram::WriteHTMLGraph(string* output) const { // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. output->append("<PRE>"); WriteAsciiImpl(true, "<br>", output); output->append("</PRE>"); } -void Histogram::WriteAscii(std::string* output) const { +void Histogram::WriteAscii(string* output) const { WriteAsciiImpl(true, "\n", output); } -void Histogram::WriteAsciiImpl(bool graph_it, - const std::string& newline, - std::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(); - - 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); - - // 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)) { - if (0 == largest_non_empty_bucket) - break; // All buckets are empty. - --largest_non_empty_bucket; - } - - // 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)) { - size_t width = GetAsciiBucketRange(i).size() + 1; - if (width > print_width) - print_width = width; - } - } - - int64 remaining = sample_count; - int64 past = 0; - // Output the actual histogram graph. - for (size_t i = 0; i < bucket_count(); ++i) { - Count current = snapshot.counts(i); - if (!current && !PrintEmptyBucket(i)) - continue; - remaining -= current; - std::string range = GetAsciiBucketRange(i); - 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)) - ++i; - output->append("... "); - output->append(newline); - continue; // No reason to plot emptiness. - } - double current_size = GetBucketSize(current, i); - if (graph_it) - WriteAsciiBucketGraph(current_size, max_size, output); - WriteAsciiBucketContext(past, current, remaining, i, output); - output->append(newline); - past += current; - } - DCHECK_EQ(sample_count, past); -} - // static -std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, - const SampleSet& snapshot) { +string Histogram::SerializeHistogramInfo(const Histogram& histogram, + const SampleSet& snapshot) { DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); + DCHECK(histogram.bucket_ranges()->HasValidChecksum()); Pickle pickle; pickle.WriteString(histogram.histogram_name()); pickle.WriteInt(histogram.declared_min()); pickle.WriteInt(histogram.declared_max()); pickle.WriteUInt64(histogram.bucket_count()); - pickle.WriteUInt32(histogram.range_checksum()); + pickle.WriteUInt32(histogram.bucket_ranges()->checksum()); pickle.WriteInt(histogram.histogram_type()); pickle.WriteInt(histogram.flags()); @@ -243,18 +268,18 @@ std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, histogram.SerializeRanges(&pickle); - return std::string(static_cast<const char*>(pickle.data()), pickle.size()); + return string(static_cast<const char*>(pickle.data()), pickle.size()); } // static -bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { +bool Histogram::DeserializeHistogramInfo(const string& histogram_info) { if (histogram_info.empty()) { return false; } Pickle pickle(histogram_info.data(), static_cast<int>(histogram_info.size())); - std::string histogram_name; + string histogram_name; int declared_min; int declared_max; uint64 bucket_count; @@ -300,7 +325,7 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { } else if (histogram_type == BOOLEAN_HISTOGRAM) { render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); } else if (histogram_type == CUSTOM_HISTOGRAM) { - std::vector<Histogram::Sample> sample_ranges(bucket_count); + vector<Sample> sample_ranges(bucket_count); if (!CustomHistogram::DeserializeRanges(&iter, &sample_ranges)) { DLOG(ERROR) << "Pickle error decoding ranges: " << histogram_name; return false; @@ -316,9 +341,12 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { DCHECK_EQ(render_histogram->declared_min(), declared_min); DCHECK_EQ(render_histogram->declared_max(), declared_max); DCHECK_EQ(render_histogram->bucket_count(), bucket_count); - DCHECK_EQ(render_histogram->range_checksum(), range_checksum); DCHECK_EQ(render_histogram->histogram_type(), histogram_type); + if (render_histogram->bucket_ranges()->checksum() != range_checksum) { + return false; + } + if (render_histogram->flags() & kIPCSerializationSourceFlag) { DVLOG(1) << "Single process mode, histogram observed and not copied: " << histogram_name; @@ -330,10 +358,8 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { return true; } -//------------------------------------------------------------------------------ -// Methods for the validating a sample and a related histogram. -//------------------------------------------------------------------------------ +// Validate a sample and related histogram. Histogram::Inconsistencies Histogram::FindCorruption( const SampleSet& snapshot) const { int inconsistencies = NO_INCONSISTENCIES; @@ -347,7 +373,7 @@ Histogram::Inconsistencies Histogram::FindCorruption( previous_range = new_range; } - if (!HasValidRangeChecksum()) + if (!bucket_ranges()->HasValidChecksum()) inconsistencies |= RANGE_CHECKSUM_ERROR; int64 delta64 = snapshot.redundant_count() - count; @@ -382,7 +408,7 @@ Histogram::ClassType Histogram::histogram_type() const { return HISTOGRAM; } -Histogram::Sample Histogram::ranges(size_t i) const { +Sample Histogram::ranges(size_t i) const { return bucket_ranges_->range(i); } @@ -397,99 +423,58 @@ void Histogram::SnapshotSample(SampleSet* sample) const { *sample = sample_; } -bool Histogram::HasConstructorArguments(Sample minimum, - Sample maximum, - size_t bucket_count) { +bool Histogram::HasConstructionArguments(Sample minimum, + Sample maximum, + size_t bucket_count) { return ((minimum == declared_min_) && (maximum == declared_max_) && (bucket_count == bucket_count_)); } -bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, - TimeDelta maximum, - size_t bucket_count) { - return ((minimum.InMilliseconds() == declared_min_) && - (maximum.InMilliseconds() == declared_max_) && - (bucket_count == bucket_count_)); -} - -bool Histogram::HasValidRangeChecksum() const { - return CalculateRangeChecksum() == range_checksum_; -} - -Histogram::Histogram(const std::string& name, Sample minimum, - Sample maximum, size_t bucket_count) +Histogram::Histogram(const string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + const BucketRanges* ranges) : HistogramBase(name), + bucket_ranges_(ranges), declared_min_(minimum), declared_max_(maximum), bucket_count_(bucket_count), flags_(kNoFlags), - bucket_ranges_(new BucketRanges(bucket_count + 1)), - range_checksum_(0), - sample_() { - Initialize(); -} - -Histogram::Histogram(const std::string& name, TimeDelta minimum, - TimeDelta maximum, size_t bucket_count) - : HistogramBase(name), - declared_min_(static_cast<int> (minimum.InMilliseconds())), - declared_max_(static_cast<int> (maximum.InMilliseconds())), - bucket_count_(bucket_count), - flags_(kNoFlags), - bucket_ranges_(new BucketRanges(bucket_count + 1)), - range_checksum_(0), - sample_() { - Initialize(); -} + sample_(bucket_count) {} Histogram::~Histogram() { if (StatisticsRecorder::dump_on_exit()) { - std::string output; + string output; WriteAsciiImpl(true, "\n", &output); DLOG(INFO) << output; } - - // Just to make sure most derived class did this properly... - DCHECK(ValidateBucketRanges()); } -bool Histogram::SerializeRanges(Pickle* pickle) const { +// static +bool Histogram::InspectConstructionArguments(const string& name, + Sample* minimum, + Sample* maximum, + size_t* bucket_count) { + // Defensive code for backward compatibility. + if (*minimum < 1) { + DLOG(WARNING) << "Histogram: " << name << " Bad minimum: " << *minimum; + *minimum = 1; + } + if (*maximum >= kSampleType_MAX) { + DLOG(WARNING) << "Histogram: " << name << " Bad maximum: " << *maximum; + *maximum = kSampleType_MAX; + } + + if (*bucket_count < 3 || *bucket_count >= kBucketCount_MAX) + return false; + if (*bucket_count > static_cast<size_t>(*maximum - *minimum + 2)) + return false; return true; } -// Calculate what range of values are held in each bucket. -// We have to be careful that we don't pick a ratio between starting points in -// consecutive buckets that is sooo small, that the integer bounds are the same -// (effectively making one bucket get no values). We need to avoid: -// ranges(i) == ranges(i + 1) -// To avoid that, we just do a fine-grained bucket width as far as we need to -// until we get a ratio that moves us along at least 2 units at a time. From -// that bucket onward we do use the exponential growth of buckets. -void Histogram::InitializeBucketRange() { - double log_max = log(static_cast<double>(declared_max())); - double log_ratio; - double log_next; - size_t bucket_index = 1; - Sample current = declared_min(); - SetBucketRange(bucket_index, current); - while (bucket_count() > ++bucket_index) { - double log_current; - log_current = log(static_cast<double>(current)); - // Calculate the count'th root of the range. - log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); - // See where the next bucket would start. - log_next = log_current + log_ratio; - int next; - next = static_cast<int>(floor(exp(log_next) + 0.5)); - if (next > current) - current = next; - else - ++current; // Just do a narrow bucket, and keep trying. - SetBucketRange(bucket_index, current); - } - ResetRangeChecksum(); - - DCHECK_EQ(bucket_count(), bucket_index); +bool Histogram::SerializeRanges(Pickle* pickle) const { + return true; } bool Histogram::PrintEmptyBucket(size_t index) const { @@ -535,12 +520,8 @@ double Histogram::GetBucketSize(Count current, size_t i) const { return current/denominator; } -void Histogram::ResetRangeChecksum() { - range_checksum_ = CalculateRangeChecksum(); -} - -const std::string Histogram::GetAsciiBucketRange(size_t i) const { - std::string result; +const string Histogram::GetAsciiBucketRange(size_t i) const { + string result; if (kHexRangePrintingFlag & flags_) StringAppendF(&result, "%#x", ranges(i)); else @@ -554,84 +535,73 @@ void Histogram::Accumulate(Sample value, Count count, size_t index) { sample_.Accumulate(value, count, index); } -void Histogram::SetBucketRange(size_t i, Sample value) { - DCHECK_GT(bucket_count_, i); - DCHECK_GE(value, 0); - bucket_ranges_->set_range(i, value); -} +//------------------------------------------------------------------------------ +// Private methods -bool Histogram::ValidateBucketRanges() const { - // Standard assertions that all bucket ranges should satisfy. - DCHECK_EQ(bucket_count_ + 1, bucket_ranges_->size()); - DCHECK_EQ(0, ranges(0)); - DCHECK_EQ(declared_min(), ranges(1)); - DCHECK_EQ(declared_max(), ranges(bucket_count_ - 1)); - DCHECK_EQ(kSampleType_MAX, ranges(bucket_count_)); - return true; -} +void Histogram::WriteAsciiImpl(bool graph_it, + const string& newline, + 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(); -uint32 Histogram::CalculateRangeChecksum() const { - DCHECK_EQ(bucket_ranges_->size(), bucket_count() + 1); - // Seed checksum. - uint32 checksum = static_cast<uint32>(bucket_ranges_->size()); - for (size_t index = 0; index < bucket_count(); ++index) - checksum = Crc32(checksum, ranges(index)); - return checksum; -} + WriteAsciiHeader(snapshot, sample_count, output); + output->append(newline); -void Histogram::Initialize() { - sample_.Resize(*this); - if (declared_min_ < 1) - declared_min_ = 1; - if (declared_max_ > kSampleType_MAX - 1) - declared_max_ = kSampleType_MAX - 1; - DCHECK_LE(declared_min_, declared_max_); - DCHECK_GT(bucket_count_, 1u); - CHECK_LT(bucket_count_, kBucketCount_MAX); - size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; - DCHECK_LE(bucket_count_, maximal_bucket_count); - DCHECK_EQ(0, ranges(0)); - bucket_ranges_->set_range(bucket_count_, kSampleType_MAX); -} + // Prepare to normalize graphical rendering of bucket contents. + double max_size = 0; + if (graph_it) + max_size = GetPeakBucketSize(snapshot); -// We generate the CRC-32 using the low order bits to select whether to XOR in -// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us -// to keep the quotient in a uint32. Since we're not concerned about the nature -// of corruptions (i.e., we don't care about bit sequencing, since we are -// handling memory changes, which are more grotesque) so we don't bother to -// get the CRC correct for big-endian vs little-ending calculations. All we -// need is a nice hash, that tends to depend on all the bits of the sample, with -// very little chance of changes in one place impacting changes in another -// place. -uint32 Histogram::Crc32(uint32 sum, Histogram::Sample range) { - const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats. - if (kUseRealCrc) { - union { - Histogram::Sample range; - unsigned char bytes[sizeof(Histogram::Sample)]; - } converter; - converter.range = range; - for (size_t i = 0; i < sizeof(converter); ++i) - sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8); - } else { - // Use hash techniques provided in ReallyFastHash, except we don't care - // about "avalanching" (which would worsten the hash, and add collisions), - // and we don't care about edge cases since we have an even number of bytes. - union { - Histogram::Sample range; - uint16 ints[sizeof(Histogram::Sample) / 2]; - } converter; - DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter)); - converter.range = range; - sum += converter.ints[0]; - sum = (sum << 16) ^ sum ^ (static_cast<uint32>(converter.ints[1]) << 11); - sum += sum >> 11; + // 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)) { + if (0 == largest_non_empty_bucket) + break; // All buckets are empty. + --largest_non_empty_bucket; } - return sum; -} -//------------------------------------------------------------------------------ -// Private methods + // 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)) { + size_t width = GetAsciiBucketRange(i).size() + 1; + if (width > print_width) + print_width = width; + } + } + + int64 remaining = sample_count; + int64 past = 0; + // Output the actual histogram graph. + for (size_t i = 0; i < bucket_count(); ++i) { + Count current = snapshot.counts(i); + if (!current && !PrintEmptyBucket(i)) + continue; + remaining -= current; + string range = GetAsciiBucketRange(i); + 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)) + ++i; + output->append("... "); + output->append(newline); + continue; // No reason to plot emptiness. + } + double current_size = GetBucketSize(current, i); + if (graph_it) + WriteAsciiBucketGraph(current_size, max_size, output); + WriteAsciiBucketContext(past, current, remaining, i, output); + output->append(newline); + past += current; + } + DCHECK_EQ(sample_count, past); +} double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { double max = 0; @@ -645,7 +615,7 @@ double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { void Histogram::WriteAsciiHeader(const SampleSet& snapshot, Count sample_count, - std::string* output) const { + string* output) const { StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name().c_str(), @@ -665,7 +635,7 @@ void Histogram::WriteAsciiBucketContext(const int64 past, const Count current, const int64 remaining, const size_t i, - std::string* output) const { + string* output) const { double scaled_sum = (past + current + remaining) / 100.0; WriteAsciiBucketValue(current, scaled_sum, output); if (0 < i) { @@ -674,13 +644,15 @@ void Histogram::WriteAsciiBucketContext(const int64 past, } } -void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, - std::string* output) const { +void Histogram::WriteAsciiBucketValue(Count current, + double scaled_sum, + string* output) const { StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); } -void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, - std::string* output) const { +void Histogram::WriteAsciiBucketGraph(double current_size, + double max_size, + string* output) const { const int k_line_length = 72; // Maximal horizontal width of graph. int x_count = static_cast<int>(k_line_length * (current_size / max_size) + 0.5); @@ -694,148 +666,42 @@ void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, } //------------------------------------------------------------------------------ -// Methods for the Histogram::SampleSet class -//------------------------------------------------------------------------------ - -Histogram::SampleSet::SampleSet() - : counts_(), - sum_(0), - redundant_count_(0) { -} - -Histogram::SampleSet::~SampleSet() { -} - -void Histogram::SampleSet::Resize(const Histogram& histogram) { - counts_.resize(histogram.bucket_count(), 0); -} - -void Histogram::SampleSet::CheckSize(const Histogram& histogram) const { - DCHECK_EQ(histogram.bucket_count(), counts_.size()); -} - - -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_; -} - -//------------------------------------------------------------------------------ // LinearHistogram: This histogram uses a traditional set of evenly spaced // buckets. //------------------------------------------------------------------------------ -LinearHistogram::~LinearHistogram() { -} +LinearHistogram::~LinearHistogram() {} -Histogram* LinearHistogram::FactoryGet(const std::string& name, +Histogram* LinearHistogram::FactoryGet(const string& name, Sample minimum, Sample maximum, size_t bucket_count, Flags flags) { - if (minimum < 1) - minimum = 1; - if (maximum > kSampleType_MAX - 1) - maximum = kSampleType_MAX - 1; - - DCHECK_GT(maximum, minimum); - DCHECK_GT((Sample) bucket_count, 2); - DCHECK_LE((Sample) bucket_count, maximum - minimum + 2); + CHECK(Histogram::InspectConstructionArguments(name, &minimum, &maximum, + &bucket_count)); Histogram* histogram = StatisticsRecorder::FindHistogram(name); if (!histogram) { // To avoid racy destruction at shutdown, the following will be leaked. + BucketRanges* ranges = new BucketRanges(bucket_count + 1); + InitializeBucketRanges(minimum, maximum, bucket_count, ranges); + const BucketRanges* registered_ranges = + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + LinearHistogram* tentative_histogram = - new LinearHistogram(name, minimum, maximum, bucket_count); - tentative_histogram->InitializeBucketRange(); + new LinearHistogram(name, minimum, maximum, bucket_count, + registered_ranges); tentative_histogram->SetFlags(flags); histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); } - DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); - DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + CHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); + CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count)); return histogram; } -Histogram* LinearHistogram::FactoryTimeGet(const std::string& name, +Histogram* LinearHistogram::FactoryTimeGet(const string& name, TimeDelta minimum, TimeDelta maximum, size_t bucket_count, @@ -855,33 +721,12 @@ void LinearHistogram::SetRangeDescriptions( } } -LinearHistogram::LinearHistogram(const std::string& name, +LinearHistogram::LinearHistogram(const string& name, Sample minimum, Sample maximum, - size_t bucket_count) - : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { -} - -LinearHistogram::LinearHistogram(const std::string& name, - TimeDelta minimum, - TimeDelta maximum, - size_t bucket_count) - : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? - minimum : TimeDelta::FromMilliseconds(1), - maximum, bucket_count) { -} - -void LinearHistogram::InitializeBucketRange() { - DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. - double min = declared_min(); - double max = declared_max(); - size_t i; - for (i = 1; i < bucket_count(); ++i) { - double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / - (bucket_count() - 2); - SetBucketRange(i, static_cast<int> (linear_range + 0.5)); - } - ResetRangeChecksum(); + size_t bucket_count, + const BucketRanges* ranges) + : Histogram(name, minimum, maximum, bucket_count, ranges) { } double LinearHistogram::GetBucketSize(Count current, size_t i) const { @@ -892,7 +737,7 @@ double LinearHistogram::GetBucketSize(Count current, size_t i) const { return current/denominator; } -const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { +const string LinearHistogram::GetAsciiBucketRange(size_t i) const { int range = ranges(i); BucketDescriptionMap::const_iterator it = bucket_description_.find(range); if (it == bucket_description_.end()) @@ -904,23 +749,45 @@ bool LinearHistogram::PrintEmptyBucket(size_t index) const { return bucket_description_.find(ranges(index)) == bucket_description_.end(); } +// static +void LinearHistogram::InitializeBucketRanges(Sample minimum, + Sample maximum, + size_t bucket_count, + BucketRanges* ranges) { + DCHECK_EQ(ranges->size(), bucket_count + 1); + double min = minimum; + double max = maximum; + size_t i; + for (i = 1; i < bucket_count; ++i) { + double linear_range = + (min * (bucket_count -1 - i) + max * (i - 1)) / (bucket_count - 2); + ranges->set_range(i, static_cast<Sample>(linear_range + 0.5)); + } + ranges->set_range(ranges->size() - 1, HistogramBase::kSampleType_MAX); + ranges->ResetChecksum(); +} //------------------------------------------------------------------------------ // This section provides implementation for BooleanHistogram. //------------------------------------------------------------------------------ -Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) { +Histogram* BooleanHistogram::FactoryGet(const string& name, Flags flags) { Histogram* histogram = StatisticsRecorder::FindHistogram(name); if (!histogram) { // To avoid racy destruction at shutdown, the following will be leaked. - BooleanHistogram* tentative_histogram = new BooleanHistogram(name); - tentative_histogram->InitializeBucketRange(); + BucketRanges* ranges = new BucketRanges(4); + LinearHistogram::InitializeBucketRanges(1, 2, 3, ranges); + const BucketRanges* registered_ranges = + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + + BooleanHistogram* tentative_histogram = + new BooleanHistogram(name, registered_ranges); tentative_histogram->SetFlags(flags); histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); } - DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); + CHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); return histogram; } @@ -932,43 +799,35 @@ void BooleanHistogram::AddBoolean(bool value) { Add(value ? 1 : 0); } -BooleanHistogram::BooleanHistogram(const std::string& name) - : LinearHistogram(name, 1, 2, 3) { -} +BooleanHistogram::BooleanHistogram(const string& name, + const BucketRanges* ranges) + : LinearHistogram(name, 1, 2, 3, ranges) {} //------------------------------------------------------------------------------ // CustomHistogram: //------------------------------------------------------------------------------ -Histogram* CustomHistogram::FactoryGet(const std::string& name, - const std::vector<Sample>& custom_ranges, +Histogram* CustomHistogram::FactoryGet(const string& name, + const vector<Sample>& custom_ranges, Flags flags) { - // Remove the duplicates in the custom ranges array. - std::vector<int> ranges = custom_ranges; - ranges.push_back(0); // Ensure we have a zero value. - std::sort(ranges.begin(), ranges.end()); - ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); - if (ranges.size() <= 1) { - DCHECK(false); - // Note that we pushed a 0 in above, so for defensive code.... - ranges.push_back(1); // Put in some data so we can index to [1]. - } - - DCHECK_LT(ranges.back(), kSampleType_MAX); + CHECK(ValidateCustomRanges(custom_ranges)); Histogram* histogram = StatisticsRecorder::FindHistogram(name); if (!histogram) { + BucketRanges* ranges = CreateBucketRangesFromCustomRanges(custom_ranges); + const BucketRanges* registered_ranges = + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + // To avoid racy destruction at shutdown, the following will be leaked. - CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges); - tentative_histogram->InitializedCustomBucketRange(ranges); + CustomHistogram* tentative_histogram = + new CustomHistogram(name, registered_ranges); tentative_histogram->SetFlags(flags); + histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); } - DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); - DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(), - ranges.size())); + CHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); return histogram; } @@ -977,9 +836,9 @@ Histogram::ClassType CustomHistogram::histogram_type() const { } // static -std::vector<Histogram::Sample> CustomHistogram::ArrayToCustomRanges( +vector<Sample> CustomHistogram::ArrayToCustomRanges( const Sample* values, size_t num_values) { - std::vector<Sample> all_values; + vector<Sample> all_values; for (size_t i = 0; i < num_values; ++i) { Sample value = values[i]; all_values.push_back(value); @@ -991,13 +850,13 @@ std::vector<Histogram::Sample> CustomHistogram::ArrayToCustomRanges( return all_values; } -CustomHistogram::CustomHistogram(const std::string& name, - const std::vector<Sample>& custom_ranges) - : Histogram(name, custom_ranges[1], custom_ranges.back(), - custom_ranges.size()) { - DCHECK_GT(custom_ranges.size(), 1u); - DCHECK_EQ(custom_ranges[0], 0); -} +CustomHistogram::CustomHistogram(const string& name, + const BucketRanges* ranges) + : Histogram(name, + ranges->range(1), + ranges->range(ranges->size() - 2), + ranges->size() - 1, + ranges) {} bool CustomHistogram::SerializeRanges(Pickle* pickle) const { for (size_t i = 0; i < bucket_ranges()->size(); ++i) { @@ -1009,7 +868,7 @@ bool CustomHistogram::SerializeRanges(Pickle* pickle) const { // static bool CustomHistogram::DeserializeRanges( - PickleIterator* iter, std::vector<Histogram::Sample>* ranges) { + PickleIterator* iter, vector<Sample>* ranges) { for (size_t i = 0; i < ranges->size(); ++i) { if (!iter->ReadInt(&(*ranges)[i])) return false; @@ -1017,18 +876,39 @@ bool CustomHistogram::DeserializeRanges( return true; } -void CustomHistogram::InitializedCustomBucketRange( - const std::vector<Sample>& custom_ranges) { - DCHECK_GT(custom_ranges.size(), 1u); - DCHECK_EQ(custom_ranges[0], 0); - DCHECK_LE(custom_ranges.size(), bucket_count()); - for (size_t index = 0; index < custom_ranges.size(); ++index) - SetBucketRange(index, custom_ranges[index]); - ResetRangeChecksum(); -} - double CustomHistogram::GetBucketSize(Count current, size_t i) const { return 1; } +// static +bool CustomHistogram::ValidateCustomRanges( + const vector<Sample>& custom_ranges) { + if (custom_ranges.size() < 1) + return false; + for (size_t i = 0; i < custom_ranges.size(); i++) { + Sample s = custom_ranges[i]; + if (s < 0 || s > HistogramBase::kSampleType_MAX - 1) + return false; + } + return true; +} + +// static +BucketRanges* CustomHistogram::CreateBucketRangesFromCustomRanges( + const vector<Sample>& custom_ranges) { + // Remove the duplicates in the custom ranges array. + vector<int> ranges = custom_ranges; + ranges.push_back(0); // Ensure we have a zero value. + ranges.push_back(HistogramBase::kSampleType_MAX); + std::sort(ranges.begin(), ranges.end()); + ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); + + BucketRanges* bucket_ranges = new BucketRanges(ranges.size()); + for (size_t i = 0; i < ranges.size(); i++) { + bucket_ranges->set_range(i, ranges[i]); + } + bucket_ranges->ResetChecksum(); + return bucket_ranges; +} + } // namespace base diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 8580ea3..a969dcfa 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h @@ -9,14 +9,31 @@ // It supports calls to accumulate either time intervals (which are processed // as integral number of milliseconds), or arbitrary integral units. -// The default layout of buckets is exponential. For example, buckets might -// contain (sequentially) the count of values in the following intervals: +// For Histogram(exponential histogram), LinearHistogram and CustomHistogram, +// the minimum for a declared range is 1 (instead of 0), while the maximum is +// (HistogramBase::kSampleType_MAX - 1). Currently you can declare histograms +// with ranges exceeding those limits (e.g. 0 as minimal or +// HistogramBase::kSampleType_MAX as maximal), but those excesses will be +// silently clamped to those limits (for backwards compatibility with existing +// code). Best practice is to not exceed the limits. + +// For Histogram and LinearHistogram, the maximum for a declared range should +// always be larger (not equal) than minmal range. Zero and +// HistogramBase::kSampleType_MAX are implicitly added as first and last ranges, +// so the smallest legal bucket_count is 3. However CustomHistogram can have +// bucket count as 2 (when you give a custom ranges vector containing only 1 +// range). +// For these 3 kinds of histograms, the max bucket count is always +// (Histogram::kBucketCount_MAX - 1). + +// The buckets layout of class Histogram is exponential. For example, buckets +// might contain (sequentially) the count of values in the following intervals: // [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) // That bucket allocation would actually result from construction of a histogram // for values between 1 and 64, with 8 buckets, such as: -// Histogram count(L"some name", 1, 64, 8); +// Histogram count("some name", 1, 64, 8); // Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) -// are not counted by the constructor in the user supplied "bucket_count" +// are also counted by the constructor in the user supplied "bucket_count" // argument. // The above example has an exponential ratio of 2 (doubling the bucket width // in each consecutive bucket. The Histogram class automatically calculates @@ -28,8 +45,9 @@ // at the low end of the histogram scale, but allows the histogram to cover a // gigantic range with the addition of very few buckets. -// Histograms use a pattern involving a function static variable, that is a -// pointer to a histogram. This static is explicitly initialized on any thread +// Usually we use macros to define and use a histogram. These macros use a +// pattern involving a function static variable, that is a pointer to a +// histogram. This static is explicitly initialized on any thread // that detects a uninitialized (NULL) pointer. The potentially racy // initialization is not a problem as it is always set to point to the same // value (i.e., the FactoryGet always returns the same value). FactoryGet @@ -177,9 +195,10 @@ class Lock; boundary_value + 1, base::Histogram::kNoFlags)) // Support histograming of an enumerated value. Samples should be one of the -// std::vector<int> list provided via |custom_ranges|. You can use the helper -// function |base::CustomHistogram::ArrayToCustomRanges(samples, num_samples)| -// to transform a C-style array of valid sample values to a std::vector<int>. +// std::vector<int> list provided via |custom_ranges|. See comments above +// CustomRanges::FactoryGet about the requirement of |custom_ranges|. +// You can use the helper function CustomHistogram::ArrayToCustomRanges to +// transform a C-style array of valid sample values to a std::vector<int>. #define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ base::CustomHistogram::FactoryGet(name, custom_ranges, \ @@ -338,13 +357,13 @@ class BASE_EXPORT Histogram : public HistogramBase { LINEAR_HISTOGRAM, BOOLEAN_HISTOGRAM, CUSTOM_HISTOGRAM, - NOT_VALID_IN_RENDERER + NOT_VALID_IN_RENDERER, }; enum BucketLayout { EXPONENTIAL, LINEAR, - CUSTOM + CUSTOM, }; enum Flags { @@ -381,17 +400,17 @@ class BASE_EXPORT Histogram : public HistogramBase { class BASE_EXPORT SampleSet { public: - explicit SampleSet(); + explicit SampleSet(size_t size); + SampleSet(); ~SampleSet(); - // Adjust size of counts_ for use with given histogram. - void Resize(const Histogram& histogram); - void CheckSize(const Histogram& histogram) const; + 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_; } @@ -447,10 +466,16 @@ class BASE_EXPORT Histogram : public HistogramBase { base::TimeDelta maximum, size_t bucket_count, Flags flags); + // Time call for use with DHISTOGRAM*. // Returns TimeTicks::Now() in debug and TimeTicks() in release build. static TimeTicks DebugNow(); + static void InitializeBucketRanges(Sample minimum, + Sample maximum, + size_t bucket_count, + BucketRanges* ranges); + virtual void Add(Sample value) OVERRIDE; // This method is an interface, used only by BooleanHistogram. @@ -507,43 +532,44 @@ class BASE_EXPORT Histogram : public HistogramBase { Sample declared_min() const { return declared_min_; } Sample declared_max() const { return declared_max_; } virtual Sample ranges(size_t i) const; - uint32 range_checksum() const { return range_checksum_; } virtual size_t bucket_count() const; - BucketRanges* bucket_ranges() const { return bucket_ranges_; } - void set_bucket_ranges(BucketRanges* bucket_ranges) { - bucket_ranges_ = bucket_ranges; - } + const BucketRanges* bucket_ranges() const { return bucket_ranges_; } + // Snapshot the current complete set of sample data. // Override with atomic/locked snapshot if needed. virtual void SnapshotSample(SampleSet* sample) const; - virtual bool HasConstructorArguments(Sample minimum, Sample maximum, - size_t bucket_count); - - virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum, - TimeDelta maximum, - size_t bucket_count); - // Return true iff the range_checksum_ matches current |ranges_| vector in - // |bucket_ranges_|. - bool HasValidRangeChecksum() const; - + virtual bool HasConstructionArguments(Sample minimum, + Sample maximum, + size_t bucket_count); protected: - Histogram(const std::string& name, Sample minimum, - Sample maximum, size_t bucket_count); - Histogram(const std::string& name, TimeDelta minimum, - TimeDelta maximum, size_t bucket_count); + // |bucket_count| and |ranges| should contain the underflow and overflow + // buckets. See top comments for example. + Histogram(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + const BucketRanges* ranges); virtual ~Histogram(); + // This function validates histogram construction arguments. It returns false + // if some of the arguments are totally bad. + // Note. Currently it allow some bad input, e.g. 0 as minimum, but silently + // converts it to good input: 1. + // TODO(kaiwang): Be more restrict and return false for any bad input, and + // make this a readonly validating function. + static bool InspectConstructionArguments(const std::string& name, + Sample* minimum, + Sample* maximum, + size_t* bucket_count); + // Serialize the histogram's ranges to |*pickle|, returning true on success. // Most subclasses can leave this no-op implementation, but some will want to // override it, especially if the ranges cannot be re-derived from other // serialized parameters. virtual bool SerializeRanges(Pickle* pickle) const; - // Initialize ranges_ mapping in bucket_ranges_. - void InitializeBucketRange(); - // Method to override to skip the display of the i'th bucket if it's empty. virtual bool PrintEmptyBucket(size_t index) const; @@ -555,9 +581,6 @@ class BASE_EXPORT Histogram : public HistogramBase { // Get normalized size, relative to the ranges(i). virtual double GetBucketSize(Count current, size_t i) const; - // Recalculate range_checksum_. - void ResetRangeChecksum(); - // Return a string description of what goes in a given bucket. // Most commonly this is the numeric value, but in derived classes it may // be a name (or string description) given to the bucket. @@ -569,18 +592,6 @@ class BASE_EXPORT Histogram : public HistogramBase { // Update all our internal data, including histogram virtual void Accumulate(Sample value, Count count, size_t index); - //---------------------------------------------------------------------------- - // Accessors for derived classes. - //---------------------------------------------------------------------------- - void SetBucketRange(size_t i, Sample value); - - // Validate that ranges_ in bucket_ranges_ was created sensibly (top and - // bottom range values relate properly to the declared_min_ and - // declared_max_). - bool ValidateBucketRanges() const; - - virtual uint32 CalculateRangeChecksum() const; - private: // Allow tests to corrupt our innards for testing purposes. FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptBucketBounds); @@ -589,12 +600,7 @@ class BASE_EXPORT Histogram : public HistogramBase { FRIEND_TEST_ALL_PREFIXES(HistogramTest, Crc32TableTest); friend class StatisticsRecorder; // To allow it to delete duplicates. - - // Post constructor initialization. - void Initialize(); - - // Checksum function for accumulating range values into a checksum. - static uint32 Crc32(uint32 sum, Sample range); + friend class StatisticsRecorderTest; //---------------------------------------------------------------------------- // Helpers for emitting Ascii graphic. Each method appends data to output. @@ -625,11 +631,8 @@ class BASE_EXPORT Histogram : public HistogramBase { void WriteAsciiBucketGraph(double current_size, double max_size, std::string* output) const; - //---------------------------------------------------------------------------- - // Table for generating Crc32 values. - static const uint32 kCrcTable[256]; - //---------------------------------------------------------------------------- - // Invariant values set at/near construction time + // Does not own this object. Should get from StatisticsRecorder. + const BucketRanges* bucket_ranges_; Sample declared_min_; // Less than this goes into counts_[0] Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1]. @@ -638,17 +641,6 @@ class BASE_EXPORT Histogram : public HistogramBase { // Flag the histogram for recording by UMA via metric_services.h. Flags flags_; - // For each index, show the least value that can be stored in the - // corresponding bucket. We also append one extra element in this array, - // containing kSampleType_MAX, to make calculations easy. - // The dimension of ranges_ in bucket_ranges_ is bucket_count + 1. - BucketRanges* bucket_ranges_; - - // For redundancy, we store a checksum of all the sample ranges when ranges - // are generated. If ever there is ever a difference, then the histogram must - // have been corrupted. - uint32 range_checksum_; - // Finally, provide the state that changes with the addition of each new // sample. SampleSet sample_; @@ -677,6 +669,11 @@ class BASE_EXPORT LinearHistogram : public Histogram { size_t bucket_count, Flags flags); + static void InitializeBucketRanges(Sample minimum, + Sample maximum, + size_t bucket_count, + BucketRanges* ranges); + // Overridden from Histogram: virtual ClassType histogram_type() const OVERRIDE; @@ -686,14 +683,12 @@ class BASE_EXPORT LinearHistogram : public Histogram { const DescriptionPair descriptions[]) OVERRIDE; protected: - LinearHistogram(const std::string& name, Sample minimum, - Sample maximum, size_t bucket_count); + LinearHistogram(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + const BucketRanges* ranges); - LinearHistogram(const std::string& name, TimeDelta minimum, - TimeDelta maximum, size_t bucket_count); - - // Initialize ranges_ mapping in bucket_ranges_. - void InitializeBucketRange(); virtual double GetBucketSize(Count current, size_t i) const OVERRIDE; // If we have a description for a bucket, then return that. Otherwise @@ -726,7 +721,7 @@ class BASE_EXPORT BooleanHistogram : public LinearHistogram { virtual void AddBoolean(bool value) OVERRIDE; private: - explicit BooleanHistogram(const std::string& name); + BooleanHistogram(const std::string& name, const BucketRanges* ranges); DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); }; @@ -736,7 +731,10 @@ class BASE_EXPORT BooleanHistogram : public LinearHistogram { // CustomHistogram is a histogram for a set of custom integers. class BASE_EXPORT CustomHistogram : public Histogram { public: - + // |custom_ranges| contains a vector of limits on ranges. Each limit should be + // > 0 and < kSampleType_MAX. (Currently 0 is still accepted for backward + // compatibility). The limits can be unordered or contain duplication, but + // client should not depend on this. static Histogram* FactoryGet(const std::string& name, const std::vector<Sample>& custom_ranges, Flags flags); @@ -749,25 +747,27 @@ class BASE_EXPORT CustomHistogram : public Histogram { // This function ensures that a guard bucket exists right after any // valid sample value (unless the next higher sample is also a valid value), // so that invalid samples never fall into the same bucket as valid samples. + // TODO(kaiwang): Change name to ArrayToCustomEnumRanges. static std::vector<Sample> ArrayToCustomRanges(const Sample* values, size_t num_values); // Helper for deserializing CustomHistograms. |*ranges| should already be // correctly sized before this call. Return true on success. static bool DeserializeRanges(PickleIterator* iter, - std::vector<Histogram::Sample>* ranges); - - + std::vector<Sample>* ranges); protected: CustomHistogram(const std::string& name, - const std::vector<Sample>& custom_ranges); + const BucketRanges* ranges); virtual bool SerializeRanges(Pickle* pickle) const OVERRIDE; - // Initialize ranges_ mapping in bucket_ranges_. - void InitializedCustomBucketRange(const std::vector<Sample>& custom_ranges); virtual double GetBucketSize(Count current, size_t i) const OVERRIDE; + private: + static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges); + static BucketRanges* CreateBucketRangesFromCustomRanges( + const std::vector<Sample>& custom_ranges); + DISALLOW_COPY_AND_ASSIGN(CustomHistogram); }; diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc index 3385f27..42d793e 100644 --- a/base/metrics/histogram_base.cc +++ b/base/metrics/histogram_base.cc @@ -6,6 +6,8 @@ namespace base { +const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; + HistogramBase::HistogramBase(const std::string& name) : histogram_name_(name) {} diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h index 85081a6..d8c2910 100644 --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h @@ -17,7 +17,7 @@ class BASE_EXPORT HistogramBase { typedef int Sample; // Used for samples. typedef int Count; // Used to count samples. - static const Sample kSampleType_MAX = INT_MAX; + static const Sample kSampleType_MAX; // INT_MAX HistogramBase(const std::string& name); virtual ~HistogramBase(); diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc index 9d9398d..2812c16 100644 --- a/base/metrics/histogram_snapshot_manager.cc +++ b/base/metrics/histogram_snapshot_manager.cc @@ -76,7 +76,8 @@ void HistogramSnapshotManager::PrepareDelta(const Histogram& histogram) { if (logged_samples_.end() == it) { // Add new entry already_logged = &logged_samples_[histogram.histogram_name()]; - already_logged->Resize(histogram); // Complete initialization. + // Complete initialization. + already_logged->Resize(histogram.bucket_count()); } else { already_logged = &(it->second); int64 discrepancy(already_logged->TotalCount() - diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc index be9a412..7257fa5 100644 --- a/base/metrics/histogram_unittest.cc +++ b/base/metrics/histogram_unittest.cc @@ -15,43 +15,27 @@ #include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" -namespace base { +using std::vector; -class HistogramTest : public testing::Test { -}; +namespace base { // Check for basic syntax and use. -TEST(HistogramTest, StartupShutdownTest) { +TEST(HistogramTest, BasicTest) { // Try basic construction Histogram* histogram(Histogram::FactoryGet( "TestHistogram", 1, 1000, 10, Histogram::kNoFlags)); EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram); - Histogram* histogram1(Histogram::FactoryGet( - "Test1Histogram", 1, 1000, 10, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram1); - EXPECT_NE(histogram, histogram1); - Histogram* linear_histogram(LinearHistogram::FactoryGet( "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags)); EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram); - Histogram* linear_histogram1(LinearHistogram::FactoryGet( - "Test1LinearHistogram", 1, 1000, 10, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram1); - EXPECT_NE(linear_histogram, linear_histogram1); - std::vector<int> custom_ranges; + vector<int> custom_ranges; custom_ranges.push_back(1); custom_ranges.push_back(5); - custom_ranges.push_back(10); - custom_ranges.push_back(20); - custom_ranges.push_back(30); Histogram* custom_histogram(CustomHistogram::FactoryGet( "TestCustomHistogram", custom_ranges, Histogram::kNoFlags)); EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram); - Histogram* custom_histogram1(CustomHistogram::FactoryGet( - "Test1CustomHistogram", custom_ranges, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram1); // Use standard macros (but with fixed samples) HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); @@ -61,215 +45,152 @@ TEST(HistogramTest, StartupShutdownTest) { DHISTOGRAM_COUNTS("Test5Histogram", 30); HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130); - - // Try to construct samples. - Histogram::SampleSet sample1; - Histogram::SampleSet sample2; - - // Use copy constructor of SampleSet - sample1 = sample2; - Histogram::SampleSet sample3(sample1); - - // Finally test a statistics recorder, without really using it. - StatisticsRecorder recorder; } -// Repeat with a recorder present to register with. -TEST(HistogramTest, RecordedStartupTest) { - // Test a statistics recorder, by letting histograms register. - StatisticsRecorder recorder; // This initializes the global state. - - StatisticsRecorder::Histograms histograms; - EXPECT_EQ(0U, histograms.size()); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(0U, histograms.size()); - - // Try basic construction - Histogram* histogram(Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(1U, histograms.size()); - Histogram* histogram1(Histogram::FactoryGet( - "Test1Histogram", 1, 1000, 10, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram1); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(2U, histograms.size()); - - Histogram* linear_histogram(LinearHistogram::FactoryGet( - "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(3U, histograms.size()); - - Histogram* linear_histogram1(LinearHistogram::FactoryGet( - "Test1LinearHistogram", 1, 1000, 10, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram1); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(4U, histograms.size()); - - std::vector<int> custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(5); - custom_ranges.push_back(10); - custom_ranges.push_back(20); - custom_ranges.push_back(30); - Histogram* custom_histogram(CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram); - Histogram* custom_histogram1(CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, Histogram::kNoFlags)); - EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram1); - - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(5U, histograms.size()); - - // Use standard macros (but with fixed samples) - HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); - HISTOGRAM_COUNTS("Test3Histogram", 30); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(7U, histograms.size()); - - HISTOGRAM_ENUMERATION("TestEnumerationHistogram", 20, 200); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists - EXPECT_EQ(8U, histograms.size()); - - DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1)); - DHISTOGRAM_COUNTS("Test5Histogram", 30); - histograms.clear(); - StatisticsRecorder::GetHistograms(&histograms); // Load up lists -#ifndef NDEBUG - EXPECT_EQ(10U, histograms.size()); -#else - EXPECT_EQ(8U, histograms.size()); -#endif -} - -TEST(HistogramTest, RangeTest) { - StatisticsRecorder recorder; - StatisticsRecorder::Histograms histograms; - - recorder.GetHistograms(&histograms); - EXPECT_EQ(0U, histograms.size()); - - Histogram* histogram(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file. +TEST(HistogramTest, ExponentialRangesTest) { // Check that we got a nice exponential when there was enough rooom. - EXPECT_EQ(0, histogram->ranges(0)); + BucketRanges ranges(9); + Histogram::InitializeBucketRanges(1, 64, 8, &ranges); + EXPECT_EQ(0, ranges.range(0)); int power_of_2 = 1; for (int i = 1; i < 8; i++) { - EXPECT_EQ(power_of_2, histogram->ranges(i)); + EXPECT_EQ(power_of_2, ranges.range(i)); power_of_2 *= 2; } - EXPECT_EQ(INT_MAX, histogram->ranges(8)); - - Histogram* short_histogram(Histogram::FactoryGet( - "Histogram Shortened", 1, 7, 8, Histogram::kNoFlags)); - // Check that when the number of buckets is short, we get a linear histogram - // for lack of space to do otherwise. - for (int i = 0; i < 8; i++) - EXPECT_EQ(i, short_histogram->ranges(i)); - EXPECT_EQ(INT_MAX, short_histogram->ranges(8)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); - Histogram* linear_histogram(LinearHistogram::FactoryGet( - "Linear", 1, 7, 8, Histogram::kNoFlags)); - // We also get a nice linear set of bucket ranges when we ask for it - for (int i = 0; i < 8; i++) - EXPECT_EQ(i, linear_histogram->ranges(i)); - EXPECT_EQ(INT_MAX, linear_histogram->ranges(8)); + // Check the corresponding Histogram will use the correct ranges. + Histogram* histogram(Histogram::FactoryGet( + "Histogram", 1, 64, 8, Histogram::kNoFlags)); + EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); + + // When bucket count is limited, exponential ranges will partially look like + // linear. + BucketRanges ranges2(16); + Histogram::InitializeBucketRanges(1, 32, 15, &ranges2); + + EXPECT_EQ(0, ranges2.range(0)); + EXPECT_EQ(1, ranges2.range(1)); + EXPECT_EQ(2, ranges2.range(2)); + EXPECT_EQ(3, ranges2.range(3)); + EXPECT_EQ(4, ranges2.range(4)); + EXPECT_EQ(5, ranges2.range(5)); + EXPECT_EQ(6, ranges2.range(6)); + EXPECT_EQ(7, ranges2.range(7)); + EXPECT_EQ(9, ranges2.range(8)); + EXPECT_EQ(11, ranges2.range(9)); + EXPECT_EQ(14, ranges2.range(10)); + EXPECT_EQ(17, ranges2.range(11)); + EXPECT_EQ(21, ranges2.range(12)); + EXPECT_EQ(26, ranges2.range(13)); + EXPECT_EQ(32, ranges2.range(14)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(15)); + + // Check the corresponding Histogram will use the correct ranges. + Histogram* histogram2(Histogram::FactoryGet( + "Histogram2", 1, 32, 15, Histogram::kNoFlags)); + EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); +} - Histogram* linear_broad_histogram(LinearHistogram::FactoryGet( - "Linear widened", 2, 14, 8, Histogram::kNoFlags)); - // ...but when the list has more space, then the ranges naturally spread out. +TEST(HistogramTest, LinearRangesTest) { + BucketRanges ranges(9); + LinearHistogram::InitializeBucketRanges(1, 7, 8, &ranges); + // Gets a nice linear set of bucket ranges. for (int i = 0; i < 8; i++) - EXPECT_EQ(2 * i, linear_broad_histogram->ranges(i)); - EXPECT_EQ(INT_MAX, linear_broad_histogram->ranges(8)); - - Histogram* transitioning_histogram(Histogram::FactoryGet( - "LinearAndExponential", 1, 32, 15, Histogram::kNoFlags)); - // When space is a little tight, we transition from linear to exponential. - EXPECT_EQ(0, transitioning_histogram->ranges(0)); - EXPECT_EQ(1, transitioning_histogram->ranges(1)); - EXPECT_EQ(2, transitioning_histogram->ranges(2)); - EXPECT_EQ(3, transitioning_histogram->ranges(3)); - EXPECT_EQ(4, transitioning_histogram->ranges(4)); - EXPECT_EQ(5, transitioning_histogram->ranges(5)); - EXPECT_EQ(6, transitioning_histogram->ranges(6)); - EXPECT_EQ(7, transitioning_histogram->ranges(7)); - EXPECT_EQ(9, transitioning_histogram->ranges(8)); - EXPECT_EQ(11, transitioning_histogram->ranges(9)); - EXPECT_EQ(14, transitioning_histogram->ranges(10)); - EXPECT_EQ(17, transitioning_histogram->ranges(11)); - EXPECT_EQ(21, transitioning_histogram->ranges(12)); - EXPECT_EQ(26, transitioning_histogram->ranges(13)); - EXPECT_EQ(32, transitioning_histogram->ranges(14)); - EXPECT_EQ(INT_MAX, transitioning_histogram->ranges(15)); - - std::vector<int> custom_ranges; - custom_ranges.push_back(0); - custom_ranges.push_back(9); - custom_ranges.push_back(10); - custom_ranges.push_back(11); - custom_ranges.push_back(300); - Histogram* test_custom_histogram(CustomHistogram::FactoryGet( - "TestCustomRangeHistogram", custom_ranges, Histogram::kNoFlags)); - - EXPECT_EQ(custom_ranges[0], test_custom_histogram->ranges(0)); - EXPECT_EQ(custom_ranges[1], test_custom_histogram->ranges(1)); - EXPECT_EQ(custom_ranges[2], test_custom_histogram->ranges(2)); - EXPECT_EQ(custom_ranges[3], test_custom_histogram->ranges(3)); - EXPECT_EQ(custom_ranges[4], test_custom_histogram->ranges(4)); - - recorder.GetHistograms(&histograms); - EXPECT_EQ(6U, histograms.size()); + EXPECT_EQ(i, ranges.range(i)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); + // The correspoding LinearHistogram should use the correct ranges. + Histogram* histogram( + LinearHistogram::FactoryGet("Linear", 1, 7, 8, Histogram::kNoFlags)); + EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); + + // Linear ranges are not divisible. + BucketRanges ranges2(6); + LinearHistogram::InitializeBucketRanges(1, 6, 5, &ranges2); + EXPECT_EQ(0, ranges2.range(0)); + EXPECT_EQ(1, ranges2.range(1)); + EXPECT_EQ(3, ranges2.range(2)); + EXPECT_EQ(4, ranges2.range(3)); + EXPECT_EQ(6, ranges2.range(4)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5)); + // The correspoding LinearHistogram should use the correct ranges. + Histogram* histogram2( + LinearHistogram::FactoryGet("Linear2", 1, 6, 5, Histogram::kNoFlags)); + EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); } -TEST(HistogramTest, CustomRangeTest) { - StatisticsRecorder recorder; - StatisticsRecorder::Histograms histograms; - - // Check that missing leading zero is handled by an auto-insertion. - std::vector<int> custom_ranges; - // Don't include a zero. - custom_ranges.push_back(9); - custom_ranges.push_back(10); - custom_ranges.push_back(11); - Histogram* test_custom_histogram(CustomHistogram::FactoryGet( - "TestCustomRangeHistogram", custom_ranges, Histogram::kNoFlags)); - - EXPECT_EQ(0, test_custom_histogram->ranges(0)); // Auto added - EXPECT_EQ(custom_ranges[0], test_custom_histogram->ranges(1)); - EXPECT_EQ(custom_ranges[1], test_custom_histogram->ranges(2)); - EXPECT_EQ(custom_ranges[2], test_custom_histogram->ranges(3)); +TEST(HistogramTest, ArrayToCustomRangesTest) { + const HistogramBase::Sample ranges[3] = {5, 10 ,20}; + vector<HistogramBase::Sample> ranges_vec = + CustomHistogram::ArrayToCustomRanges(ranges, 3); + ASSERT_EQ(6u, ranges_vec.size()); + EXPECT_EQ(5, ranges_vec[0]); + EXPECT_EQ(6, ranges_vec[1]); + EXPECT_EQ(10, ranges_vec[2]); + EXPECT_EQ(11, ranges_vec[3]); + EXPECT_EQ(20, ranges_vec[4]); + EXPECT_EQ(21, ranges_vec[5]); +} - // Check that unsorted data with dups is handled gracefully. - const int kSmall = 7; - const int kMid = 8; - const int kBig = 9; +TEST(HistogramTest, CustomHistogramTest) { + // A well prepared custom ranges. + vector<HistogramBase::Sample> custom_ranges; + custom_ranges.push_back(1); + custom_ranges.push_back(2); + Histogram* histogram = CustomHistogram::FactoryGet( + "TestCustomHistogram1", custom_ranges, Histogram::kNoFlags); + const BucketRanges* ranges = histogram->bucket_ranges(); + ASSERT_EQ(4u, ranges->size()); + EXPECT_EQ(0, ranges->range(0)); // Auto added. + EXPECT_EQ(1, ranges->range(1)); + EXPECT_EQ(2, ranges->range(2)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); // Auto added. + + // A unordered custom ranges. + custom_ranges.clear(); + custom_ranges.push_back(2); + custom_ranges.push_back(1); + histogram = CustomHistogram::FactoryGet( + "TestCustomHistogram2", custom_ranges, Histogram::kNoFlags); + ranges = histogram->bucket_ranges(); + ASSERT_EQ(4u, ranges->size()); + EXPECT_EQ(0, ranges->range(0)); + EXPECT_EQ(1, ranges->range(1)); + EXPECT_EQ(2, ranges->range(2)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); + + // A custom ranges with duplicated values. custom_ranges.clear(); - custom_ranges.push_back(kBig); - custom_ranges.push_back(kMid); - custom_ranges.push_back(kSmall); - custom_ranges.push_back(kSmall); - custom_ranges.push_back(kMid); - custom_ranges.push_back(0); // Push an explicit zero. - custom_ranges.push_back(kBig); - - Histogram* unsorted_histogram(CustomHistogram::FactoryGet( - "TestCustomUnsortedDupedHistogram", custom_ranges, Histogram::kNoFlags)); - EXPECT_EQ(0, unsorted_histogram->ranges(0)); - EXPECT_EQ(kSmall, unsorted_histogram->ranges(1)); - EXPECT_EQ(kMid, unsorted_histogram->ranges(2)); - EXPECT_EQ(kBig, unsorted_histogram->ranges(3)); + custom_ranges.push_back(4); + custom_ranges.push_back(1); + custom_ranges.push_back(4); + histogram = CustomHistogram::FactoryGet( + "TestCustomHistogram3", custom_ranges, Histogram::kNoFlags); + ranges = histogram->bucket_ranges(); + ASSERT_EQ(4u, ranges->size()); + EXPECT_EQ(0, ranges->range(0)); + EXPECT_EQ(1, ranges->range(1)); + EXPECT_EQ(4, ranges->range(2)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); } +TEST(HistogramTest, CustomHistogramWithOnly2Buckets) { + // This test exploits the fact that the CustomHistogram can have 2 buckets, + // while the base class Histogram is *supposed* to have at least 3 buckets. + // We should probably change the restriction on the base class (or not inherit + // the base class!). + + vector<HistogramBase::Sample> custom_ranges; + custom_ranges.push_back(4); + + Histogram* histogram = CustomHistogram::FactoryGet( + "2BucketsCustomHistogram", custom_ranges, Histogram::kNoFlags); + const BucketRanges* ranges = histogram->bucket_ranges(); + ASSERT_EQ(3u, ranges->size()); + EXPECT_EQ(0, ranges->range(0)); + EXPECT_EQ(4, ranges->range(1)); + EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2)); +} // Make sure histogram handles out-of-bounds data gracefully. TEST(HistogramTest, BoundsTest) { @@ -294,7 +215,7 @@ TEST(HistogramTest, BoundsTest) { EXPECT_EQ(0, sample.counts(array_size - 2)); EXPECT_EQ(2, sample.counts(array_size - 1)); - std::vector<int> custom_ranges; + vector<int> custom_ranges; custom_ranges.push_back(10); custom_ranges.push_back(50); custom_ranges.push_back(100); @@ -320,31 +241,20 @@ TEST(HistogramTest, BoundsTest) { // Check to be sure samples land as expected is "correct" buckets. TEST(HistogramTest, BucketPlacementTest) { Histogram* histogram(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file. - - // Check that we got a nice exponential since there was enough rooom. - EXPECT_EQ(0, histogram->ranges(0)); - int power_of_2 = 1; - for (int i = 1; i < 8; i++) { - EXPECT_EQ(power_of_2, histogram->ranges(i)); - power_of_2 *= 2; - } - EXPECT_EQ(INT_MAX, histogram->ranges(8)); + "Histogram", 1, 64, 8, Histogram::kNoFlags)); // Add i+1 samples to the i'th bucket. histogram->Add(0); - power_of_2 = 1; + int power_of_2 = 1; for (int i = 1; i < 8; i++) { for (int j = 0; j <= i; j++) histogram->Add(power_of_2); power_of_2 *= 2; } - // Leave overflow bucket empty. // Check to see that the bucket counts reflect our additions. Histogram::SampleSet sample; histogram->SnapshotSample(&sample); - EXPECT_EQ(INT_MAX, histogram->ranges(8)); for (int i = 0; i < 8; i++) EXPECT_EQ(i + 1, sample.counts(i)); } @@ -383,7 +293,8 @@ TEST(HistogramTest, CorruptBucketBounds) { EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0); EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption. - BucketRanges* bucket_ranges = histogram->bucket_ranges(); + BucketRanges* bucket_ranges = + const_cast<BucketRanges*>(histogram->bucket_ranges()); HistogramBase::Sample tmp = bucket_ranges->range(1); bucket_ranges->set_range(1, bucket_ranges->range(2)); bucket_ranges->set_range(2, tmp); @@ -408,45 +319,4 @@ TEST(HistogramTest, CorruptBucketBounds) { bucket_ranges->set_range(4, bucket_ranges->range(4) + 1); } -// Table was generated similarly to sample code for CRC-32 given on: -// http://www.w3.org/TR/PNG/#D-CRCAppendix. -TEST(HistogramTest, Crc32TableTest) { - for (int i = 0; i < 256; ++i) { - uint32 checksum = i; - for (int j = 0; j < 8; ++j) { - const uint32 kReversedPolynomial = 0xedb88320L; - if (checksum & 1) - checksum = kReversedPolynomial ^ (checksum >> 1); - else - checksum >>= 1; - } - EXPECT_EQ(Histogram::kCrcTable[i], checksum); - } -} - -// RangeTest, CustomRangeTest and CorruptBucketBounds test BucketRanges class. -// The following tests sharing of BucketRanges object. -TEST(HistogramTest, BucketRangesTest) { - StatisticsRecorder recorder; - StatisticsRecorder::Histograms histograms; - - recorder.GetHistograms(&histograms); - EXPECT_EQ(0U, histograms.size()); - - Histogram* histogram1(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); - - Histogram* histogram2(Histogram::FactoryGet( - "Histogram2", 1, 64, 8, Histogram::kNoFlags)); - - Histogram* histogram3(Histogram::FactoryGet( - "Histogram3", 1, 64, 16, Histogram::kNoFlags)); - - BucketRanges* bucket_ranges1 = histogram1->bucket_ranges(); - BucketRanges* bucket_ranges2 = histogram2->bucket_ranges(); - BucketRanges* bucket_ranges3 = histogram3->bucket_ranges(); - EXPECT_TRUE(bucket_ranges1->Equals(bucket_ranges2)); - EXPECT_FALSE(bucket_ranges1->Equals(bucket_ranges3)); -} - } // namespace base diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc index 874cdb8..20d0349 100644 --- a/base/metrics/statistics_recorder.cc +++ b/base/metrics/statistics_recorder.cc @@ -6,14 +6,18 @@ #include "base/debug/leak_annotations.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" #include "base/stringprintf.h" #include "base/synchronization/lock.h" +using std::list; +using std::string; + namespace { // Initialize histogram statistics gathering system. -base::LazyInstance<base::StatisticsRecorder>::Leaky - g_statistics_recorder_ = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = + LAZY_INSTANCE_INITIALIZER; } // namespace namespace base { @@ -25,53 +29,6 @@ static uint32 number_of_vectors_saved_ = 0; // Collect the number of ranges_ elements saved because of caching ranges. static size_t saved_ranges_size_ = 0; -// This singleton instance should be started during the single threaded portion -// of main(), and hence it is not thread safe. It initializes globals to -// provide support for all future calls. -StatisticsRecorder::StatisticsRecorder() { - DCHECK(!histograms_); - if (lock_ == NULL) { - // This will leak on purpose. It's the only way to make sure we won't race - // against the static uninitialization of the module while one of our - // static methods relying on the lock get called at an inappropriate time - // during the termination phase. Since it's a static data member, we will - // leak one per process, which would be similar to the instance allocated - // during static initialization and released only on process termination. - lock_ = new base::Lock; - } - base::AutoLock auto_lock(*lock_); - histograms_ = new HistogramMap; - ranges_ = new RangesMap; -} - -StatisticsRecorder::~StatisticsRecorder() { - DCHECK(histograms_ && lock_); - - if (dump_on_exit_) { - std::string output; - WriteGraph("", &output); - DLOG(INFO) << output; - } - // Clean up. - HistogramMap* histograms = NULL; - { - base::AutoLock auto_lock(*lock_); - histograms = histograms_; - histograms_ = NULL; - } - RangesMap* ranges = NULL; - { - base::AutoLock auto_lock(*lock_); - ranges = ranges_; - ranges_ = NULL; - } - // We are going to leak the histograms and the ranges. - delete histograms; - delete ranges; - // We don't delete lock_ on purpose to avoid having to properly protect - // against it going away after we checked for NULL in the static methods. -} - // static void StatisticsRecorder::Initialize() { // Ensure that an instance of the StatisticsRecorder object is created. @@ -87,77 +44,96 @@ bool StatisticsRecorder::IsActive() { return NULL != histograms_; } +// static Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) { // As per crbug.com/79322 the histograms are intentionally leaked, so we need // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once // for an object, the duplicates should not be annotated. // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) // twice if (lock_ == NULL) || (!histograms_). - DCHECK(histogram->HasValidRangeChecksum()); if (lock_ == NULL) { ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 return histogram; } - base::AutoLock auto_lock(*lock_); - if (!histograms_) { - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - return histogram; - } - const std::string name = histogram->histogram_name(); - HistogramMap::iterator it = histograms_->find(name); - // Avoid overwriting a previous registration. - if (histograms_->end() == it) { - (*histograms_)[name] = histogram; - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - RegisterOrDeleteDuplicateRanges(histogram); - ++number_of_histograms_; - } else { - delete histogram; // We already have one by this name. - histogram = it->second; + + Histogram* histogram_to_delete = NULL; + Histogram* histogram_to_return = NULL; + { + base::AutoLock auto_lock(*lock_); + if (histograms_ == NULL) { + ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 + histogram_to_return = histogram; + } else { + const string& name = histogram->histogram_name(); + HistogramMap::iterator it = histograms_->find(name); + if (histograms_->end() == it) { + (*histograms_)[name] = histogram; + ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 + ++number_of_histograms_; + histogram_to_return = histogram; + } else if (histogram == it->second) { + // The histogram was registered before. + histogram_to_return = histogram; + } else { + // We already have one histogram with this name. + histogram_to_return = it->second; + histogram_to_delete = histogram; + } + } } - return histogram; + delete histogram_to_delete; + return histogram_to_return; } // static -void StatisticsRecorder::RegisterOrDeleteDuplicateRanges(Histogram* histogram) { - DCHECK(histogram); - BucketRanges* histogram_ranges = histogram->bucket_ranges(); - DCHECK(histogram_ranges); - uint32 checksum = histogram->range_checksum(); - histogram_ranges->set_checksum(checksum); - - RangesMap::iterator ranges_it = ranges_->find(checksum); +const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( + const BucketRanges* ranges) { + DCHECK(ranges->HasValidChecksum()); + scoped_ptr<const BucketRanges> ranges_deleter; + + if (lock_ == NULL) { + ANNOTATE_LEAKING_OBJECT_PTR(ranges); + return ranges; + } + + base::AutoLock auto_lock(*lock_); + if (ranges_ == NULL) { + ANNOTATE_LEAKING_OBJECT_PTR(ranges); + return ranges; + } + + list<const BucketRanges*>* checksum_matching_list; + RangesMap::iterator ranges_it = ranges_->find(ranges->checksum()); if (ranges_->end() == ranges_it) { - // Register the new BucketRanges. - std::list<BucketRanges*>* checksum_matching_list( - new std::list<BucketRanges*>()); - checksum_matching_list->push_front(histogram_ranges); - (*ranges_)[checksum] = checksum_matching_list; - return; + // Add a new matching list to map. + checksum_matching_list = new list<const BucketRanges*>(); + ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list); + (*ranges_)[ranges->checksum()] = checksum_matching_list; + } else { + checksum_matching_list = ranges_it->second; } - // Use the registered BucketRanges if the registered BucketRanges has same - // ranges_ as |histogram|'s BucketRanges. - std::list<BucketRanges*>* checksum_matching_list = ranges_it->second; - std::list<BucketRanges*>::iterator checksum_matching_list_it; + list<const BucketRanges*>::iterator checksum_matching_list_it; for (checksum_matching_list_it = checksum_matching_list->begin(); checksum_matching_list_it != checksum_matching_list->end(); ++checksum_matching_list_it) { - BucketRanges* existing_histogram_ranges = *checksum_matching_list_it; - DCHECK(existing_histogram_ranges); - if (existing_histogram_ranges->Equals(histogram_ranges)) { - histogram->set_bucket_ranges(existing_histogram_ranges); - ++number_of_vectors_saved_; - saved_ranges_size_ += histogram_ranges->size(); - delete histogram_ranges; - return; + const BucketRanges* existing_ranges = *checksum_matching_list_it; + if (existing_ranges->Equals(ranges)) { + if (existing_ranges == ranges) { + return ranges; + } else { + ++number_of_vectors_saved_; + saved_ranges_size_ += ranges->size(); + ranges_deleter.reset(ranges); + return existing_ranges; + } } } - // We haven't found a BucketRanges which has the same ranges. Register the // new BucketRanges. - DCHECK(checksum_matching_list_it == checksum_matching_list->end()); - checksum_matching_list->push_front(histogram_ranges); + checksum_matching_list->push_front(ranges); + ANNOTATE_LEAKING_OBJECT_PTR(ranges); + return ranges; } // static @@ -246,8 +222,9 @@ void StatisticsRecorder::GetHistograms(Histograms* output) { if (lock_ == NULL) return; base::AutoLock auto_lock(*lock_); - if (!histograms_) + if (histograms_ == NULL) return; + for (HistogramMap::iterator it = histograms_->begin(); histograms_->end() != it; ++it) { @@ -257,12 +234,35 @@ void StatisticsRecorder::GetHistograms(Histograms* output) { } // static +void StatisticsRecorder::GetBucketRanges( + std::vector<const BucketRanges*>* output) { + if (lock_ == NULL) + return; + base::AutoLock auto_lock(*lock_); + if (ranges_ == NULL) + return; + + for (RangesMap::iterator it = ranges_->begin(); + ranges_->end() != it; + ++it) { + list<const BucketRanges*>* ranges_list = it->second; + list<const BucketRanges*>::iterator ranges_list_it; + for (ranges_list_it = ranges_list->begin(); + ranges_list_it != ranges_list->end(); + ++ranges_list_it) { + output->push_back(*ranges_list_it); + } + } +} + +// static Histogram* StatisticsRecorder::FindHistogram(const std::string& name) { if (lock_ == NULL) return NULL; base::AutoLock auto_lock(*lock_); - if (!histograms_) + if (histograms_ == NULL) return NULL; + HistogramMap::iterator it = histograms_->find(name); if (histograms_->end() == it) return NULL; @@ -275,8 +275,9 @@ void StatisticsRecorder::GetSnapshot(const std::string& query, if (lock_ == NULL) return; base::AutoLock auto_lock(*lock_); - if (!histograms_) + if (histograms_ == NULL) return; + for (HistogramMap::iterator it = histograms_->begin(); histograms_->end() != it; ++it) { @@ -285,6 +286,49 @@ void StatisticsRecorder::GetSnapshot(const std::string& query, } } +// This singleton instance should be started during the single threaded portion +// of main(), and hence it is not thread safe. It initializes globals to +// provide support for all future calls. +StatisticsRecorder::StatisticsRecorder() { + DCHECK(!histograms_); + if (lock_ == NULL) { + // This will leak on purpose. It's the only way to make sure we won't race + // against the static uninitialization of the module while one of our + // static methods relying on the lock get called at an inappropriate time + // during the termination phase. Since it's a static data member, we will + // leak one per process, which would be similar to the instance allocated + // during static initialization and released only on process termination. + lock_ = new base::Lock; + } + base::AutoLock auto_lock(*lock_); + histograms_ = new HistogramMap; + ranges_ = new RangesMap; +} + +StatisticsRecorder::~StatisticsRecorder() { + DCHECK(histograms_ && ranges_ && lock_); + if (dump_on_exit_) { + string output; + WriteGraph("", &output); + DLOG(INFO) << output; + } + + // Clean up. + scoped_ptr<HistogramMap> histograms_deleter; + scoped_ptr<RangesMap> ranges_deleter; + // We don't delete lock_ on purpose to avoid having to properly protect + // against it going away after we checked for NULL in the static methods. + { + base::AutoLock auto_lock(*lock_); + histograms_deleter.reset(histograms_); + ranges_deleter.reset(ranges_); + histograms_ = NULL; + ranges_ = NULL; + } + // We are going to leak the histograms and the ranges. +} + + // static StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; // static diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h index 42ef137..2d14514 100644 --- a/base/metrics/statistics_recorder.h +++ b/base/metrics/statistics_recorder.h @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// StatisticsRecorder handles all histograms in the system. It provides a -// general place for histograms to register, and supports a global API for -// accessing (i.e., dumping, or graphing) the data in all the histograms. +// StatisticsRecorder holds all Histograms and BucketRanges that are used by +// Histograms in the system. It provides a general place for +// Histograms/BucketRanges to register, and supports a global API for accessing +// (i.e., dumping, or graphing) the data. #ifndef BASE_METRICS_STATISTICS_RECORDER_H_ #define BASE_METRICS_STATISTICS_RECORDER_H_ @@ -41,13 +42,12 @@ class BASE_EXPORT StatisticsRecorder { // histogram (either the argument, or the pre-existing registered histogram). static Histogram* RegisterOrDeleteDuplicate(Histogram* histogram); - // Register, or add a new bucket_ranges_ of |histogram|. If an identical - // bucket_ranges_ is already registered, then the bucket_ranges_ of - // |histogram| is deleted and the |histogram|'s bucket_ranges_ is reset to the - // registered bucket_ranges_. The bucket_ranges_ of |histogram| is always the - // registered BucketRanges (either the argument's bucket_ranges_, or the - // pre-existing registered bucket_ranges_). - static void RegisterOrDeleteDuplicateRanges(Histogram* histogram); + // Register, or add a new BucketRanges. If an identically BucketRanges is + // already registered, then the argument |ranges| will deleted. The returned + // value is always the registered BucketRanges (either the argument, or the + // pre-existing one). + static const BucketRanges* RegisterOrDeleteDuplicateRanges( + const BucketRanges* ranges); // Method for collecting stats about histograms created in browser and // renderer processes. |suffix| is appended to histogram names. |suffix| could @@ -63,6 +63,9 @@ class BASE_EXPORT StatisticsRecorder { // Method for extracting histograms which were marked for use by UMA. static void GetHistograms(Histograms* output); + // Method for extracting BucketRanges used by all histograms registered. + static void GetBucketRanges(std::vector<const BucketRanges*>* output); + // Find a histogram by name. It matches the exact name. This method is thread // safe. It returns NULL if a matching histogram is not found. static Histogram* FindHistogram(const std::string& name); @@ -84,26 +87,21 @@ class BASE_EXPORT StatisticsRecorder { // We keep all |bucket_ranges_| in a map, from checksum to a list of // |bucket_ranges_|. Checksum is calculated from the |ranges_| in // |bucket_ranges_|. - typedef std::map<uint32, std::list<BucketRanges*>*> RangesMap; + typedef std::map<uint32, std::list<const BucketRanges*>*> RangesMap; friend struct DefaultLazyInstanceTraits<StatisticsRecorder>; + friend class StatisticsRecorderTest; - // Allow tests to access our innards for testing purposes. - FRIEND_TEST_ALL_PREFIXES(HistogramTest, StartupShutdownTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, RecordedStartupTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, RangeTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, CustomRangeTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketRangesTest); - + // The constructor just initializes static members. Usually client code should + // use Initialize to do this. But in test code, you can friend this class and + // call destructor/constructor to get a clean StatisticsRecorder. StatisticsRecorder(); - ~StatisticsRecorder(); static HistogramMap* histograms_; - static RangesMap* ranges_; - // lock protects access to the above map. + // Lock protects access to above maps. static base::Lock* lock_; // Dump all known histograms to log. diff --git a/base/metrics/statistics_recorder_unittest.cc b/base/metrics/statistics_recorder_unittest.cc new file mode 100644 index 0000000..95759c1 --- /dev/null +++ b/base/metrics/statistics_recorder_unittest.cc @@ -0,0 +1,256 @@ +// 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/histogram.h" +#include "base/metrics/statistics_recorder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +class StatisticsRecorderTest : public testing::Test { + protected: + virtual void SetUp() { + // Each test will have a clean state (no Histogram / BucketRanges + // registered). + InitializeStatisticsRecorder(); + } + + virtual void TearDown() { + UninitializeStatisticsRecorder(); + } + + void InitializeStatisticsRecorder() { + statistics_recorder_ = new StatisticsRecorder(); + } + + void UninitializeStatisticsRecorder() { + delete statistics_recorder_; + statistics_recorder_ = NULL; + } + + void DeleteHistogram(Histogram* histogram) { + delete histogram; + } + + StatisticsRecorder* statistics_recorder_; +}; + +TEST_F(StatisticsRecorderTest, NotInitialized) { + UninitializeStatisticsRecorder(); + + ASSERT_FALSE(StatisticsRecorder::IsActive()); + + StatisticsRecorder::Histograms registered_histograms; + std::vector<const BucketRanges*> registered_ranges; + + // We can still create histograms, but it's not registered. + // TODO(kaiwang): Do not depend on Histogram FactoryGet implementation. + Histogram* histogram( + Histogram::FactoryGet("StatisticsRecorderTest_NotInitialized", + 1, 1000, 10, Histogram::kNoFlags)); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(0u, registered_histograms.size()); + + // RegisterOrDeleteDuplicateRanges is a no-op. + BucketRanges* ranges = new BucketRanges(3);; + ranges->ResetChecksum(); + EXPECT_EQ(ranges, + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges)); + StatisticsRecorder::GetBucketRanges(®istered_ranges); + EXPECT_EQ(0u, registered_ranges.size()); + + DeleteHistogram(histogram); +} + +TEST_F(StatisticsRecorderTest, RegisterBucketRanges) { + std::vector<const BucketRanges*> registered_ranges; + + BucketRanges* ranges1 = new BucketRanges(3);; + ranges1->ResetChecksum(); + BucketRanges* ranges2 = new BucketRanges(4);; + ranges2->ResetChecksum(); + + // Register new ranges. + EXPECT_EQ(ranges1, + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1)); + EXPECT_EQ(ranges2, + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges2)); + StatisticsRecorder::GetBucketRanges(®istered_ranges); + ASSERT_EQ(2u, registered_ranges.size()); + + // Register some ranges again. + EXPECT_EQ(ranges1, + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1)); + registered_ranges.clear(); + StatisticsRecorder::GetBucketRanges(®istered_ranges); + ASSERT_EQ(2u, registered_ranges.size()); + // Make sure the ranges is still the one we know. + ASSERT_EQ(3u, ranges1->size()); + EXPECT_EQ(0, ranges1->range(0)); + EXPECT_EQ(0, ranges1->range(1)); + EXPECT_EQ(0, ranges1->range(2)); + + // Register ranges with same values. + BucketRanges* ranges3 = new BucketRanges(3);; + ranges3->ResetChecksum(); + EXPECT_EQ(ranges1, // returning ranges1 + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges3)); + registered_ranges.clear(); + StatisticsRecorder::GetBucketRanges(®istered_ranges); + ASSERT_EQ(2u, registered_ranges.size()); +} + +TEST_F(StatisticsRecorderTest, RegisterHistogram) { + // Create a Histogram that was not registered. + // TODO(kaiwang): Do not depend on Histogram FactoryGet implementation. + UninitializeStatisticsRecorder(); + Histogram* histogram = Histogram::FactoryGet( + "TestHistogram", 1, 1000, 10, Histogram::kNoFlags); + + // Clean StatisticsRecorder. + InitializeStatisticsRecorder(); + StatisticsRecorder::Histograms registered_histograms; + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(0u, registered_histograms.size()); + + // Register the Histogram. + EXPECT_EQ(histogram, + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram)); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(1u, registered_histograms.size()); + + // Register the same Histogram again. + EXPECT_EQ(histogram, + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram)); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(1u, registered_histograms.size()); +} + +TEST_F(StatisticsRecorderTest, FindHistogram) { + Histogram* histogram1 = Histogram::FactoryGet( + "TestHistogram1", 1, 1000, 10, Histogram::kNoFlags); + Histogram* histogram2 = Histogram::FactoryGet( + "TestHistogram2", 1, 1000, 10, Histogram::kNoFlags); + + EXPECT_EQ(histogram1, StatisticsRecorder::FindHistogram("TestHistogram1")); + EXPECT_EQ(histogram2, StatisticsRecorder::FindHistogram("TestHistogram2")); + EXPECT_TRUE(StatisticsRecorder::FindHistogram("TestHistogram") == NULL); +} + +TEST_F(StatisticsRecorderTest, GetSnapshot) { + Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, Histogram::kNoFlags); + Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, Histogram::kNoFlags); + Histogram::FactoryGet("TestHistogram3", 1, 1000, 10, Histogram::kNoFlags); + + StatisticsRecorder::Histograms snapshot; + StatisticsRecorder::GetSnapshot("Test", &snapshot); + EXPECT_EQ(3u, snapshot.size()); + + snapshot.clear(); + StatisticsRecorder::GetSnapshot("1", &snapshot); + EXPECT_EQ(1u, snapshot.size()); + + snapshot.clear(); + StatisticsRecorder::GetSnapshot("hello", &snapshot); + EXPECT_EQ(0u, snapshot.size()); +} + +TEST_F(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) { + StatisticsRecorder::Histograms registered_histograms; + + StatisticsRecorder::GetHistograms(®istered_histograms); + ASSERT_EQ(0u, registered_histograms.size()); + + // Create a Histogram. + Histogram* histogram = Histogram::FactoryGet( + "TestHistogram", 1, 1000, 10, Histogram::kNoFlags); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(1u, registered_histograms.size()); + + // Get an existing histogram. + Histogram* histogram2 = Histogram::FactoryGet( + "TestHistogram", 1, 1000, 10, Histogram::kNoFlags); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(1u, registered_histograms.size()); + EXPECT_EQ(histogram, histogram2); + + // Create a LinearHistogram. + histogram = LinearHistogram::FactoryGet( + "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(2u, registered_histograms.size()); + + // Create a BooleanHistogram. + histogram = BooleanHistogram::FactoryGet( + "TestBooleanHistogram", Histogram::kNoFlags); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(3u, registered_histograms.size()); + + // Create a CustomHistogram. + std::vector<int> custom_ranges; + custom_ranges.push_back(1); + custom_ranges.push_back(5); + histogram = CustomHistogram::FactoryGet( + "TestCustomHistogram", custom_ranges, Histogram::kNoFlags); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(4u, registered_histograms.size()); +} + +TEST_F(StatisticsRecorderTest, RegisterHistogramWithMacros) { + StatisticsRecorder::Histograms registered_histograms; + + Histogram* histogram = Histogram::FactoryGet( + "TestHistogramCounts", 1, 1000000, 50, Histogram::kNoFlags); + + // The histogram we got from macro is the same as from FactoryGet. + HISTOGRAM_COUNTS("TestHistogramCounts", 30); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + ASSERT_EQ(1u, registered_histograms.size()); + EXPECT_EQ(histogram, registered_histograms[0]); + + HISTOGRAM_TIMES("TestHistogramTimes", TimeDelta::FromDays(1)); + HISTOGRAM_ENUMERATION("TestHistogramEnumeration", 20, 200); + + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); + EXPECT_EQ(3u, registered_histograms.size()); + + // Debugging only macros. + DHISTOGRAM_TIMES("TestHistogramDebugTimes", TimeDelta::FromDays(1)); + DHISTOGRAM_COUNTS("TestHistogramDebugCounts", 30); + registered_histograms.clear(); + StatisticsRecorder::GetHistograms(®istered_histograms); +#ifndef NDEBUG + EXPECT_EQ(5u, registered_histograms.size()); +#else + EXPECT_EQ(3u, registered_histograms.size()); +#endif +} + +TEST_F(StatisticsRecorderTest, BucketRangesSharing) { + Histogram* histogram1(Histogram::FactoryGet( + "Histogram", 1, 64, 8, Histogram::kNoFlags)); + Histogram* histogram2(Histogram::FactoryGet( + "Histogram2", 1, 64, 8, Histogram::kNoFlags)); + Histogram* histogram3(Histogram::FactoryGet( + "Histogram3", 1, 64, 16, Histogram::kNoFlags)); + + const BucketRanges* bucket_ranges1 = histogram1->bucket_ranges(); + const BucketRanges* bucket_ranges2 = histogram2->bucket_ranges(); + const BucketRanges* bucket_ranges3 = histogram3->bucket_ranges(); + EXPECT_EQ(bucket_ranges1, bucket_ranges2); + EXPECT_FALSE(bucket_ranges1->Equals(bucket_ranges3)); +} + +} // namespace base diff --git a/chrome/common/metrics/metrics_log_base.cc b/chrome/common/metrics/metrics_log_base.cc index 8617c92..e16b46d 100644 --- a/chrome/common/metrics/metrics_log_base.cc +++ b/chrome/common/metrics/metrics_log_base.cc @@ -452,7 +452,7 @@ void MetricsLogBase::RecordHistogramDelta( const Histogram::SampleSet& snapshot) { DCHECK(!locked_); DCHECK_NE(0, snapshot.TotalCount()); - snapshot.CheckSize(histogram); + DCHECK_EQ(histogram.bucket_count(), snapshot.size()); // We will ignore the MAX_INT/infinite value in the last element of range[]. diff --git a/content/common/child_histogram_message_filter.cc b/content/common/child_histogram_message_filter.cc index 79d4fdb..85d905c 100644 --- a/content/common/child_histogram_message_filter.cc +++ b/content/common/child_histogram_message_filter.cc @@ -73,7 +73,7 @@ void ChildHistogramMessageFilter::RecordDelta( const base::Histogram& histogram, const base::Histogram::SampleSet& snapshot) { DCHECK_NE(0, snapshot.TotalCount()); - snapshot.CheckSize(histogram); + DCHECK_EQ(histogram.bucket_count(), snapshot.size()); std::string histogram_info = base::Histogram::SerializeHistogramInfo(histogram, snapshot); diff --git a/net/disk_cache/stats_histogram.cc b/net/disk_cache/stats_histogram.cc index 66c8e50..d3bab34 100644 --- a/net/disk_cache/stats_histogram.cc +++ b/net/disk_cache/stats_histogram.cc @@ -33,20 +33,16 @@ StatsHistogram* StatsHistogram::FactoryGet(const std::string& name) { // To avoid racy destruction at shutdown, the following will be leaked. StatsHistogram* stats_histogram = new StatsHistogram(name, minimum, maximum, bucket_count); - stats_histogram->InitializeBucketRange(); stats_histogram->SetFlags(kUmaTargetedHistogramFlag); histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(stats_histogram); } DCHECK(HISTOGRAM == histogram->histogram_type()); - DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + DCHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count)); // We're preparing for an otherwise unsafe upcast by ensuring we have the // proper class type. StatsHistogram* return_histogram = static_cast<StatsHistogram*>(histogram); - // Validate upcast by seeing that we're probably providing the checksum. - CHECK_EQ(return_histogram->StatsHistogram::CalculateRangeChecksum(), - return_histogram->CalculateRangeChecksum()); return return_histogram; } @@ -87,10 +83,4 @@ Histogram::Inconsistencies StatsHistogram::FindCorruption( return NO_INCONSISTENCIES; // This class won't monitor inconsistencies. } -uint32 StatsHistogram::CalculateRangeChecksum() const { - // We don't calculate checksums, so at least establish a unique constant. - const uint32 kStatsHistogramChecksum = 0x0cecce; - return kStatsHistogramChecksum; -} - } // namespace disk_cache diff --git a/net/disk_cache/stats_histogram.h b/net/disk_cache/stats_histogram.h index 97b2d401..f3af304 100644 --- a/net/disk_cache/stats_histogram.h +++ b/net/disk_cache/stats_histogram.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -29,9 +29,11 @@ class StatsHistogram : public base::Histogram { } }; - StatsHistogram(const std::string& name, Sample minimum, - Sample maximum, size_t bucket_count) - : Histogram(name, minimum, maximum, bucket_count), init_(false) {} + StatsHistogram(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count) + : Histogram(name, minimum, maximum, bucket_count, NULL), init_(false) {} virtual ~StatsHistogram(); static StatsHistogram* FactoryGet(const std::string& name); @@ -44,7 +46,6 @@ class StatsHistogram : public base::Histogram { virtual void SnapshotSample(SampleSet* sample) const OVERRIDE; virtual Inconsistencies FindCorruption( const SampleSet& snapshot) const OVERRIDE; - virtual uint32 CalculateRangeChecksum() const OVERRIDE; private: bool init_; diff --git a/net/socket_stream/socket_stream_metrics_unittest.cc b/net/socket_stream/socket_stream_metrics_unittest.cc index d4daf1e..f644f8d 100644 --- a/net/socket_stream/socket_stream_metrics_unittest.cc +++ b/net/socket_stream/socket_stream_metrics_unittest.cc @@ -41,7 +41,7 @@ TEST(SocketStreamMetricsTest, ProtocolType) { Histogram::SampleSet sample; histogram->SnapshotSample(&sample); - original.Resize(*histogram); // Ensure |original| size is same as |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)); @@ -75,7 +75,7 @@ TEST(SocketStreamMetricsTest, ConnectionType) { Histogram::SampleSet sample; histogram->SnapshotSample(&sample); - original.Resize(*histogram); + original.Resize(histogram->bucket_count()); sample.Subtract(original); EXPECT_EQ(1, sample.counts(SocketStreamMetrics::ALL_CONNECTIONS)); EXPECT_EQ(2, sample.counts(SocketStreamMetrics::TUNNEL_CONNECTION)); @@ -106,7 +106,7 @@ TEST(SocketStreamMetricsTest, WireProtocolType) { Histogram::SampleSet sample; histogram->SnapshotSample(&sample); - original.Resize(*histogram); + 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)); diff --git a/net/url_request/url_request_throttler_unittest.cc b/net/url_request/url_request_throttler_unittest.cc index 4721d4c..bc71a4b 100644 --- a/net/url_request/url_request_throttler_unittest.cc +++ b/net/url_request/url_request_throttler_unittest.cc @@ -228,7 +228,7 @@ void URLRequestThrottlerEntryTest::CalculateHistogramDeltas() { histogram->SnapshotSample(&sample); // Ensure |original| size is same as |sample|, then subtract original // values. - original.Resize(*histogram); + original.Resize(histogram->bucket_count()); sample.Subtract(original); } } |