Home | History | Annotate | Download | only in metrics
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Histogram is an object that aggregates statistics, and can summarize them in
      6 // various forms, including ASCII graphical, HTML, and numerically (as a
      7 // vector of numbers corresponding to each of the aggregating buckets).
      8 // See header file for details and examples.
      9 
     10 #include "base/metrics/histogram.h"
     11 
     12 #include <math.h>
     13 
     14 #include <algorithm>
     15 #include <string>
     16 
     17 #include "base/compiler_specific.h"
     18 #include "base/debug/alias.h"
     19 #include "base/logging.h"
     20 #include "base/metrics/sample_vector.h"
     21 #include "base/metrics/statistics_recorder.h"
     22 #include "base/pickle.h"
     23 #include "base/strings/string_util.h"
     24 #include "base/strings/stringprintf.h"
     25 #include "base/synchronization/lock.h"
     26 #include "base/values.h"
     27 
     28 using std::string;
     29 using std::vector;
     30 
     31 namespace base {
     32 
     33 namespace {
     34 
     35 bool ReadHistogramArguments(PickleIterator* iter,
     36                             string* histogram_name,
     37                             int* flags,
     38                             int* declared_min,
     39                             int* declared_max,
     40                             uint64* bucket_count,
     41                             uint32* range_checksum) {
     42   if (!iter->ReadString(histogram_name) ||
     43       !iter->ReadInt(flags) ||
     44       !iter->ReadInt(declared_min) ||
     45       !iter->ReadInt(declared_max) ||
     46       !iter->ReadUInt64(bucket_count) ||
     47       !iter->ReadUInt32(range_checksum)) {
     48     DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name;
     49     return false;
     50   }
     51 
     52   // Since these fields may have come from an untrusted renderer, do additional
     53   // checks above and beyond those in Histogram::Initialize()
     54   if (*declared_max <= 0 ||
     55       *declared_min <= 0 ||
     56       *declared_max < *declared_min ||
     57       INT_MAX / sizeof(HistogramBase::Count) <= *bucket_count ||
     58       *bucket_count < 2) {
     59     DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
     60     return false;
     61   }
     62 
     63   // We use the arguments to find or create the local version of the histogram
     64   // in this process, so we need to clear the IPC flag.
     65   DCHECK(*flags & HistogramBase::kIPCSerializationSourceFlag);
     66   *flags &= ~HistogramBase::kIPCSerializationSourceFlag;
     67 
     68   return true;
     69 }
     70 
     71 bool ValidateRangeChecksum(const HistogramBase& histogram,
     72                            uint32 range_checksum) {
     73   const Histogram& casted_histogram =
     74       static_cast<const Histogram&>(histogram);
     75 
     76   return casted_histogram.bucket_ranges()->checksum() == range_checksum;
     77 }
     78 
     79 }  // namespace
     80 
     81 typedef HistogramBase::Count Count;
     82 typedef HistogramBase::Sample Sample;
     83 
     84 // static
     85 const size_t Histogram::kBucketCount_MAX = 16384u;
     86 
     87 HistogramBase* Histogram::FactoryGet(const string& name,
     88                                      Sample minimum,
     89                                      Sample maximum,
     90                                      size_t bucket_count,
     91                                      int32 flags) {
     92   bool valid_arguments =
     93       InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
     94   DCHECK(valid_arguments);
     95 
     96   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
     97   if (!histogram) {
     98     // To avoid racy destruction at shutdown, the following will be leaked.
     99     BucketRanges* ranges = new BucketRanges(bucket_count + 1);
    100     InitializeBucketRanges(minimum, maximum, ranges);
    101     const BucketRanges* registered_ranges =
    102         StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
    103 
    104     Histogram* tentative_histogram =
    105         new Histogram(name, minimum, maximum, registered_ranges);
    106 
    107     tentative_histogram->SetFlags(flags);
    108     histogram =
    109         StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
    110   }
    111 
    112   DCHECK_EQ(HISTOGRAM, histogram->GetHistogramType());
    113   if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) {
    114     // The construction arguments do not match the existing histogram.  This can
    115     // come about if an extension updates in the middle of a chrome run and has
    116     // changed one of them, or simply by bad code within Chrome itself.  We
    117     // return NULL here with the expectation that bad code in Chrome will crash
    118     // on dereference, but extension/Pepper APIs will guard against NULL and not
    119     // crash.
    120     DLOG(ERROR) << "Histogram " << name << " has bad construction arguments";
    121     return NULL;
    122   }
    123   return histogram;
    124 }
    125 
    126 HistogramBase* Histogram::FactoryTimeGet(const string& name,
    127                                          TimeDelta minimum,
    128                                          TimeDelta maximum,
    129                                          size_t bucket_count,
    130                                          int32 flags) {
    131   return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
    132                     bucket_count, flags);
    133 }
    134 
    135 // Calculate what range of values are held in each bucket.
    136 // We have to be careful that we don't pick a ratio between starting points in
    137 // consecutive buckets that is sooo small, that the integer bounds are the same
    138 // (effectively making one bucket get no values).  We need to avoid:
    139 //   ranges(i) == ranges(i + 1)
    140 // To avoid that, we just do a fine-grained bucket width as far as we need to
    141 // until we get a ratio that moves us along at least 2 units at a time.  From
    142 // that bucket onward we do use the exponential growth of buckets.
    143 //
    144 // static
    145 void Histogram::InitializeBucketRanges(Sample minimum,
    146                                        Sample maximum,
    147                                        BucketRanges* ranges) {
    148   double log_max = log(static_cast<double>(maximum));
    149   double log_ratio;
    150   double log_next;
    151   size_t bucket_index = 1;
    152   Sample current = minimum;
    153   ranges->set_range(bucket_index, current);
    154   size_t bucket_count = ranges->bucket_count();
    155   while (bucket_count > ++bucket_index) {
    156     double log_current;
    157     log_current = log(static_cast<double>(current));
    158     // Calculate the count'th root of the range.
    159     log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
    160     // See where the next bucket would start.
    161     log_next = log_current + log_ratio;
    162     Sample next;
    163     next = static_cast<int>(floor(exp(log_next) + 0.5));
    164     if (next > current)
    165       current = next;
    166     else
    167       ++current;  // Just do a narrow bucket, and keep trying.
    168     ranges->set_range(bucket_index, current);
    169   }
    170   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
    171   ranges->ResetChecksum();
    172 }
    173 
    174 // static
    175 const int Histogram::kCommonRaceBasedCountMismatch = 5;
    176 
    177 int Histogram::FindCorruption(const HistogramSamples& samples) const {
    178   int inconsistencies = NO_INCONSISTENCIES;
    179   Sample previous_range = -1;  // Bottom range is always 0.
    180   for (size_t index = 0; index < bucket_count(); ++index) {
    181     int new_range = ranges(index);
    182     if (previous_range >= new_range)
    183       inconsistencies |= BUCKET_ORDER_ERROR;
    184     previous_range = new_range;
    185   }
    186 
    187   if (!bucket_ranges()->HasValidChecksum())
    188     inconsistencies |= RANGE_CHECKSUM_ERROR;
    189 
    190   int64 delta64 = samples.redundant_count() - samples.TotalCount();
    191   if (delta64 != 0) {
    192     int delta = static_cast<int>(delta64);
    193     if (delta != delta64)
    194       delta = INT_MAX;  // Flag all giant errors as INT_MAX.
    195     if (delta > 0) {
    196       UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
    197       if (delta > kCommonRaceBasedCountMismatch)
    198         inconsistencies |= COUNT_HIGH_ERROR;
    199     } else {
    200       DCHECK_GT(0, delta);
    201       UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta);
    202       if (-delta > kCommonRaceBasedCountMismatch)
    203         inconsistencies |= COUNT_LOW_ERROR;
    204     }
    205   }
    206   return inconsistencies;
    207 }
    208 
    209 Sample Histogram::ranges(size_t i) const {
    210   return bucket_ranges_->range(i);
    211 }
    212 
    213 size_t Histogram::bucket_count() const {
    214   return bucket_ranges_->bucket_count();
    215 }
    216 
    217 // static
    218 bool Histogram::InspectConstructionArguments(const string& name,
    219                                              Sample* minimum,
    220                                              Sample* maximum,
    221                                              size_t* bucket_count) {
    222   // Defensive code for backward compatibility.
    223   if (*minimum < 1) {
    224     DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum;
    225     *minimum = 1;
    226   }
    227   if (*maximum >= kSampleType_MAX) {
    228     DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum;
    229     *maximum = kSampleType_MAX - 1;
    230   }
    231   if (*bucket_count >= kBucketCount_MAX) {
    232     DVLOG(1) << "Histogram: " << name << " has bad bucket_count: "
    233              << *bucket_count;
    234     *bucket_count = kBucketCount_MAX - 1;
    235   }
    236 
    237   if (*minimum >= *maximum)
    238     return false;
    239   if (*bucket_count < 3)
    240     return false;
    241   if (*bucket_count > static_cast<size_t>(*maximum - *minimum + 2))
    242     return false;
    243   return true;
    244 }
    245 
    246 HistogramType Histogram::GetHistogramType() const {
    247   return HISTOGRAM;
    248 }
    249 
    250 bool Histogram::HasConstructionArguments(Sample expected_minimum,
    251                                          Sample expected_maximum,
    252                                          size_t expected_bucket_count) const {
    253   return ((expected_minimum == declared_min_) &&
    254           (expected_maximum == declared_max_) &&
    255           (expected_bucket_count == bucket_count()));
    256 }
    257 
    258 void Histogram::Add(int value) {
    259   DCHECK_EQ(0, ranges(0));
    260   DCHECK_EQ(kSampleType_MAX, ranges(bucket_count()));
    261 
    262   if (value > kSampleType_MAX - 1)
    263     value = kSampleType_MAX - 1;
    264   if (value < 0)
    265     value = 0;
    266   samples_->Accumulate(value, 1);
    267 }
    268 
    269 scoped_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
    270   return SnapshotSampleVector().PassAs<HistogramSamples>();
    271 }
    272 
    273 void Histogram::AddSamples(const HistogramSamples& samples) {
    274   samples_->Add(samples);
    275 }
    276 
    277 bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
    278   return samples_->AddFromPickle(iter);
    279 }
    280 
    281 // The following methods provide a graphical histogram display.
    282 void Histogram::WriteHTMLGraph(string* output) const {
    283   // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
    284   output->append("<PRE>");
    285   WriteAsciiImpl(true, "<br>", output);
    286   output->append("</PRE>");
    287 }
    288 
    289 void Histogram::WriteAscii(string* output) const {
    290   WriteAsciiImpl(true, "\n", output);
    291 }
    292 
    293 bool Histogram::SerializeInfoImpl(Pickle* pickle) const {
    294   DCHECK(bucket_ranges()->HasValidChecksum());
    295   return pickle->WriteString(histogram_name()) &&
    296       pickle->WriteInt(flags()) &&
    297       pickle->WriteInt(declared_min()) &&
    298       pickle->WriteInt(declared_max()) &&
    299       pickle->WriteUInt64(bucket_count()) &&
    300       pickle->WriteUInt32(bucket_ranges()->checksum());
    301 }
    302 
    303 Histogram::Histogram(const string& name,
    304                      Sample minimum,
    305                      Sample maximum,
    306                      const BucketRanges* ranges)
    307   : HistogramBase(name),
    308     bucket_ranges_(ranges),
    309     declared_min_(minimum),
    310     declared_max_(maximum) {
    311   if (ranges)
    312     samples_.reset(new SampleVector(ranges));
    313 }
    314 
    315 Histogram::~Histogram() {
    316 }
    317 
    318 bool Histogram::PrintEmptyBucket(size_t index) const {
    319   return true;
    320 }
    321 
    322 // Use the actual bucket widths (like a linear histogram) until the widths get
    323 // over some transition value, and then use that transition width.  Exponentials
    324 // get so big so fast (and we don't expect to see a lot of entries in the large
    325 // buckets), so we need this to make it possible to see what is going on and
    326 // not have 0-graphical-height buckets.
    327 double Histogram::GetBucketSize(Count current, size_t i) const {
    328   DCHECK_GT(ranges(i + 1), ranges(i));
    329   static const double kTransitionWidth = 5;
    330   double denominator = ranges(i + 1) - ranges(i);
    331   if (denominator > kTransitionWidth)
    332     denominator = kTransitionWidth;  // Stop trying to normalize.
    333   return current/denominator;
    334 }
    335 
    336 const string Histogram::GetAsciiBucketRange(size_t i) const {
    337   return GetSimpleAsciiBucketRange(ranges(i));
    338 }
    339 
    340 //------------------------------------------------------------------------------
    341 // Private methods
    342 
    343 // static
    344 HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) {
    345   string histogram_name;
    346   int flags;
    347   int declared_min;
    348   int declared_max;
    349   uint64 bucket_count;
    350   uint32 range_checksum;
    351 
    352   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
    353                               &declared_max, &bucket_count, &range_checksum)) {
    354     return NULL;
    355   }
    356 
    357   // Find or create the local version of the histogram in this process.
    358   HistogramBase* histogram = Histogram::FactoryGet(
    359       histogram_name, declared_min, declared_max, bucket_count, flags);
    360 
    361   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
    362     // The serialized histogram might be corrupted.
    363     return NULL;
    364   }
    365   return histogram;
    366 }
    367 
    368 scoped_ptr<SampleVector> Histogram::SnapshotSampleVector() const {
    369   scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges()));
    370   samples->Add(*samples_);
    371   return samples.Pass();
    372 }
    373 
    374 void Histogram::WriteAsciiImpl(bool graph_it,
    375                                const string& newline,
    376                                string* output) const {
    377   // Get local (stack) copies of all effectively volatile class data so that we
    378   // are consistent across our output activities.
    379   scoped_ptr<SampleVector> snapshot = SnapshotSampleVector();
    380   Count sample_count = snapshot->TotalCount();
    381 
    382   WriteAsciiHeader(*snapshot, sample_count, output);
    383   output->append(newline);
    384 
    385   // Prepare to normalize graphical rendering of bucket contents.
    386   double max_size = 0;
    387   if (graph_it)
    388     max_size = GetPeakBucketSize(*snapshot);
    389 
    390   // Calculate space needed to print bucket range numbers.  Leave room to print
    391   // nearly the largest bucket range without sliding over the histogram.
    392   size_t largest_non_empty_bucket = bucket_count() - 1;
    393   while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) {
    394     if (0 == largest_non_empty_bucket)
    395       break;  // All buckets are empty.
    396     --largest_non_empty_bucket;
    397   }
    398 
    399   // Calculate largest print width needed for any of our bucket range displays.
    400   size_t print_width = 1;
    401   for (size_t i = 0; i < bucket_count(); ++i) {
    402     if (snapshot->GetCountAtIndex(i)) {
    403       size_t width = GetAsciiBucketRange(i).size() + 1;
    404       if (width > print_width)
    405         print_width = width;
    406     }
    407   }
    408 
    409   int64 remaining = sample_count;
    410   int64 past = 0;
    411   // Output the actual histogram graph.
    412   for (size_t i = 0; i < bucket_count(); ++i) {
    413     Count current = snapshot->GetCountAtIndex(i);
    414     if (!current && !PrintEmptyBucket(i))
    415       continue;
    416     remaining -= current;
    417     string range = GetAsciiBucketRange(i);
    418     output->append(range);
    419     for (size_t j = 0; range.size() + j < print_width + 1; ++j)
    420       output->push_back(' ');
    421     if (0 == current && i < bucket_count() - 1 &&
    422         0 == snapshot->GetCountAtIndex(i + 1)) {
    423       while (i < bucket_count() - 1 &&
    424              0 == snapshot->GetCountAtIndex(i + 1)) {
    425         ++i;
    426       }
    427       output->append("... ");
    428       output->append(newline);
    429       continue;  // No reason to plot emptiness.
    430     }
    431     double current_size = GetBucketSize(current, i);
    432     if (graph_it)
    433       WriteAsciiBucketGraph(current_size, max_size, output);
    434     WriteAsciiBucketContext(past, current, remaining, i, output);
    435     output->append(newline);
    436     past += current;
    437   }
    438   DCHECK_EQ(sample_count, past);
    439 }
    440 
    441 double Histogram::GetPeakBucketSize(const SampleVector& samples) const {
    442   double max = 0;
    443   for (size_t i = 0; i < bucket_count() ; ++i) {
    444     double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
    445     if (current_size > max)
    446       max = current_size;
    447   }
    448   return max;
    449 }
    450 
    451 void Histogram::WriteAsciiHeader(const SampleVector& samples,
    452                                  Count sample_count,
    453                                  string* output) const {
    454   StringAppendF(output,
    455                 "Histogram: %s recorded %d samples",
    456                 histogram_name().c_str(),
    457                 sample_count);
    458   if (0 == sample_count) {
    459     DCHECK_EQ(samples.sum(), 0);
    460   } else {
    461     double average = static_cast<float>(samples.sum()) / sample_count;
    462 
    463     StringAppendF(output, ", average = %.1f", average);
    464   }
    465   if (flags() & ~kHexRangePrintingFlag)
    466     StringAppendF(output, " (flags = 0x%x)", flags() & ~kHexRangePrintingFlag);
    467 }
    468 
    469 void Histogram::WriteAsciiBucketContext(const int64 past,
    470                                         const Count current,
    471                                         const int64 remaining,
    472                                         const size_t i,
    473                                         string* output) const {
    474   double scaled_sum = (past + current + remaining) / 100.0;
    475   WriteAsciiBucketValue(current, scaled_sum, output);
    476   if (0 < i) {
    477     double percentage = past / scaled_sum;
    478     StringAppendF(output, " {%3.1f%%}", percentage);
    479   }
    480 }
    481 
    482 void Histogram::GetParameters(DictionaryValue* params) const {
    483   params->SetString("type", HistogramTypeToString(GetHistogramType()));
    484   params->SetInteger("min", declared_min());
    485   params->SetInteger("max", declared_max());
    486   params->SetInteger("bucket_count", static_cast<int>(bucket_count()));
    487 }
    488 
    489 void Histogram::GetCountAndBucketData(Count* count,
    490                                       int64* sum,
    491                                       ListValue* buckets) const {
    492   scoped_ptr<SampleVector> snapshot = SnapshotSampleVector();
    493   *count = snapshot->TotalCount();
    494   *sum = snapshot->sum();
    495   size_t index = 0;
    496   for (size_t i = 0; i < bucket_count(); ++i) {
    497     Sample count = snapshot->GetCountAtIndex(i);
    498     if (count > 0) {
    499       scoped_ptr<DictionaryValue> bucket_value(new DictionaryValue());
    500       bucket_value->SetInteger("low", ranges(i));
    501       if (i != bucket_count() - 1)
    502         bucket_value->SetInteger("high", ranges(i + 1));
    503       bucket_value->SetInteger("count", count);
    504       buckets->Set(index, bucket_value.release());
    505       ++index;
    506     }
    507   }
    508 }
    509 
    510 //------------------------------------------------------------------------------
    511 // LinearHistogram: This histogram uses a traditional set of evenly spaced
    512 // buckets.
    513 //------------------------------------------------------------------------------
    514 
    515 LinearHistogram::~LinearHistogram() {}
    516 
    517 HistogramBase* LinearHistogram::FactoryGet(const string& name,
    518                                            Sample minimum,
    519                                            Sample maximum,
    520                                            size_t bucket_count,
    521                                            int32 flags) {
    522   return FactoryGetWithRangeDescription(
    523       name, minimum, maximum, bucket_count, flags, NULL);
    524 }
    525 
    526 HistogramBase* LinearHistogram::FactoryTimeGet(const string& name,
    527                                                TimeDelta minimum,
    528                                                TimeDelta maximum,
    529                                                size_t bucket_count,
    530                                                int32 flags) {
    531   return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
    532                     bucket_count, flags);
    533 }
    534 
    535 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
    536       const std::string& name,
    537       Sample minimum,
    538       Sample maximum,
    539       size_t bucket_count,
    540       int32 flags,
    541       const DescriptionPair descriptions[]) {
    542   bool valid_arguments = Histogram::InspectConstructionArguments(
    543       name, &minimum, &maximum, &bucket_count);
    544   DCHECK(valid_arguments);
    545 
    546   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
    547   if (!histogram) {
    548     // To avoid racy destruction at shutdown, the following will be leaked.
    549     BucketRanges* ranges = new BucketRanges(bucket_count + 1);
    550     InitializeBucketRanges(minimum, maximum, ranges);
    551     const BucketRanges* registered_ranges =
    552         StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
    553 
    554     LinearHistogram* tentative_histogram =
    555         new LinearHistogram(name, minimum, maximum, registered_ranges);
    556 
    557     // Set range descriptions.
    558     if (descriptions) {
    559       for (int i = 0; descriptions[i].description; ++i) {
    560         tentative_histogram->bucket_description_[descriptions[i].sample] =
    561             descriptions[i].description;
    562       }
    563     }
    564 
    565     tentative_histogram->SetFlags(flags);
    566     histogram =
    567         StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
    568   }
    569 
    570   DCHECK_EQ(LINEAR_HISTOGRAM, histogram->GetHistogramType());
    571   if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) {
    572     // The construction arguments do not match the existing histogram.  This can
    573     // come about if an extension updates in the middle of a chrome run and has
    574     // changed one of them, or simply by bad code within Chrome itself.  We
    575     // return NULL here with the expectation that bad code in Chrome will crash
    576     // on dereference, but extension/Pepper APIs will guard against NULL and not
    577     // crash.
    578     DLOG(ERROR) << "Histogram " << name << " has bad construction arguments";
    579     return NULL;
    580   }
    581   return histogram;
    582 }
    583 
    584 HistogramType LinearHistogram::GetHistogramType() const {
    585   return LINEAR_HISTOGRAM;
    586 }
    587 
    588 LinearHistogram::LinearHistogram(const string& name,
    589                                  Sample minimum,
    590                                  Sample maximum,
    591                                  const BucketRanges* ranges)
    592     : Histogram(name, minimum, maximum, ranges) {
    593 }
    594 
    595 double LinearHistogram::GetBucketSize(Count current, size_t i) const {
    596   DCHECK_GT(ranges(i + 1), ranges(i));
    597   // Adjacent buckets with different widths would have "surprisingly" many (few)
    598   // samples in a histogram if we didn't normalize this way.
    599   double denominator = ranges(i + 1) - ranges(i);
    600   return current/denominator;
    601 }
    602 
    603 const string LinearHistogram::GetAsciiBucketRange(size_t i) const {
    604   int range = ranges(i);
    605   BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
    606   if (it == bucket_description_.end())
    607     return Histogram::GetAsciiBucketRange(i);
    608   return it->second;
    609 }
    610 
    611 bool LinearHistogram::PrintEmptyBucket(size_t index) const {
    612   return bucket_description_.find(ranges(index)) == bucket_description_.end();
    613 }
    614 
    615 // static
    616 void LinearHistogram::InitializeBucketRanges(Sample minimum,
    617                                              Sample maximum,
    618                                              BucketRanges* ranges) {
    619   double min = minimum;
    620   double max = maximum;
    621   size_t bucket_count = ranges->bucket_count();
    622   for (size_t i = 1; i < bucket_count; ++i) {
    623     double linear_range =
    624         (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2);
    625     ranges->set_range(i, static_cast<Sample>(linear_range + 0.5));
    626   }
    627   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
    628   ranges->ResetChecksum();
    629 }
    630 
    631 // static
    632 HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) {
    633   string histogram_name;
    634   int flags;
    635   int declared_min;
    636   int declared_max;
    637   uint64 bucket_count;
    638   uint32 range_checksum;
    639 
    640   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
    641                               &declared_max, &bucket_count, &range_checksum)) {
    642     return NULL;
    643   }
    644 
    645   HistogramBase* histogram = LinearHistogram::FactoryGet(
    646       histogram_name, declared_min, declared_max, bucket_count, flags);
    647   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
    648     // The serialized histogram might be corrupted.
    649     return NULL;
    650   }
    651   return histogram;
    652 }
    653 
    654 //------------------------------------------------------------------------------
    655 // This section provides implementation for BooleanHistogram.
    656 //------------------------------------------------------------------------------
    657 
    658 HistogramBase* BooleanHistogram::FactoryGet(const string& name, int32 flags) {
    659   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
    660   if (!histogram) {
    661     // To avoid racy destruction at shutdown, the following will be leaked.
    662     BucketRanges* ranges = new BucketRanges(4);
    663     LinearHistogram::InitializeBucketRanges(1, 2, ranges);
    664     const BucketRanges* registered_ranges =
    665         StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
    666 
    667     BooleanHistogram* tentative_histogram =
    668         new BooleanHistogram(name, registered_ranges);
    669 
    670     tentative_histogram->SetFlags(flags);
    671     histogram =
    672         StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
    673   }
    674 
    675   DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->GetHistogramType());
    676   return histogram;
    677 }
    678 
    679 HistogramType BooleanHistogram::GetHistogramType() const {
    680   return BOOLEAN_HISTOGRAM;
    681 }
    682 
    683 BooleanHistogram::BooleanHistogram(const string& name,
    684                                    const BucketRanges* ranges)
    685     : LinearHistogram(name, 1, 2, ranges) {}
    686 
    687 HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
    688   string histogram_name;
    689   int flags;
    690   int declared_min;
    691   int declared_max;
    692   uint64 bucket_count;
    693   uint32 range_checksum;
    694 
    695   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
    696                               &declared_max, &bucket_count, &range_checksum)) {
    697     return NULL;
    698   }
    699 
    700   HistogramBase* histogram = BooleanHistogram::FactoryGet(
    701       histogram_name, flags);
    702   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
    703     // The serialized histogram might be corrupted.
    704     return NULL;
    705   }
    706   return histogram;
    707 }
    708 
    709 //------------------------------------------------------------------------------
    710 // CustomHistogram:
    711 //------------------------------------------------------------------------------
    712 
    713 HistogramBase* CustomHistogram::FactoryGet(const string& name,
    714                                            const vector<Sample>& custom_ranges,
    715                                            int32 flags) {
    716   CHECK(ValidateCustomRanges(custom_ranges));
    717 
    718   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
    719   if (!histogram) {
    720     BucketRanges* ranges = CreateBucketRangesFromCustomRanges(custom_ranges);
    721     const BucketRanges* registered_ranges =
    722         StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
    723 
    724     // To avoid racy destruction at shutdown, the following will be leaked.
    725     CustomHistogram* tentative_histogram =
    726         new CustomHistogram(name, registered_ranges);
    727 
    728     tentative_histogram->SetFlags(flags);
    729 
    730     histogram =
    731         StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
    732   }
    733 
    734   DCHECK_EQ(histogram->GetHistogramType(), CUSTOM_HISTOGRAM);
    735   return histogram;
    736 }
    737 
    738 HistogramType CustomHistogram::GetHistogramType() const {
    739   return CUSTOM_HISTOGRAM;
    740 }
    741 
    742 // static
    743 vector<Sample> CustomHistogram::ArrayToCustomRanges(
    744     const Sample* values, size_t num_values) {
    745   vector<Sample> all_values;
    746   for (size_t i = 0; i < num_values; ++i) {
    747     Sample value = values[i];
    748     all_values.push_back(value);
    749 
    750     // Ensure that a guard bucket is added. If we end up with duplicate
    751     // values, FactoryGet will take care of removing them.
    752     all_values.push_back(value + 1);
    753   }
    754   return all_values;
    755 }
    756 
    757 CustomHistogram::CustomHistogram(const string& name,
    758                                  const BucketRanges* ranges)
    759     : Histogram(name,
    760                 ranges->range(1),
    761                 ranges->range(ranges->bucket_count() - 1),
    762                 ranges) {}
    763 
    764 bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
    765   if (!Histogram::SerializeInfoImpl(pickle))
    766     return false;
    767 
    768   // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't
    769   // write them.
    770   for (size_t i = 1; i < bucket_ranges()->bucket_count(); ++i) {
    771     if (!pickle->WriteInt(bucket_ranges()->range(i)))
    772       return false;
    773   }
    774   return true;
    775 }
    776 
    777 double CustomHistogram::GetBucketSize(Count current, size_t i) const {
    778   return 1;
    779 }
    780 
    781 // static
    782 HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) {
    783   string histogram_name;
    784   int flags;
    785   int declared_min;
    786   int declared_max;
    787   uint64 bucket_count;
    788   uint32 range_checksum;
    789 
    790   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
    791                               &declared_max, &bucket_count, &range_checksum)) {
    792     return NULL;
    793   }
    794 
    795   // First and last ranges are not serialized.
    796   vector<Sample> sample_ranges(bucket_count - 1);
    797 
    798   for (size_t i = 0; i < sample_ranges.size(); ++i) {
    799     if (!iter->ReadInt(&sample_ranges[i]))
    800       return NULL;
    801   }
    802 
    803   HistogramBase* histogram = CustomHistogram::FactoryGet(
    804       histogram_name, sample_ranges, flags);
    805   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
    806     // The serialized histogram might be corrupted.
    807     return NULL;
    808   }
    809   return histogram;
    810 }
    811 
    812 // static
    813 bool CustomHistogram::ValidateCustomRanges(
    814     const vector<Sample>& custom_ranges) {
    815   bool has_valid_range = false;
    816   for (size_t i = 0; i < custom_ranges.size(); i++) {
    817     Sample sample = custom_ranges[i];
    818     if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1)
    819       return false;
    820     if (sample != 0)
    821       has_valid_range = true;
    822   }
    823   return has_valid_range;
    824 }
    825 
    826 // static
    827 BucketRanges* CustomHistogram::CreateBucketRangesFromCustomRanges(
    828       const vector<Sample>& custom_ranges) {
    829   // Remove the duplicates in the custom ranges array.
    830   vector<int> ranges = custom_ranges;
    831   ranges.push_back(0);  // Ensure we have a zero value.
    832   ranges.push_back(HistogramBase::kSampleType_MAX);
    833   std::sort(ranges.begin(), ranges.end());
    834   ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
    835 
    836   BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
    837   for (size_t i = 0; i < ranges.size(); i++) {
    838     bucket_ranges->set_range(i, ranges[i]);
    839   }
    840   bucket_ranges->ResetChecksum();
    841   return bucket_ranges;
    842 }
    843 
    844 }  // namespace base
    845