tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

histogram.cc (23482B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 // Histogram is an object that aggregates statistics, and can summarize them in
      8 // various forms, including ASCII graphical, HTML, and numerically (as a
      9 // vector of numbers corresponding to each of the aggregating buckets).
     10 // See header file for details and examples.
     11 
     12 #include "base/histogram.h"
     13 
     14 #include <math.h>
     15 
     16 #include <string>
     17 
     18 #include "base/logging.h"
     19 #include "base/pickle.h"
     20 #include "base/string_util.h"
     21 #include "base/logging.h"
     22 
     23 namespace base {
     24 
     25 #define CHECK_GT DCHECK_GT
     26 #define CHECK_LT DCHECK_LT
     27 
     28 // Static table of checksums for all possible 8 bit bytes.
     29 const uint32_t Histogram::kCrcTable[256] = {
     30    0x0,         0x77073096L, 0xee0e612cL, 0x990951baL, 0x76dc419L,
     31    0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,  0x79dcb8a4L,
     32    0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL,  0x7eb17cbdL, 0xe7b82d07L,
     33    0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
     34    0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
     35    0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
     36    0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
     37    0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
     38    0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
     39    0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
     40    0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
     41    0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
     42    0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
     43    0x1db7106L,  0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x6b6b51fL,
     44    0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L,  0x9609a88eL,
     45    0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL,  0x91646c97L, 0xe6635c01L,
     46    0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
     47    0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
     48    0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
     49    0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
     50    0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
     51    0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
     52    0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
     53    0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
     54    0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
     55    0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
     56    0x3b6e20cL,  0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
     57    0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL,  0x7a6a5aa8L,
     58    0xe40ecf0bL, 0x9309ff9dL, 0xa00ae27L,  0x7d079eb1L, 0xf00f9344L,
     59    0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
     60    0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
     61    0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
     62    0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
     63    0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
     64    0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
     65    0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
     66    0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
     67    0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
     68    0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
     69    0x26d930aL,  0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L,
     70    0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L,  0x92d28e9bL,
     71    0xe5d5be0dL, 0x7cdcefb7L, 0xbdbdf21L,  0x86d3d2d4L, 0xf1d4e242L,
     72    0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
     73    0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
     74    0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
     75    0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
     76    0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
     77    0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
     78    0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
     79    0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
     80    0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
     81    0x2d02ef8dL,
     82 };
     83 
     84 typedef Histogram::Count Count;
     85 
     86 // static
     87 const size_t Histogram::kBucketCount_MAX = 16384u;
     88 
     89 Histogram* Histogram::FactoryGet(Sample minimum, Sample maximum,
     90                                 size_t bucket_count, Flags flags,
     91                                 const int* buckets) {
     92  DCHECK(buckets);
     93  Histogram* histogram(NULL);
     94 
     95  // Defensive code.
     96  if (minimum < 1) minimum = 1;
     97  if (maximum > kSampleType_MAX - 1) maximum = kSampleType_MAX - 1;
     98 
     99  histogram = new Histogram(minimum, maximum, bucket_count);
    100  histogram->InitializeBucketRangeFromData(buckets);
    101  histogram->SetFlags(flags);
    102 
    103  DCHECK_EQ(HISTOGRAM, histogram->histogram_type());
    104  DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
    105  return histogram;
    106 }
    107 
    108 void Histogram::Add(int value) {
    109  if (value > kSampleType_MAX - 1) value = kSampleType_MAX - 1;
    110  if (value < 0) value = 0;
    111  size_t index = BucketIndex(value);
    112  DCHECK_GE(value, ranges(index));
    113  DCHECK_LT(value, ranges(index + 1));
    114  Accumulate(value, 1, index);
    115 }
    116 
    117 void Histogram::Subtract(int value) {
    118  if (value > kSampleType_MAX - 1) value = kSampleType_MAX - 1;
    119  if (value < 0) value = 0;
    120  size_t index = BucketIndex(value);
    121  DCHECK_GE(value, ranges(index));
    122  DCHECK_LT(value, ranges(index + 1));
    123  Accumulate(value, -1, index);
    124 }
    125 
    126 void Histogram::AddBoolean(bool value) { DCHECK(false); }
    127 
    128 void Histogram::AddSampleSet(const SampleSet& sample) { sample_.Add(sample); }
    129 
    130 void Histogram::Clear() { sample_.Clear(); }
    131 
    132 void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
    133  DCHECK(false);
    134 }
    135 
    136 //------------------------------------------------------------------------------
    137 // Methods for the validating a sample and a related histogram.
    138 //------------------------------------------------------------------------------
    139 
    140 Histogram::Inconsistencies Histogram::FindCorruption(
    141    const SampleSet& snapshot) const {
    142  int inconsistencies = NO_INCONSISTENCIES;
    143  Sample previous_range = -1;  // Bottom range is always 0.
    144  int64_t count = 0;
    145  for (size_t index = 0; index < bucket_count(); ++index) {
    146    count += snapshot.counts(index);
    147    int new_range = ranges(index);
    148    if (previous_range >= new_range) inconsistencies |= BUCKET_ORDER_ERROR;
    149    previous_range = new_range;
    150  }
    151 
    152  if (!HasValidRangeChecksum()) inconsistencies |= RANGE_CHECKSUM_ERROR;
    153 
    154  int64_t delta64 = snapshot.redundant_count() - count;
    155  if (delta64 != 0) {
    156    int delta = static_cast<int>(delta64);
    157    if (delta != delta64) delta = INT_MAX;  // Flag all giant errors as INT_MAX.
    158    // Since snapshots of histograms are taken asynchronously relative to
    159    // sampling (and snapped from different threads), it is pretty likely that
    160    // we'll catch a redundant count that doesn't match the sample count.  We
    161    // allow for a certain amount of slop before flagging this as an
    162    // inconsistency.  Even with an inconsistency, we'll snapshot it again (for
    163    // UMA in about a half hour, so we'll eventually get the data, if it was
    164    // not the result of a corruption.  If histograms show that 1 is "too tight"
    165    // then we may try to use 2 or 3 for this slop value.
    166    const int kCommonRaceBasedCountMismatch = 1;
    167    if (delta > 0) {
    168      if (delta > kCommonRaceBasedCountMismatch)
    169        inconsistencies |= COUNT_HIGH_ERROR;
    170    } else {
    171      DCHECK_GT(0, delta);
    172      if (-delta > kCommonRaceBasedCountMismatch)
    173        inconsistencies |= COUNT_LOW_ERROR;
    174    }
    175  }
    176  return static_cast<Inconsistencies>(inconsistencies);
    177 }
    178 
    179 Histogram::ClassType Histogram::histogram_type() const { return HISTOGRAM; }
    180 
    181 Histogram::Sample Histogram::ranges(size_t i) const { return ranges_[i]; }
    182 
    183 size_t Histogram::bucket_count() const { return bucket_count_; }
    184 
    185 Histogram::SampleSet Histogram::SnapshotSample() const {
    186  return sample_.Clone();
    187 }
    188 
    189 bool Histogram::HasConstructorArguments(Sample minimum, Sample maximum,
    190                                        size_t bucket_count) {
    191  return ((minimum == declared_min_) && (maximum == declared_max_) &&
    192          (bucket_count == bucket_count_));
    193 }
    194 
    195 bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum,
    196                                                 TimeDelta maximum,
    197                                                 size_t bucket_count) {
    198  return ((minimum.InMilliseconds() == declared_min_) &&
    199          (maximum.InMilliseconds() == declared_max_) &&
    200          (bucket_count == bucket_count_));
    201 }
    202 
    203 bool Histogram::HasValidRangeChecksum() const {
    204  return CalculateRangeChecksum() == range_checksum_;
    205 }
    206 
    207 size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
    208  size_t n = 0;
    209  n += aMallocSizeOf(this);
    210  n += sample_.SizeOfExcludingThis(aMallocSizeOf);
    211  return n;
    212 }
    213 
    214 size_t Histogram::SampleSet::SizeOfExcludingThis(
    215    mozilla::MallocSizeOf aMallocSizeOf) {
    216  return counts_.ShallowSizeOfExcludingThis(aMallocSizeOf);
    217 }
    218 
    219 Histogram::Histogram(Sample minimum, Sample maximum, size_t bucket_count)
    220    : declared_min_(minimum),
    221      declared_max_(maximum),
    222      bucket_count_(bucket_count),
    223      flags_(kNoFlags),
    224      range_checksum_(0) {
    225  Initialize();
    226 }
    227 
    228 Histogram::Histogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count)
    229    : declared_min_(static_cast<int>(minimum.InMilliseconds())),
    230      declared_max_(static_cast<int>(maximum.InMilliseconds())),
    231      bucket_count_(bucket_count),
    232      flags_(kNoFlags),
    233      range_checksum_(0) {
    234  Initialize();
    235 }
    236 
    237 Histogram::~Histogram() {
    238  // Just to make sure most derived class did this properly...
    239  DCHECK(ValidateBucketRanges());
    240 }
    241 
    242 void Histogram::InitializeBucketRangeFromData(const int* buckets) {
    243  ranges_ = buckets;
    244  ResetRangeChecksum();
    245  DCHECK(ValidateBucketRanges());
    246 }
    247 
    248 bool Histogram::PrintEmptyBucket(size_t index) const { return true; }
    249 
    250 size_t Histogram::BucketIndex(Sample value) const {
    251  // Use simple binary search.  This is very general, but there are better
    252  // approaches if we knew that the buckets were linearly distributed.
    253  DCHECK_LE(ranges(0), value);
    254  DCHECK_GT(ranges(bucket_count()), value);
    255  size_t under = 0;
    256  size_t over = bucket_count();
    257  size_t mid;
    258 
    259  do {
    260    DCHECK_GE(over, under);
    261    mid = under + (over - under) / 2;
    262    if (mid == under) break;
    263    if (ranges(mid) <= value)
    264      under = mid;
    265    else
    266      over = mid;
    267  } while (true);
    268 
    269  DCHECK_LE(ranges(mid), value);
    270  CHECK_GT(ranges(mid + 1), value);
    271  return mid;
    272 }
    273 
    274 // Use the actual bucket widths (like a linear histogram) until the widths get
    275 // over some transition value, and then use that transition width.  Exponentials
    276 // get so big so fast (and we don't expect to see a lot of entries in the large
    277 // buckets), so we need this to make it possible to see what is going on and
    278 // not have 0-graphical-height buckets.
    279 double Histogram::GetBucketSize(Count current, size_t i) const {
    280  DCHECK_GT(ranges(i + 1), ranges(i));
    281  static const double kTransitionWidth = 5;
    282  double denominator = ranges(i + 1) - ranges(i);
    283  if (denominator > kTransitionWidth)
    284    denominator = kTransitionWidth;  // Stop trying to normalize.
    285  return current / denominator;
    286 }
    287 
    288 void Histogram::ResetRangeChecksum() {
    289  range_checksum_ = CalculateRangeChecksum();
    290 }
    291 
    292 const std::string Histogram::GetAsciiBucketRange(size_t i) const {
    293  std::string result;
    294  if (kHexRangePrintingFlag & flags_)
    295    StringAppendF(&result, "%#x", ranges(i));
    296  else
    297    StringAppendF(&result, "%d", ranges(i));
    298  return result;
    299 }
    300 
    301 // Update histogram data with new sample.
    302 void Histogram::Accumulate(Sample value, Count count, size_t index) {
    303  sample_.Accumulate(value, count, index);
    304 }
    305 
    306 bool Histogram::ValidateBucketRanges() const {
    307  // Standard assertions that all bucket ranges should satisfy.
    308  DCHECK_EQ(0, ranges_[bucket_count_ + 1]);
    309  DCHECK_EQ(0, ranges_[0]);
    310  DCHECK_EQ(declared_min(), ranges_[1]);
    311  DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]);
    312  DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]);
    313  return true;
    314 }
    315 
    316 uint32_t Histogram::CalculateRangeChecksum() const {
    317  DCHECK_EQ(0, ranges_[bucket_count_ + 1]);
    318  uint32_t checksum =
    319      static_cast<uint32_t>(bucket_count_ + 1);  // Seed checksum.
    320  for (size_t index = 0; index < bucket_count(); ++index)
    321    checksum = Crc32(checksum, ranges(index));
    322  return checksum;
    323 }
    324 
    325 void Histogram::Initialize() {
    326  sample_.Resize(*this);
    327  if (declared_min_ < 1) declared_min_ = 1;
    328  if (declared_max_ > kSampleType_MAX - 1) declared_max_ = kSampleType_MAX - 1;
    329  DCHECK_LE(declared_min_, declared_max_);
    330  DCHECK_GT(bucket_count_, 1u);
    331  CHECK_LT(bucket_count_, kBucketCount_MAX);
    332  size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
    333  DCHECK_LE(bucket_count_, maximal_bucket_count);
    334 }
    335 
    336 // We generate the CRC-32 using the low order bits to select whether to XOR in
    337 // the reversed polynomial 0xedb88320L.  This is nice and simple, and allows us
    338 // to keep the quotient in a uint32_t.  Since we're not concerned about the
    339 // nature of corruptions (i.e., we don't care about bit sequencing, since we are
    340 // handling memory changes, which are more grotesque) so we don't bother to
    341 // get the CRC correct for big-endian vs little-ending calculations.  All we
    342 // need is a nice hash, that tends to depend on all the bits of the sample, with
    343 // very little chance of changes in one place impacting changes in another
    344 // place.
    345 uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
    346  const bool kUseRealCrc = true;  // TODO(jar): Switch to false and watch stats.
    347  if (kUseRealCrc) {
    348    union {
    349      Histogram::Sample range;
    350      unsigned char bytes[sizeof(Histogram::Sample)];
    351    } converter;
    352    converter.range = range;
    353    for (size_t i = 0; i < sizeof(converter); ++i)
    354      sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
    355  } else {
    356    // Use hash techniques provided in ReallyFastHash, except we don't care
    357    // about "avalanching" (which would worsten the hash, and add collisions),
    358    // and we don't care about edge cases since we have an even number of bytes.
    359    union {
    360      Histogram::Sample range;
    361      uint16_t ints[sizeof(Histogram::Sample) / 2];
    362    } converter;
    363    DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter));
    364    converter.range = range;
    365    sum += converter.ints[0];
    366    sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11);
    367    sum += sum >> 11;
    368  }
    369  return sum;
    370 }
    371 
    372 //------------------------------------------------------------------------------
    373 // Private methods
    374 
    375 double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
    376  double max = 0;
    377  for (size_t i = 0; i < bucket_count(); ++i) {
    378    double current_size = GetBucketSize(snapshot.counts(i), i);
    379    if (current_size > max) max = current_size;
    380  }
    381  return max;
    382 }
    383 
    384 //------------------------------------------------------------------------------
    385 // Methods for the Histogram::SampleSet class
    386 //------------------------------------------------------------------------------
    387 
    388 Histogram::SampleSet::SampleSet() : sum_(0), redundant_count_(0) {}
    389 
    390 Histogram::SampleSet::~SampleSet() = default;
    391 
    392 void Histogram::SampleSet::Resize(const Histogram& histogram) {
    393  size_t oldSize = counts_.Length();
    394  counts_.SetLength(histogram.bucket_count());
    395 
    396  for (size_t i = oldSize; i < histogram.bucket_count(); ++i) counts_[i] = 0;
    397 }
    398 
    399 void Histogram::SampleSet::Accumulate(Sample value, Count count, size_t index) {
    400  DCHECK(count == 1 || count == -1);
    401  counts_[index] += count;
    402  redundant_count_ += count;
    403  sum_ += static_cast<int64_t>(count) * value;
    404  DCHECK_GE(counts_[index], 0);
    405  DCHECK_GE(sum_, 0);
    406  DCHECK_GE(redundant_count_, 0);
    407 }
    408 
    409 Count Histogram::SampleSet::TotalCount() const {
    410  Count total = 0;
    411  for (Counts::const_iterator it = counts_.begin(); it != counts_.end(); ++it) {
    412    total += *it;
    413  }
    414  return total;
    415 }
    416 
    417 void Histogram::SampleSet::Add(const SampleSet& other) {
    418  DCHECK_EQ(counts_.Length(), other.counts_.Length());
    419  sum_ += other.sum_;
    420  redundant_count_ += other.redundant_count_;
    421  for (size_t index = 0; index < counts_.Length(); ++index)
    422    counts_[index] += other.counts_[index];
    423 }
    424 
    425 //------------------------------------------------------------------------------
    426 // LinearHistogram: This histogram uses a traditional set of evenly spaced
    427 // buckets.
    428 //------------------------------------------------------------------------------
    429 
    430 LinearHistogram::~LinearHistogram() = default;
    431 
    432 Histogram* LinearHistogram::FactoryGet(Sample minimum, Sample maximum,
    433                                       size_t bucket_count, Flags flags,
    434                                       const int* buckets) {
    435  Histogram* histogram(NULL);
    436 
    437  if (minimum < 1) minimum = 1;
    438  if (maximum > kSampleType_MAX - 1) maximum = kSampleType_MAX - 1;
    439 
    440  LinearHistogram* linear_histogram =
    441      new LinearHistogram(minimum, maximum, bucket_count);
    442  linear_histogram->InitializeBucketRangeFromData(buckets);
    443  linear_histogram->SetFlags(flags);
    444  histogram = linear_histogram;
    445 
    446  DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type());
    447  DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
    448  return histogram;
    449 }
    450 
    451 Histogram::ClassType LinearHistogram::histogram_type() const {
    452  return LINEAR_HISTOGRAM;
    453 }
    454 
    455 void LinearHistogram::Accumulate(Sample value, Count count, size_t index) {
    456  sample_.Accumulate(value, count, index);
    457 }
    458 
    459 void LinearHistogram::SetRangeDescriptions(
    460    const DescriptionPair descriptions[]) {
    461  for (int i = 0; descriptions[i].description; ++i) {
    462    bucket_description_[descriptions[i].sample] = descriptions[i].description;
    463  }
    464 }
    465 
    466 LinearHistogram::LinearHistogram(Sample minimum, Sample maximum,
    467                                 size_t bucket_count)
    468    : Histogram(minimum >= 1 ? minimum : 1, maximum, bucket_count) {}
    469 
    470 LinearHistogram::LinearHistogram(TimeDelta minimum, TimeDelta maximum,
    471                                 size_t bucket_count)
    472    : Histogram(minimum >= TimeDelta::FromMilliseconds(1)
    473                    ? minimum
    474                    : TimeDelta::FromMilliseconds(1),
    475                maximum, bucket_count) {}
    476 
    477 double LinearHistogram::GetBucketSize(Count current, size_t i) const {
    478  DCHECK_GT(ranges(i + 1), ranges(i));
    479  // Adjacent buckets with different widths would have "surprisingly" many (few)
    480  // samples in a histogram if we didn't normalize this way.
    481  double denominator = ranges(i + 1) - ranges(i);
    482  return current / denominator;
    483 }
    484 
    485 const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
    486  int range = ranges(i);
    487  BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
    488  if (it == bucket_description_.end()) return Histogram::GetAsciiBucketRange(i);
    489  return it->second;
    490 }
    491 
    492 bool LinearHistogram::PrintEmptyBucket(size_t index) const {
    493  return bucket_description_.find(ranges(index)) == bucket_description_.end();
    494 }
    495 
    496 //------------------------------------------------------------------------------
    497 // This section provides implementation for BooleanHistogram.
    498 //------------------------------------------------------------------------------
    499 
    500 Histogram* BooleanHistogram::FactoryGet(Flags flags, const int* buckets) {
    501  Histogram* histogram(NULL);
    502 
    503  BooleanHistogram* tentative_histogram = new BooleanHistogram();
    504  tentative_histogram->InitializeBucketRangeFromData(buckets);
    505  tentative_histogram->SetFlags(flags);
    506  histogram = tentative_histogram;
    507 
    508  DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type());
    509  return histogram;
    510 }
    511 
    512 Histogram::ClassType BooleanHistogram::histogram_type() const {
    513  return BOOLEAN_HISTOGRAM;
    514 }
    515 
    516 void BooleanHistogram::AddBoolean(bool value) { Add(value ? 1 : 0); }
    517 
    518 BooleanHistogram::BooleanHistogram() : LinearHistogram(1, 2, 3) {}
    519 
    520 void BooleanHistogram::Accumulate(Sample value, Count count, size_t index) {
    521  // Callers will have computed index based on the non-booleanified value.
    522  // So we need to adjust the index manually.
    523  LinearHistogram::Accumulate(!!value, count, value ? 1 : 0);
    524 }
    525 
    526 //------------------------------------------------------------------------------
    527 // FlagHistogram:
    528 //------------------------------------------------------------------------------
    529 
    530 Histogram* FlagHistogram::FactoryGet(Flags flags, const int* buckets) {
    531  Histogram* h(nullptr);
    532 
    533  FlagHistogram* fh = new FlagHistogram();
    534  fh->InitializeBucketRangeFromData(buckets);
    535  fh->SetFlags(flags);
    536  size_t zero_index = fh->BucketIndex(0);
    537  fh->LinearHistogram::Accumulate(0, 1, zero_index);
    538  h = fh;
    539 
    540  return h;
    541 }
    542 
    543 FlagHistogram::FlagHistogram() : mSwitched(false) {}
    544 
    545 Histogram::ClassType FlagHistogram::histogram_type() const {
    546  return FLAG_HISTOGRAM;
    547 }
    548 
    549 void FlagHistogram::Accumulate(Sample value, Count count, size_t index) {
    550  if (mSwitched) {
    551    return;
    552  }
    553 
    554  mSwitched = true;
    555  DCHECK_EQ(value, 1);
    556  LinearHistogram::Accumulate(value, 1, index);
    557  size_t zero_index = BucketIndex(0);
    558  LinearHistogram::Accumulate(0, -1, zero_index);
    559 }
    560 
    561 void FlagHistogram::AddSampleSet(const SampleSet& sample) {
    562  DCHECK_EQ(bucket_count(), sample.size());
    563  // We can't be sure the SampleSet provided came from another FlagHistogram,
    564  // so we take the following steps:
    565  //  - If our flag has already been set do nothing.
    566  //  - Set our flag if the following hold:
    567  //      - The sum of the counts in the provided SampleSet is 1.
    568  //      - The bucket index for that single value is the same as the index
    569  //      where we
    570  //        would place our set flag.
    571  //  - Otherwise, take no action.
    572 
    573  if (mSwitched) {
    574    return;
    575  }
    576 
    577  if (sample.sum() != 1) {
    578    return;
    579  }
    580 
    581  size_t one_index = BucketIndex(1);
    582  if (sample.counts(one_index) == 1) {
    583    Accumulate(1, 1, one_index);
    584  }
    585 }
    586 
    587 void FlagHistogram::Clear() {
    588  Histogram::Clear();
    589 
    590  mSwitched = false;
    591  size_t zero_index = BucketIndex(0);
    592  LinearHistogram::Accumulate(0, 1, zero_index);
    593 }
    594 
    595 //------------------------------------------------------------------------------
    596 // CountHistogram:
    597 //------------------------------------------------------------------------------
    598 
    599 Histogram* CountHistogram::FactoryGet(Flags flags, const int* buckets) {
    600  Histogram* h(nullptr);
    601 
    602  CountHistogram* fh = new CountHistogram();
    603  fh->InitializeBucketRangeFromData(buckets);
    604  fh->SetFlags(flags);
    605  h = fh;
    606 
    607  return h;
    608 }
    609 
    610 CountHistogram::CountHistogram() : LinearHistogram(1, 2, 3) {}
    611 
    612 Histogram::ClassType CountHistogram::histogram_type() const {
    613  return COUNT_HISTOGRAM;
    614 }
    615 
    616 void CountHistogram::Accumulate(Sample value, Count count, size_t index) {
    617  size_t zero_index = BucketIndex(0);
    618  LinearHistogram::Accumulate(value, 1, zero_index);
    619 }
    620 
    621 void CountHistogram::AddSampleSet(const SampleSet& sample) {
    622  DCHECK_EQ(bucket_count(), sample.size());
    623  // We can't be sure the SampleSet provided came from another CountHistogram,
    624  // so we at least check that the unused buckets are empty.
    625 
    626  const size_t indices[] = {BucketIndex(0), BucketIndex(1), BucketIndex(2)};
    627 
    628  if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) {
    629    return;
    630  }
    631 
    632  if (sample.counts(indices[0]) != 0) {
    633    Histogram::AddSampleSet(sample);
    634  }
    635 }
    636 
    637 }  // namespace base