ipc/chromium/src/base/histogram.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/base/histogram.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1286 @@
     1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +// Histogram is an object that aggregates statistics, and can summarize them in
     1.9 +// various forms, including ASCII graphical, HTML, and numerically (as a
    1.10 +// vector of numbers corresponding to each of the aggregating buckets).
    1.11 +// See header file for details and examples.
    1.12 +
    1.13 +#include "base/histogram.h"
    1.14 +
    1.15 +#include <math.h>
    1.16 +
    1.17 +#include <algorithm>
    1.18 +#include <string>
    1.19 +
    1.20 +#include "base/logging.h"
    1.21 +#include "base/pickle.h"
    1.22 +#include "base/string_util.h"
    1.23 +#include "base/logging.h"
    1.24 +
    1.25 +namespace base {
    1.26 +
    1.27 +#define DVLOG(x) CHROMIUM_LOG(ERROR)
    1.28 +#define CHECK_GT DCHECK_GT
    1.29 +#define CHECK_LT DCHECK_LT
    1.30 +typedef ::Lock Lock;
    1.31 +typedef ::AutoLock AutoLock;
    1.32 +
    1.33 +// Static table of checksums for all possible 8 bit bytes.
    1.34 +const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
    1.35 +0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
    1.36 +0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
    1.37 +0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL,
    1.38 +0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL,
    1.39 +0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L,
    1.40 +0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL,
    1.41 +0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
    1.42 +0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L,
    1.43 +0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL,
    1.44 +0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL,
    1.45 +0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L,
    1.46 +0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL,
    1.47 +0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L,
    1.48 +0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L,
    1.49 +0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL,
    1.50 +0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L,
    1.51 +0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
    1.52 +0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL,
    1.53 +0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L,
    1.54 +0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L,
    1.55 +0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L,
    1.56 +0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
    1.57 +0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL,
    1.58 +0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L,
    1.59 +0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L,
    1.60 +0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L,
    1.61 +0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
    1.62 +0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L,
    1.63 +0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL,
    1.64 +0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L,
    1.65 +0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L,
    1.66 +0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
    1.67 +0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L,
    1.68 +0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L,
    1.69 +0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL,
    1.70 +0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL,
    1.71 +0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
    1.72 +0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL,
    1.73 +0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L,
    1.74 +0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L,
    1.75 +0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL,
    1.76 +0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
    1.77 +0x2d02ef8dL,
    1.78 +};
    1.79 +
    1.80 +typedef Histogram::Count Count;
    1.81 +
    1.82 +// static
    1.83 +const size_t Histogram::kBucketCount_MAX = 16384u;
    1.84 +
    1.85 +Histogram* Histogram::FactoryGet(const std::string& name,
    1.86 +                                 Sample minimum,
    1.87 +                                 Sample maximum,
    1.88 +                                 size_t bucket_count,
    1.89 +                                 Flags flags) {
    1.90 +  Histogram* histogram(NULL);
    1.91 +
    1.92 +  // Defensive code.
    1.93 +  if (minimum < 1)
    1.94 +    minimum = 1;
    1.95 +  if (maximum > kSampleType_MAX - 1)
    1.96 +    maximum = kSampleType_MAX - 1;
    1.97 +
    1.98 +  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
    1.99 +    // Extra variable is not needed... but this keeps this section basically
   1.100 +    // identical to other derived classes in this file (and compiler will
   1.101 +    // optimize away the extra variable.
   1.102 +    Histogram* tentative_histogram =
   1.103 +        new Histogram(name, minimum, maximum, bucket_count);
   1.104 +    tentative_histogram->InitializeBucketRange();
   1.105 +    tentative_histogram->SetFlags(flags);
   1.106 +    histogram =
   1.107 +        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
   1.108 +  }
   1.109 +
   1.110 +  DCHECK_EQ(HISTOGRAM, histogram->histogram_type());
   1.111 +  DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
   1.112 +  return histogram;
   1.113 +}
   1.114 +
   1.115 +Histogram* Histogram::FactoryTimeGet(const std::string& name,
   1.116 +                                     TimeDelta minimum,
   1.117 +                                     TimeDelta maximum,
   1.118 +                                     size_t bucket_count,
   1.119 +                                     Flags flags) {
   1.120 +  return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
   1.121 +                    bucket_count, flags);
   1.122 +}
   1.123 +
   1.124 +void Histogram::Add(int value) {
   1.125 +  if (value > kSampleType_MAX - 1)
   1.126 +    value = kSampleType_MAX - 1;
   1.127 +  if (value < 0)
   1.128 +    value = 0;
   1.129 +  size_t index = BucketIndex(value);
   1.130 +  DCHECK_GE(value, ranges(index));
   1.131 +  DCHECK_LT(value, ranges(index + 1));
   1.132 +  Accumulate(value, 1, index);
   1.133 +}
   1.134 +
   1.135 +void Histogram::Subtract(int value) {
   1.136 +  if (value > kSampleType_MAX - 1)
   1.137 +    value = kSampleType_MAX - 1;
   1.138 +  if (value < 0)
   1.139 +    value = 0;
   1.140 +  size_t index = BucketIndex(value);
   1.141 +  DCHECK_GE(value, ranges(index));
   1.142 +  DCHECK_LT(value, ranges(index + 1));
   1.143 +  Accumulate(value, -1, index);
   1.144 +}
   1.145 +
   1.146 +void Histogram::AddBoolean(bool value) {
   1.147 +  DCHECK(false);
   1.148 +}
   1.149 +
   1.150 +void Histogram::AddSampleSet(const SampleSet& sample) {
   1.151 +  sample_.Add(sample);
   1.152 +}
   1.153 +
   1.154 +void Histogram::Clear() {
   1.155 +  SampleSet ss;
   1.156 +  ss.Resize(*this);
   1.157 +  sample_ = ss;
   1.158 +}
   1.159 +
   1.160 +void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
   1.161 +  DCHECK(false);
   1.162 +}
   1.163 +
   1.164 +// The following methods provide a graphical histogram display.
   1.165 +void Histogram::WriteHTMLGraph(std::string* output) const {
   1.166 +  // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
   1.167 +  output->append("<PRE>");
   1.168 +  WriteAscii(true, "<br>", output);
   1.169 +  output->append("</PRE>");
   1.170 +}
   1.171 +
   1.172 +void Histogram::WriteAscii(bool graph_it, const std::string& newline,
   1.173 +                           std::string* output) const {
   1.174 +  // Get local (stack) copies of all effectively volatile class data so that we
   1.175 +  // are consistent across our output activities.
   1.176 +  SampleSet snapshot;
   1.177 +  SnapshotSample(&snapshot);
   1.178 +  Count sample_count = snapshot.TotalCount();
   1.179 +
   1.180 +  WriteAsciiHeader(snapshot, sample_count, output);
   1.181 +  output->append(newline);
   1.182 +
   1.183 +  // Prepare to normalize graphical rendering of bucket contents.
   1.184 +  double max_size = 0;
   1.185 +  if (graph_it)
   1.186 +    max_size = GetPeakBucketSize(snapshot);
   1.187 +
   1.188 +  // Calculate space needed to print bucket range numbers.  Leave room to print
   1.189 +  // nearly the largest bucket range without sliding over the histogram.
   1.190 +  size_t largest_non_empty_bucket = bucket_count() - 1;
   1.191 +  while (0 == snapshot.counts(largest_non_empty_bucket)) {
   1.192 +    if (0 == largest_non_empty_bucket)
   1.193 +      break;  // All buckets are empty.
   1.194 +    --largest_non_empty_bucket;
   1.195 +  }
   1.196 +
   1.197 +  // Calculate largest print width needed for any of our bucket range displays.
   1.198 +  size_t print_width = 1;
   1.199 +  for (size_t i = 0; i < bucket_count(); ++i) {
   1.200 +    if (snapshot.counts(i)) {
   1.201 +      size_t width = GetAsciiBucketRange(i).size() + 1;
   1.202 +      if (width > print_width)
   1.203 +        print_width = width;
   1.204 +    }
   1.205 +  }
   1.206 +
   1.207 +  int64_t remaining = sample_count;
   1.208 +  int64_t past = 0;
   1.209 +  // Output the actual histogram graph.
   1.210 +  for (size_t i = 0; i < bucket_count(); ++i) {
   1.211 +    Count current = snapshot.counts(i);
   1.212 +    if (!current && !PrintEmptyBucket(i))
   1.213 +      continue;
   1.214 +    remaining -= current;
   1.215 +    std::string range = GetAsciiBucketRange(i);
   1.216 +    output->append(range);
   1.217 +    for (size_t j = 0; range.size() + j < print_width + 1; ++j)
   1.218 +      output->push_back(' ');
   1.219 +    if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
   1.220 +      while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
   1.221 +        ++i;
   1.222 +      output->append("... ");
   1.223 +      output->append(newline);
   1.224 +      continue;  // No reason to plot emptiness.
   1.225 +    }
   1.226 +    double current_size = GetBucketSize(current, i);
   1.227 +    if (graph_it)
   1.228 +      WriteAsciiBucketGraph(current_size, max_size, output);
   1.229 +    WriteAsciiBucketContext(past, current, remaining, i, output);
   1.230 +    output->append(newline);
   1.231 +    past += current;
   1.232 +  }
   1.233 +  DCHECK_EQ(sample_count, past);
   1.234 +}
   1.235 +
   1.236 +// static
   1.237 +std::string Histogram::SerializeHistogramInfo(const Histogram& histogram,
   1.238 +                                              const SampleSet& snapshot) {
   1.239 +  DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type());
   1.240 +
   1.241 +  Pickle pickle;
   1.242 +  pickle.WriteString(histogram.histogram_name());
   1.243 +  pickle.WriteInt(histogram.declared_min());
   1.244 +  pickle.WriteInt(histogram.declared_max());
   1.245 +  pickle.WriteSize(histogram.bucket_count());
   1.246 +  pickle.WriteUInt32(histogram.range_checksum());
   1.247 +  pickle.WriteInt(histogram.histogram_type());
   1.248 +  pickle.WriteInt(histogram.flags());
   1.249 +
   1.250 +  snapshot.Serialize(&pickle);
   1.251 +  return std::string(static_cast<const char*>(pickle.data()), pickle.size());
   1.252 +}
   1.253 +
   1.254 +// static
   1.255 +bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) {
   1.256 +  if (histogram_info.empty()) {
   1.257 +      return false;
   1.258 +  }
   1.259 +
   1.260 +  Pickle pickle(histogram_info.data(),
   1.261 +                static_cast<int>(histogram_info.size()));
   1.262 +  std::string histogram_name;
   1.263 +  int declared_min;
   1.264 +  int declared_max;
   1.265 +  size_t bucket_count;
   1.266 +  uint32_t range_checksum;
   1.267 +  int histogram_type;
   1.268 +  int pickle_flags;
   1.269 +  SampleSet sample;
   1.270 +
   1.271 +  void* iter = NULL;
   1.272 +  if (!pickle.ReadString(&iter, &histogram_name) ||
   1.273 +      !pickle.ReadInt(&iter, &declared_min) ||
   1.274 +      !pickle.ReadInt(&iter, &declared_max) ||
   1.275 +      !pickle.ReadSize(&iter, &bucket_count) ||
   1.276 +      !pickle.ReadUInt32(&iter, &range_checksum) ||
   1.277 +      !pickle.ReadInt(&iter, &histogram_type) ||
   1.278 +      !pickle.ReadInt(&iter, &pickle_flags) ||
   1.279 +      !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) {
   1.280 +    CHROMIUM_LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
   1.281 +    return false;
   1.282 +  }
   1.283 +  DCHECK(pickle_flags & kIPCSerializationSourceFlag);
   1.284 +  // Since these fields may have come from an untrusted renderer, do additional
   1.285 +  // checks above and beyond those in Histogram::Initialize()
   1.286 +  if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min ||
   1.287 +      INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) {
   1.288 +    CHROMIUM_LOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
   1.289 +    return false;
   1.290 +  }
   1.291 +
   1.292 +  Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag);
   1.293 +
   1.294 +  DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type);
   1.295 +
   1.296 +  Histogram* render_histogram(NULL);
   1.297 +
   1.298 +  if (histogram_type == HISTOGRAM) {
   1.299 +    render_histogram = Histogram::FactoryGet(
   1.300 +        histogram_name, declared_min, declared_max, bucket_count, flags);
   1.301 +  } else if (histogram_type == LINEAR_HISTOGRAM) {
   1.302 +    render_histogram = LinearHistogram::FactoryGet(
   1.303 +        histogram_name, declared_min, declared_max, bucket_count, flags);
   1.304 +  } else if (histogram_type == BOOLEAN_HISTOGRAM) {
   1.305 +    render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags);
   1.306 +  } else {
   1.307 +    CHROMIUM_LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: "
   1.308 +                        << histogram_type;
   1.309 +    return false;
   1.310 +  }
   1.311 +
   1.312 +  DCHECK_EQ(render_histogram->declared_min(), declared_min);
   1.313 +  DCHECK_EQ(render_histogram->declared_max(), declared_max);
   1.314 +  DCHECK_EQ(render_histogram->bucket_count(), bucket_count);
   1.315 +  DCHECK_EQ(render_histogram->range_checksum(), range_checksum);
   1.316 +  DCHECK_EQ(render_histogram->histogram_type(), histogram_type);
   1.317 +
   1.318 +  if (render_histogram->flags() & kIPCSerializationSourceFlag) {
   1.319 +    DVLOG(1) << "Single process mode, histogram observed and not copied: "
   1.320 +             << histogram_name;
   1.321 +  } else {
   1.322 +    DCHECK_EQ(flags & render_histogram->flags(), flags);
   1.323 +    render_histogram->AddSampleSet(sample);
   1.324 +  }
   1.325 +
   1.326 +  return true;
   1.327 +}
   1.328 +
   1.329 +//------------------------------------------------------------------------------
   1.330 +// Methods for the validating a sample and a related histogram.
   1.331 +//------------------------------------------------------------------------------
   1.332 +
   1.333 +Histogram::Inconsistencies Histogram::FindCorruption(
   1.334 +    const SampleSet& snapshot) const {
   1.335 +  int inconsistencies = NO_INCONSISTENCIES;
   1.336 +  Sample previous_range = -1;  // Bottom range is always 0.
   1.337 +  int64_t count = 0;
   1.338 +  for (size_t index = 0; index < bucket_count(); ++index) {
   1.339 +    count += snapshot.counts(index);
   1.340 +    int new_range = ranges(index);
   1.341 +    if (previous_range >= new_range)
   1.342 +      inconsistencies |= BUCKET_ORDER_ERROR;
   1.343 +    previous_range = new_range;
   1.344 +  }
   1.345 +
   1.346 +  if (!HasValidRangeChecksum())
   1.347 +    inconsistencies |= RANGE_CHECKSUM_ERROR;
   1.348 +
   1.349 +  int64_t delta64 = snapshot.redundant_count() - count;
   1.350 +  if (delta64 != 0) {
   1.351 +    int delta = static_cast<int>(delta64);
   1.352 +    if (delta != delta64)
   1.353 +      delta = INT_MAX;  // Flag all giant errors as INT_MAX.
   1.354 +    // Since snapshots of histograms are taken asynchronously relative to
   1.355 +    // sampling (and snapped from different threads), it is pretty likely that
   1.356 +    // we'll catch a redundant count that doesn't match the sample count.  We
   1.357 +    // allow for a certain amount of slop before flagging this as an
   1.358 +    // inconsistency.  Even with an inconsistency, we'll snapshot it again (for
   1.359 +    // UMA in about a half hour, so we'll eventually get the data, if it was
   1.360 +    // not the result of a corruption.  If histograms show that 1 is "too tight"
   1.361 +    // then we may try to use 2 or 3 for this slop value.
   1.362 +    const int kCommonRaceBasedCountMismatch = 1;
   1.363 +    if (delta > 0) {
   1.364 +      UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
   1.365 +      if (delta > kCommonRaceBasedCountMismatch)
   1.366 +        inconsistencies |= COUNT_HIGH_ERROR;
   1.367 +    } else {
   1.368 +      DCHECK_GT(0, delta);
   1.369 +      UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta);
   1.370 +      if (-delta > kCommonRaceBasedCountMismatch)
   1.371 +        inconsistencies |= COUNT_LOW_ERROR;
   1.372 +    }
   1.373 +  }
   1.374 +  return static_cast<Inconsistencies>(inconsistencies);
   1.375 +}
   1.376 +
   1.377 +Histogram::ClassType Histogram::histogram_type() const {
   1.378 +  return HISTOGRAM;
   1.379 +}
   1.380 +
   1.381 +Histogram::Sample Histogram::ranges(size_t i) const {
   1.382 +  return ranges_[i];
   1.383 +}
   1.384 +
   1.385 +size_t Histogram::bucket_count() const {
   1.386 +  return bucket_count_;
   1.387 +}
   1.388 +
   1.389 +// Do a safe atomic snapshot of sample data.
   1.390 +// This implementation assumes we are on a safe single thread.
   1.391 +void Histogram::SnapshotSample(SampleSet* sample) const {
   1.392 +  // Note locking not done in this version!!!
   1.393 +  *sample = sample_;
   1.394 +}
   1.395 +
   1.396 +bool Histogram::HasConstructorArguments(Sample minimum,
   1.397 +                                        Sample maximum,
   1.398 +                                        size_t bucket_count) {
   1.399 +  return ((minimum == declared_min_) && (maximum == declared_max_) &&
   1.400 +          (bucket_count == bucket_count_));
   1.401 +}
   1.402 +
   1.403 +bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum,
   1.404 +                                                 TimeDelta maximum,
   1.405 +                                                 size_t bucket_count) {
   1.406 +  return ((minimum.InMilliseconds() == declared_min_) &&
   1.407 +          (maximum.InMilliseconds() == declared_max_) &&
   1.408 +          (bucket_count == bucket_count_));
   1.409 +}
   1.410 +
   1.411 +bool Histogram::HasValidRangeChecksum() const {
   1.412 +  return CalculateRangeChecksum() == range_checksum_;
   1.413 +}
   1.414 +
   1.415 +size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   1.416 +{
   1.417 +  size_t n = 0;
   1.418 +  n += aMallocSizeOf(this);
   1.419 +  // We're not allowed to do deep dives into STL data structures.  This
   1.420 +  // is as close as we can get to measuring this array.
   1.421 +  n += aMallocSizeOf(&ranges_[0]);
   1.422 +  n += sample_.SizeOfExcludingThis(aMallocSizeOf);
   1.423 +  return n;
   1.424 +}
   1.425 +
   1.426 +size_t Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   1.427 +{
   1.428 +  // We're not allowed to do deep dives into STL data structures.  This
   1.429 +  // is as close as we can get to measuring this array.
   1.430 +  return aMallocSizeOf(&counts_[0]);
   1.431 +}
   1.432 +
   1.433 +Histogram::Histogram(const std::string& name, Sample minimum,
   1.434 +                     Sample maximum, size_t bucket_count)
   1.435 +  : sample_(),
   1.436 +    histogram_name_(name),
   1.437 +    declared_min_(minimum),
   1.438 +    declared_max_(maximum),
   1.439 +    bucket_count_(bucket_count),
   1.440 +    flags_(kNoFlags),
   1.441 +    ranges_(bucket_count + 1, 0),
   1.442 +    range_checksum_(0) {
   1.443 +  Initialize();
   1.444 +}
   1.445 +
   1.446 +Histogram::Histogram(const std::string& name, TimeDelta minimum,
   1.447 +                     TimeDelta maximum, size_t bucket_count)
   1.448 +  : sample_(),
   1.449 +    histogram_name_(name),
   1.450 +    declared_min_(static_cast<int> (minimum.InMilliseconds())),
   1.451 +    declared_max_(static_cast<int> (maximum.InMilliseconds())),
   1.452 +    bucket_count_(bucket_count),
   1.453 +    flags_(kNoFlags),
   1.454 +    ranges_(bucket_count + 1, 0),
   1.455 +    range_checksum_(0) {
   1.456 +  Initialize();
   1.457 +}
   1.458 +
   1.459 +Histogram::~Histogram() {
   1.460 +  if (StatisticsRecorder::dump_on_exit()) {
   1.461 +    std::string output;
   1.462 +    WriteAscii(true, "\n", &output);
   1.463 +    CHROMIUM_LOG(INFO) << output;
   1.464 +  }
   1.465 +
   1.466 +  // Just to make sure most derived class did this properly...
   1.467 +  DCHECK(ValidateBucketRanges());
   1.468 +}
   1.469 +
   1.470 +// Calculate what range of values are held in each bucket.
   1.471 +// We have to be careful that we don't pick a ratio between starting points in
   1.472 +// consecutive buckets that is sooo small, that the integer bounds are the same
   1.473 +// (effectively making one bucket get no values).  We need to avoid:
   1.474 +//   ranges_[i] == ranges_[i + 1]
   1.475 +// To avoid that, we just do a fine-grained bucket width as far as we need to
   1.476 +// until we get a ratio that moves us along at least 2 units at a time.  From
   1.477 +// that bucket onward we do use the exponential growth of buckets.
   1.478 +void Histogram::InitializeBucketRange() {
   1.479 +  double log_max = log(static_cast<double>(declared_max()));
   1.480 +  double log_ratio;
   1.481 +  double log_next;
   1.482 +  size_t bucket_index = 1;
   1.483 +  Sample current = declared_min();
   1.484 +  SetBucketRange(bucket_index, current);
   1.485 +  while (bucket_count() > ++bucket_index) {
   1.486 +    double log_current;
   1.487 +    log_current = log(static_cast<double>(current));
   1.488 +    // Calculate the count'th root of the range.
   1.489 +    log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
   1.490 +    // See where the next bucket would start.
   1.491 +    log_next = log_current + log_ratio;
   1.492 +    int next;
   1.493 +    next = static_cast<int>(floor(exp(log_next) + 0.5));
   1.494 +    if (next > current)
   1.495 +      current = next;
   1.496 +    else
   1.497 +      ++current;  // Just do a narrow bucket, and keep trying.
   1.498 +    SetBucketRange(bucket_index, current);
   1.499 +  }
   1.500 +  ResetRangeChecksum();
   1.501 +
   1.502 +  DCHECK_EQ(bucket_count(), bucket_index);
   1.503 +}
   1.504 +
   1.505 +bool Histogram::PrintEmptyBucket(size_t index) const {
   1.506 +  return true;
   1.507 +}
   1.508 +
   1.509 +size_t Histogram::BucketIndex(Sample value) const {
   1.510 +  // Use simple binary search.  This is very general, but there are better
   1.511 +  // approaches if we knew that the buckets were linearly distributed.
   1.512 +  DCHECK_LE(ranges(0), value);
   1.513 +  DCHECK_GT(ranges(bucket_count()), value);
   1.514 +  size_t under = 0;
   1.515 +  size_t over = bucket_count();
   1.516 +  size_t mid;
   1.517 +
   1.518 +  do {
   1.519 +    DCHECK_GE(over, under);
   1.520 +    mid = under + (over - under)/2;
   1.521 +    if (mid == under)
   1.522 +      break;
   1.523 +    if (ranges(mid) <= value)
   1.524 +      under = mid;
   1.525 +    else
   1.526 +      over = mid;
   1.527 +  } while (true);
   1.528 +
   1.529 +  DCHECK_LE(ranges(mid), value);
   1.530 +  CHECK_GT(ranges(mid+1), value);
   1.531 +  return mid;
   1.532 +}
   1.533 +
   1.534 +// Use the actual bucket widths (like a linear histogram) until the widths get
   1.535 +// over some transition value, and then use that transition width.  Exponentials
   1.536 +// get so big so fast (and we don't expect to see a lot of entries in the large
   1.537 +// buckets), so we need this to make it possible to see what is going on and
   1.538 +// not have 0-graphical-height buckets.
   1.539 +double Histogram::GetBucketSize(Count current, size_t i) const {
   1.540 +  DCHECK_GT(ranges(i + 1), ranges(i));
   1.541 +  static const double kTransitionWidth = 5;
   1.542 +  double denominator = ranges(i + 1) - ranges(i);
   1.543 +  if (denominator > kTransitionWidth)
   1.544 +    denominator = kTransitionWidth;  // Stop trying to normalize.
   1.545 +  return current/denominator;
   1.546 +}
   1.547 +
   1.548 +void Histogram::ResetRangeChecksum() {
   1.549 +  range_checksum_ = CalculateRangeChecksum();
   1.550 +}
   1.551 +
   1.552 +const std::string Histogram::GetAsciiBucketRange(size_t i) const {
   1.553 +  std::string result;
   1.554 +  if (kHexRangePrintingFlag & flags_)
   1.555 +    StringAppendF(&result, "%#x", ranges(i));
   1.556 +  else
   1.557 +    StringAppendF(&result, "%d", ranges(i));
   1.558 +  return result;
   1.559 +}
   1.560 +
   1.561 +// Update histogram data with new sample.
   1.562 +void Histogram::Accumulate(Sample value, Count count, size_t index) {
   1.563 +  // Note locking not done in this version!!!
   1.564 +  sample_.AccumulateWithExponentialStats(value, count, index,
   1.565 +					 flags_ & kExtendedStatisticsFlag);
   1.566 +}
   1.567 +
   1.568 +void Histogram::SetBucketRange(size_t i, Sample value) {
   1.569 +  DCHECK_GT(bucket_count_, i);
   1.570 +  ranges_[i] = value;
   1.571 +}
   1.572 +
   1.573 +bool Histogram::ValidateBucketRanges() const {
   1.574 +  // Standard assertions that all bucket ranges should satisfy.
   1.575 +  DCHECK_EQ(bucket_count_ + 1, ranges_.size());
   1.576 +  DCHECK_EQ(0, ranges_[0]);
   1.577 +  DCHECK_EQ(declared_min(), ranges_[1]);
   1.578 +  DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]);
   1.579 +  DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]);
   1.580 +  return true;
   1.581 +}
   1.582 +
   1.583 +uint32_t Histogram::CalculateRangeChecksum() const {
   1.584 +  DCHECK_EQ(ranges_.size(), bucket_count() + 1);
   1.585 +  uint32_t checksum = static_cast<uint32_t>(ranges_.size());  // Seed checksum.
   1.586 +  for (size_t index = 0; index < bucket_count(); ++index)
   1.587 +    checksum = Crc32(checksum, ranges(index));
   1.588 +  return checksum;
   1.589 +}
   1.590 +
   1.591 +void Histogram::Initialize() {
   1.592 +  sample_.Resize(*this);
   1.593 +  if (declared_min_ < 1)
   1.594 +    declared_min_ = 1;
   1.595 +  if (declared_max_ > kSampleType_MAX - 1)
   1.596 +    declared_max_ = kSampleType_MAX - 1;
   1.597 +  DCHECK_LE(declared_min_, declared_max_);
   1.598 +  DCHECK_GT(bucket_count_, 1u);
   1.599 +  CHECK_LT(bucket_count_, kBucketCount_MAX);
   1.600 +  size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
   1.601 +  DCHECK_LE(bucket_count_, maximal_bucket_count);
   1.602 +  DCHECK_EQ(0, ranges_[0]);
   1.603 +  ranges_[bucket_count_] = kSampleType_MAX;
   1.604 +}
   1.605 +
   1.606 +// We generate the CRC-32 using the low order bits to select whether to XOR in
   1.607 +// the reversed polynomial 0xedb88320L.  This is nice and simple, and allows us
   1.608 +// to keep the quotient in a uint32_t.  Since we're not concerned about the nature
   1.609 +// of corruptions (i.e., we don't care about bit sequencing, since we are
   1.610 +// handling memory changes, which are more grotesque) so we don't bother to
   1.611 +// get the CRC correct for big-endian vs little-ending calculations.  All we
   1.612 +// need is a nice hash, that tends to depend on all the bits of the sample, with
   1.613 +// very little chance of changes in one place impacting changes in another
   1.614 +// place.
   1.615 +uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
   1.616 +  const bool kUseRealCrc = true;  // TODO(jar): Switch to false and watch stats.
   1.617 +  if (kUseRealCrc) {
   1.618 +    union {
   1.619 +      Histogram::Sample range;
   1.620 +      unsigned char bytes[sizeof(Histogram::Sample)];
   1.621 +    } converter;
   1.622 +    converter.range = range;
   1.623 +    for (size_t i = 0; i < sizeof(converter); ++i)
   1.624 +      sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
   1.625 +  } else {
   1.626 +    // Use hash techniques provided in ReallyFastHash, except we don't care
   1.627 +    // about "avalanching" (which would worsten the hash, and add collisions),
   1.628 +    // and we don't care about edge cases since we have an even number of bytes.
   1.629 +    union {
   1.630 +      Histogram::Sample range;
   1.631 +      uint16_t ints[sizeof(Histogram::Sample) / 2];
   1.632 +    } converter;
   1.633 +    DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter));
   1.634 +    converter.range = range;
   1.635 +    sum += converter.ints[0];
   1.636 +    sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11);
   1.637 +    sum += sum >> 11;
   1.638 +  }
   1.639 +  return sum;
   1.640 +}
   1.641 +
   1.642 +//------------------------------------------------------------------------------
   1.643 +// Private methods
   1.644 +
   1.645 +double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
   1.646 +  double max = 0;
   1.647 +  for (size_t i = 0; i < bucket_count() ; ++i) {
   1.648 +    double current_size = GetBucketSize(snapshot.counts(i), i);
   1.649 +    if (current_size > max)
   1.650 +      max = current_size;
   1.651 +  }
   1.652 +  return max;
   1.653 +}
   1.654 +
   1.655 +void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
   1.656 +                                 Count sample_count,
   1.657 +                                 std::string* output) const {
   1.658 +  StringAppendF(output,
   1.659 +                "Histogram: %s recorded %d samples",
   1.660 +                histogram_name().c_str(),
   1.661 +                sample_count);
   1.662 +  if (0 == sample_count) {
   1.663 +    DCHECK_EQ(snapshot.sum(), 0);
   1.664 +  } else {
   1.665 +    double average = static_cast<float>(snapshot.sum()) / sample_count;
   1.666 +
   1.667 +    StringAppendF(output, ", average = %.1f", average);
   1.668 +  }
   1.669 +  if (flags_ & ~kHexRangePrintingFlag)
   1.670 +    StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
   1.671 +}
   1.672 +
   1.673 +void Histogram::WriteAsciiBucketContext(const int64_t past,
   1.674 +                                        const Count current,
   1.675 +                                        const int64_t remaining,
   1.676 +                                        const size_t i,
   1.677 +                                        std::string* output) const {
   1.678 +  double scaled_sum = (past + current + remaining) / 100.0;
   1.679 +  WriteAsciiBucketValue(current, scaled_sum, output);
   1.680 +  if (0 < i) {
   1.681 +    double percentage = past / scaled_sum;
   1.682 +    StringAppendF(output, " {%3.1f%%}", percentage);
   1.683 +  }
   1.684 +}
   1.685 +
   1.686 +void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
   1.687 +                                      std::string* output) const {
   1.688 +  StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
   1.689 +}
   1.690 +
   1.691 +void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
   1.692 +                                      std::string* output) const {
   1.693 +  const int k_line_length = 72;  // Maximal horizontal width of graph.
   1.694 +  int x_count = static_cast<int>(k_line_length * (current_size / max_size)
   1.695 +                                 + 0.5);
   1.696 +  int x_remainder = k_line_length - x_count;
   1.697 +
   1.698 +  while (0 < x_count--)
   1.699 +    output->append("-");
   1.700 +  output->append("O");
   1.701 +  while (0 < x_remainder--)
   1.702 +    output->append(" ");
   1.703 +}
   1.704 +
   1.705 +//------------------------------------------------------------------------------
   1.706 +// Methods for the Histogram::SampleSet class
   1.707 +//------------------------------------------------------------------------------
   1.708 +
   1.709 +Histogram::SampleSet::SampleSet()
   1.710 +    : counts_(),
   1.711 +      sum_(0),
   1.712 +      sum_squares_(0),
   1.713 +      log_sum_(0),
   1.714 +      log_sum_squares_(0),
   1.715 +      redundant_count_(0) {
   1.716 +}
   1.717 +
   1.718 +Histogram::SampleSet::~SampleSet() {
   1.719 +}
   1.720 +
   1.721 +void Histogram::SampleSet::Resize(const Histogram& histogram) {
   1.722 +  counts_.resize(histogram.bucket_count(), 0);
   1.723 +}
   1.724 +
   1.725 +void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
   1.726 +  DCHECK_EQ(histogram.bucket_count(), counts_.size());
   1.727 +}
   1.728 +
   1.729 +void Histogram::SampleSet::Accumulate(Sample value, Count count,
   1.730 +				      size_t index) {
   1.731 +  DCHECK(count == 1 || count == -1);
   1.732 +  counts_[index] += count;
   1.733 +  redundant_count_ += count;
   1.734 +  sum_ += static_cast<int64_t>(count) * value;
   1.735 +  DCHECK_GE(counts_[index], 0);
   1.736 +  DCHECK_GE(sum_, 0);
   1.737 +  DCHECK_GE(redundant_count_, 0);
   1.738 +}
   1.739 +
   1.740 +void Histogram::SampleSet::AccumulateWithLinearStats(Sample value,
   1.741 +                                                     Count count,
   1.742 +                                                     size_t index) {
   1.743 +  Accumulate(value, count, index);
   1.744 +  sum_squares_ += static_cast<int64_t>(count) * value * value;
   1.745 +}
   1.746 +
   1.747 +void Histogram::SampleSet::AccumulateWithExponentialStats(Sample value,
   1.748 +                                                          Count count,
   1.749 +                                                          size_t index,
   1.750 +							  bool computeExtendedStatistics) {
   1.751 +  Accumulate(value, count, index);
   1.752 +  if (computeExtendedStatistics) {
   1.753 +    DCHECK_GE(value, 0);
   1.754 +    float value_log = logf(static_cast<float>(value) + 1.0f);
   1.755 +    log_sum_ += count * value_log;
   1.756 +    log_sum_squares_ += count * value_log * value_log;
   1.757 +  }
   1.758 +}
   1.759 +
   1.760 +Count Histogram::SampleSet::TotalCount() const {
   1.761 +  Count total = 0;
   1.762 +  for (Counts::const_iterator it = counts_.begin();
   1.763 +       it != counts_.end();
   1.764 +       ++it) {
   1.765 +    total += *it;
   1.766 +  }
   1.767 +  return total;
   1.768 +}
   1.769 +
   1.770 +void Histogram::SampleSet::Add(const SampleSet& other) {
   1.771 +  DCHECK_EQ(counts_.size(), other.counts_.size());
   1.772 +  sum_ += other.sum_;
   1.773 +  sum_squares_ += other.sum_squares_;
   1.774 +  log_sum_ += other.log_sum_;
   1.775 +  log_sum_squares_ += other.log_sum_squares_;
   1.776 +  redundant_count_ += other.redundant_count_;
   1.777 +  for (size_t index = 0; index < counts_.size(); ++index)
   1.778 +    counts_[index] += other.counts_[index];
   1.779 +}
   1.780 +
   1.781 +void Histogram::SampleSet::Subtract(const SampleSet& other) {
   1.782 +  DCHECK_EQ(counts_.size(), other.counts_.size());
   1.783 +  // Note: Race conditions in snapshotting a sum may lead to (temporary)
   1.784 +  // negative values when snapshots are later combined (and deltas calculated).
   1.785 +  // As a result, we don't currently CHCEK() for positive values.
   1.786 +  sum_ -= other.sum_;
   1.787 +  sum_squares_ -= other.sum_squares_;
   1.788 +  log_sum_ -= other.log_sum_;
   1.789 +  log_sum_squares_ -= other.log_sum_squares_;
   1.790 +  redundant_count_ -= other.redundant_count_;
   1.791 +  for (size_t index = 0; index < counts_.size(); ++index) {
   1.792 +    counts_[index] -= other.counts_[index];
   1.793 +    DCHECK_GE(counts_[index], 0);
   1.794 +  }
   1.795 +}
   1.796 +
   1.797 +bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
   1.798 +  pickle->WriteInt64(sum_);
   1.799 +  pickle->WriteInt64(redundant_count_);
   1.800 +  pickle->WriteSize(counts_.size());
   1.801 +
   1.802 +  for (size_t index = 0; index < counts_.size(); ++index) {
   1.803 +    pickle->WriteInt(counts_[index]);
   1.804 +  }
   1.805 +
   1.806 +  return true;
   1.807 +}
   1.808 +
   1.809 +bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) {
   1.810 +  DCHECK_EQ(counts_.size(), 0u);
   1.811 +  DCHECK_EQ(sum_, 0);
   1.812 +  DCHECK_EQ(redundant_count_, 0);
   1.813 +
   1.814 +  size_t counts_size;
   1.815 +
   1.816 +  if (!pickle.ReadInt64(iter, &sum_) ||
   1.817 +      !pickle.ReadInt64(iter, &redundant_count_) ||
   1.818 +      !pickle.ReadSize(iter, &counts_size)) {
   1.819 +    return false;
   1.820 +  }
   1.821 +
   1.822 +  if (counts_size == 0)
   1.823 +    return false;
   1.824 +
   1.825 +  int count = 0;
   1.826 +  for (size_t index = 0; index < counts_size; ++index) {
   1.827 +    int i;
   1.828 +    if (!pickle.ReadInt(iter, &i))
   1.829 +      return false;
   1.830 +    counts_.push_back(i);
   1.831 +    count += i;
   1.832 +  }
   1.833 +
   1.834 +  return true;
   1.835 +}
   1.836 +
   1.837 +//------------------------------------------------------------------------------
   1.838 +// LinearHistogram: This histogram uses a traditional set of evenly spaced
   1.839 +// buckets.
   1.840 +//------------------------------------------------------------------------------
   1.841 +
   1.842 +LinearHistogram::~LinearHistogram() {
   1.843 +}
   1.844 +
   1.845 +Histogram* LinearHistogram::FactoryGet(const std::string& name,
   1.846 +                                       Sample minimum,
   1.847 +                                       Sample maximum,
   1.848 +                                       size_t bucket_count,
   1.849 +                                       Flags flags) {
   1.850 +  Histogram* histogram(NULL);
   1.851 +
   1.852 +  if (minimum < 1)
   1.853 +    minimum = 1;
   1.854 +  if (maximum > kSampleType_MAX - 1)
   1.855 +    maximum = kSampleType_MAX - 1;
   1.856 +
   1.857 +  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
   1.858 +    LinearHistogram* tentative_histogram =
   1.859 +        new LinearHistogram(name, minimum, maximum, bucket_count);
   1.860 +    tentative_histogram->InitializeBucketRange();
   1.861 +    tentative_histogram->SetFlags(flags);
   1.862 +    histogram =
   1.863 +        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
   1.864 +  }
   1.865 +
   1.866 +  DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type());
   1.867 +  DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
   1.868 +  return histogram;
   1.869 +}
   1.870 +
   1.871 +Histogram* LinearHistogram::FactoryTimeGet(const std::string& name,
   1.872 +                                           TimeDelta minimum,
   1.873 +                                           TimeDelta maximum,
   1.874 +                                           size_t bucket_count,
   1.875 +                                           Flags flags) {
   1.876 +  return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
   1.877 +                    bucket_count, flags);
   1.878 +}
   1.879 +
   1.880 +Histogram::ClassType LinearHistogram::histogram_type() const {
   1.881 +  return LINEAR_HISTOGRAM;
   1.882 +}
   1.883 +
   1.884 +void LinearHistogram::Accumulate(Sample value, Count count, size_t index) {
   1.885 +  sample_.AccumulateWithLinearStats(value, count, index);
   1.886 +}
   1.887 +
   1.888 +void LinearHistogram::SetRangeDescriptions(
   1.889 +    const DescriptionPair descriptions[]) {
   1.890 +  for (int i =0; descriptions[i].description; ++i) {
   1.891 +    bucket_description_[descriptions[i].sample] = descriptions[i].description;
   1.892 +  }
   1.893 +}
   1.894 +
   1.895 +LinearHistogram::LinearHistogram(const std::string& name,
   1.896 +                                 Sample minimum,
   1.897 +                                 Sample maximum,
   1.898 +                                 size_t bucket_count)
   1.899 +    : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
   1.900 +}
   1.901 +
   1.902 +LinearHistogram::LinearHistogram(const std::string& name,
   1.903 +                                 TimeDelta minimum,
   1.904 +                                 TimeDelta maximum,
   1.905 +                                 size_t bucket_count)
   1.906 +    : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
   1.907 +                                 minimum : TimeDelta::FromMilliseconds(1),
   1.908 +                maximum, bucket_count) {
   1.909 +}
   1.910 +
   1.911 +void LinearHistogram::InitializeBucketRange() {
   1.912 +  DCHECK_GT(declared_min(), 0);  // 0 is the underflow bucket here.
   1.913 +  double min = declared_min();
   1.914 +  double max = declared_max();
   1.915 +  size_t i;
   1.916 +  for (i = 1; i < bucket_count(); ++i) {
   1.917 +    double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
   1.918 +                          (bucket_count() - 2);
   1.919 +    SetBucketRange(i, static_cast<int> (linear_range + 0.5));
   1.920 +  }
   1.921 +  ResetRangeChecksum();
   1.922 +}
   1.923 +
   1.924 +double LinearHistogram::GetBucketSize(Count current, size_t i) const {
   1.925 +  DCHECK_GT(ranges(i + 1), ranges(i));
   1.926 +  // Adjacent buckets with different widths would have "surprisingly" many (few)
   1.927 +  // samples in a histogram if we didn't normalize this way.
   1.928 +  double denominator = ranges(i + 1) - ranges(i);
   1.929 +  return current/denominator;
   1.930 +}
   1.931 +
   1.932 +const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
   1.933 +  int range = ranges(i);
   1.934 +  BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
   1.935 +  if (it == bucket_description_.end())
   1.936 +    return Histogram::GetAsciiBucketRange(i);
   1.937 +  return it->second;
   1.938 +}
   1.939 +
   1.940 +bool LinearHistogram::PrintEmptyBucket(size_t index) const {
   1.941 +  return bucket_description_.find(ranges(index)) == bucket_description_.end();
   1.942 +}
   1.943 +
   1.944 +
   1.945 +//------------------------------------------------------------------------------
   1.946 +// This section provides implementation for BooleanHistogram.
   1.947 +//------------------------------------------------------------------------------
   1.948 +
   1.949 +Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) {
   1.950 +  Histogram* histogram(NULL);
   1.951 +
   1.952 +  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
   1.953 +    BooleanHistogram* tentative_histogram = new BooleanHistogram(name);
   1.954 +    tentative_histogram->InitializeBucketRange();
   1.955 +    tentative_histogram->SetFlags(flags);
   1.956 +    histogram =
   1.957 +        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
   1.958 +  }
   1.959 +
   1.960 +  DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type());
   1.961 +  return histogram;
   1.962 +}
   1.963 +
   1.964 +Histogram::ClassType BooleanHistogram::histogram_type() const {
   1.965 +  return BOOLEAN_HISTOGRAM;
   1.966 +}
   1.967 +
   1.968 +void BooleanHistogram::AddBoolean(bool value) {
   1.969 +  Add(value ? 1 : 0);
   1.970 +}
   1.971 +
   1.972 +BooleanHistogram::BooleanHistogram(const std::string& name)
   1.973 +    : LinearHistogram(name, 1, 2, 3) {
   1.974 +}
   1.975 +
   1.976 +void
   1.977 +BooleanHistogram::Accumulate(Sample value, Count count, size_t index)
   1.978 +{
   1.979 +  // Callers will have computed index based on the non-booleanified value.
   1.980 +  // So we need to adjust the index manually.
   1.981 +  LinearHistogram::Accumulate(!!value, count, value ? 1 : 0);
   1.982 +}
   1.983 +
   1.984 +//------------------------------------------------------------------------------
   1.985 +// FlagHistogram:
   1.986 +//------------------------------------------------------------------------------
   1.987 +
   1.988 +Histogram *
   1.989 +FlagHistogram::FactoryGet(const std::string &name, Flags flags)
   1.990 +{
   1.991 +  Histogram *h(nullptr);
   1.992 +
   1.993 +  if (!StatisticsRecorder::FindHistogram(name, &h)) {
   1.994 +    FlagHistogram *fh = new FlagHistogram(name);
   1.995 +    fh->InitializeBucketRange();
   1.996 +    fh->SetFlags(flags);
   1.997 +    size_t zero_index = fh->BucketIndex(0);
   1.998 +    fh->LinearHistogram::Accumulate(0, 1, zero_index);
   1.999 +    h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh);
  1.1000 +  }
  1.1001 +
  1.1002 +  return h;
  1.1003 +}
  1.1004 +
  1.1005 +FlagHistogram::FlagHistogram(const std::string &name)
  1.1006 +  : BooleanHistogram(name), mSwitched(false) {
  1.1007 +}
  1.1008 +
  1.1009 +Histogram::ClassType
  1.1010 +FlagHistogram::histogram_type() const
  1.1011 +{
  1.1012 +  return FLAG_HISTOGRAM;
  1.1013 +}
  1.1014 +
  1.1015 +void
  1.1016 +FlagHistogram::Accumulate(Sample value, Count count, size_t index)
  1.1017 +{
  1.1018 +  if (mSwitched) {
  1.1019 +    return;
  1.1020 +  }
  1.1021 +
  1.1022 +  mSwitched = true;
  1.1023 +  DCHECK_EQ(value, 1);
  1.1024 +  LinearHistogram::Accumulate(value, 1, index);
  1.1025 +  size_t zero_index = BucketIndex(0);
  1.1026 +  LinearHistogram::Accumulate(0, -1, zero_index);
  1.1027 +}
  1.1028 +
  1.1029 +void
  1.1030 +FlagHistogram::AddSampleSet(const SampleSet& sample) {
  1.1031 +  DCHECK_EQ(bucket_count(), sample.size());
  1.1032 +  // We can't be sure the SampleSet provided came from another FlagHistogram,
  1.1033 +  // so we take the following steps:
  1.1034 +  //  - If our flag has already been set do nothing.
  1.1035 +  //  - Set our flag if the following hold:
  1.1036 +  //      - The sum of the counts in the provided SampleSet is 1.
  1.1037 +  //      - The bucket index for that single value is the same as the index where we
  1.1038 +  //        would place our set flag.
  1.1039 +  //  - Otherwise, take no action.
  1.1040 +
  1.1041 +  if (mSwitched) {
  1.1042 +    return;
  1.1043 +  }
  1.1044 +
  1.1045 +  if (sample.sum() != 1) {
  1.1046 +    return;
  1.1047 +  }
  1.1048 +
  1.1049 +  size_t one_index = BucketIndex(1);
  1.1050 +  if (sample.counts(one_index) == 1) {
  1.1051 +    Accumulate(1, 1, one_index);
  1.1052 +  }
  1.1053 +}
  1.1054 +//------------------------------------------------------------------------------
  1.1055 +// CustomHistogram:
  1.1056 +//------------------------------------------------------------------------------
  1.1057 +
  1.1058 +Histogram* CustomHistogram::FactoryGet(const std::string& name,
  1.1059 +                                       const std::vector<Sample>& custom_ranges,
  1.1060 +                                       Flags flags) {
  1.1061 +  Histogram* histogram(NULL);
  1.1062 +
  1.1063 +  // Remove the duplicates in the custom ranges array.
  1.1064 +  std::vector<int> ranges = custom_ranges;
  1.1065 +  ranges.push_back(0);  // Ensure we have a zero value.
  1.1066 +  std::sort(ranges.begin(), ranges.end());
  1.1067 +  ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
  1.1068 +  if (ranges.size() <= 1) {
  1.1069 +    DCHECK(false);
  1.1070 +    // Note that we pushed a 0 in above, so for defensive code....
  1.1071 +    ranges.push_back(1);  // Put in some data so we can index to [1].
  1.1072 +  }
  1.1073 +
  1.1074 +  DCHECK_LT(ranges.back(), kSampleType_MAX);
  1.1075 +
  1.1076 +  if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
  1.1077 +    CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges);
  1.1078 +    tentative_histogram->InitializedCustomBucketRange(ranges);
  1.1079 +    tentative_histogram->SetFlags(flags);
  1.1080 +    histogram =
  1.1081 +        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
  1.1082 +  }
  1.1083 +
  1.1084 +  DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM);
  1.1085 +  DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(),
  1.1086 +                                            ranges.size()));
  1.1087 +  return histogram;
  1.1088 +}
  1.1089 +
  1.1090 +Histogram::ClassType CustomHistogram::histogram_type() const {
  1.1091 +  return CUSTOM_HISTOGRAM;
  1.1092 +}
  1.1093 +
  1.1094 +CustomHistogram::CustomHistogram(const std::string& name,
  1.1095 +                                 const std::vector<Sample>& custom_ranges)
  1.1096 +    : Histogram(name, custom_ranges[1], custom_ranges.back(),
  1.1097 +                custom_ranges.size()) {
  1.1098 +  DCHECK_GT(custom_ranges.size(), 1u);
  1.1099 +  DCHECK_EQ(custom_ranges[0], 0);
  1.1100 +}
  1.1101 +
  1.1102 +void CustomHistogram::InitializedCustomBucketRange(
  1.1103 +    const std::vector<Sample>& custom_ranges) {
  1.1104 +  DCHECK_GT(custom_ranges.size(), 1u);
  1.1105 +  DCHECK_EQ(custom_ranges[0], 0);
  1.1106 +  DCHECK_LE(custom_ranges.size(), bucket_count());
  1.1107 +  for (size_t index = 0; index < custom_ranges.size(); ++index)
  1.1108 +    SetBucketRange(index, custom_ranges[index]);
  1.1109 +  ResetRangeChecksum();
  1.1110 +}
  1.1111 +
  1.1112 +double CustomHistogram::GetBucketSize(Count current, size_t i) const {
  1.1113 +  return 1;
  1.1114 +}
  1.1115 +
  1.1116 +//------------------------------------------------------------------------------
  1.1117 +// The next section handles global (central) support for all histograms, as well
  1.1118 +// as startup/teardown of this service.
  1.1119 +//------------------------------------------------------------------------------
  1.1120 +
  1.1121 +// This singleton instance should be started during the single threaded portion
  1.1122 +// of main(), and hence it is not thread safe.  It initializes globals to
  1.1123 +// provide support for all future calls.
  1.1124 +StatisticsRecorder::StatisticsRecorder() {
  1.1125 +  DCHECK(!histograms_);
  1.1126 +  if (lock_ == NULL) {
  1.1127 +    // This will leak on purpose. It's the only way to make sure we won't race
  1.1128 +    // against the static uninitialization of the module while one of our
  1.1129 +    // static methods relying on the lock get called at an inappropriate time
  1.1130 +    // during the termination phase. Since it's a static data member, we will
  1.1131 +    // leak one per process, which would be similar to the instance allocated
  1.1132 +    // during static initialization and released only on  process termination.
  1.1133 +    lock_ = new base::Lock;
  1.1134 +  }
  1.1135 +  base::AutoLock auto_lock(*lock_);
  1.1136 +  histograms_ = new HistogramMap;
  1.1137 +}
  1.1138 +
  1.1139 +StatisticsRecorder::~StatisticsRecorder() {
  1.1140 +  DCHECK(histograms_ && lock_);
  1.1141 +
  1.1142 +  if (dump_on_exit_) {
  1.1143 +    std::string output;
  1.1144 +    WriteGraph("", &output);
  1.1145 +    CHROMIUM_LOG(INFO) << output;
  1.1146 +  }
  1.1147 +  // Clean up.
  1.1148 +  HistogramMap* histograms = NULL;
  1.1149 +  {
  1.1150 +    base::AutoLock auto_lock(*lock_);
  1.1151 +    histograms = histograms_;
  1.1152 +    histograms_ = NULL;
  1.1153 +    for (HistogramMap::iterator it = histograms->begin();
  1.1154 +         histograms->end() != it;
  1.1155 +         ++it) {
  1.1156 +      // No other clients permanently hold Histogram references, so we
  1.1157 +      // have the only one and it is safe to delete it.
  1.1158 +      delete it->second;
  1.1159 +    }
  1.1160 +  }
  1.1161 +  delete histograms;
  1.1162 +  // We don't delete lock_ on purpose to avoid having to properly protect
  1.1163 +  // against it going away after we checked for NULL in the static methods.
  1.1164 +}
  1.1165 +
  1.1166 +// static
  1.1167 +bool StatisticsRecorder::IsActive() {
  1.1168 +  if (lock_ == NULL)
  1.1169 +    return false;
  1.1170 +  base::AutoLock auto_lock(*lock_);
  1.1171 +  return NULL != histograms_;
  1.1172 +}
  1.1173 +
  1.1174 +Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) {
  1.1175 +  DCHECK(histogram->HasValidRangeChecksum());
  1.1176 +  if (lock_ == NULL)
  1.1177 +    return histogram;
  1.1178 +  base::AutoLock auto_lock(*lock_);
  1.1179 +  if (!histograms_)
  1.1180 +    return histogram;
  1.1181 +  const std::string name = histogram->histogram_name();
  1.1182 +  HistogramMap::iterator it = histograms_->find(name);
  1.1183 +  // Avoid overwriting a previous registration.
  1.1184 +  if (histograms_->end() == it) {
  1.1185 +    (*histograms_)[name] = histogram;
  1.1186 +  } else {
  1.1187 +    delete histogram;  // We already have one by this name.
  1.1188 +    histogram = it->second;
  1.1189 +  }
  1.1190 +  return histogram;
  1.1191 +}
  1.1192 +
  1.1193 +// static
  1.1194 +void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
  1.1195 +                                        std::string* output) {
  1.1196 +  if (!IsActive())
  1.1197 +    return;
  1.1198 +  output->append("<html><head><title>About Histograms");
  1.1199 +  if (!query.empty())
  1.1200 +    output->append(" - " + query);
  1.1201 +  output->append("</title>"
  1.1202 +                 // We'd like the following no-cache... but it doesn't work.
  1.1203 +                 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
  1.1204 +                 "</head><body>");
  1.1205 +
  1.1206 +  Histograms snapshot;
  1.1207 +  GetSnapshot(query, &snapshot);
  1.1208 +  for (Histograms::iterator it = snapshot.begin();
  1.1209 +       it != snapshot.end();
  1.1210 +       ++it) {
  1.1211 +    (*it)->WriteHTMLGraph(output);
  1.1212 +    output->append("<br><hr><br>");
  1.1213 +  }
  1.1214 +  output->append("</body></html>");
  1.1215 +}
  1.1216 +
  1.1217 +// static
  1.1218 +void StatisticsRecorder::WriteGraph(const std::string& query,
  1.1219 +                                    std::string* output) {
  1.1220 +  if (!IsActive())
  1.1221 +    return;
  1.1222 +  if (query.length())
  1.1223 +    StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
  1.1224 +  else
  1.1225 +    output->append("Collections of all histograms\n");
  1.1226 +
  1.1227 +  Histograms snapshot;
  1.1228 +  GetSnapshot(query, &snapshot);
  1.1229 +  for (Histograms::iterator it = snapshot.begin();
  1.1230 +       it != snapshot.end();
  1.1231 +       ++it) {
  1.1232 +    (*it)->WriteAscii(true, "\n", output);
  1.1233 +    output->append("\n");
  1.1234 +  }
  1.1235 +}
  1.1236 +
  1.1237 +// static
  1.1238 +void StatisticsRecorder::GetHistograms(Histograms* output) {
  1.1239 +  if (lock_ == NULL)
  1.1240 +    return;
  1.1241 +  base::AutoLock auto_lock(*lock_);
  1.1242 +  if (!histograms_)
  1.1243 +    return;
  1.1244 +  for (HistogramMap::iterator it = histograms_->begin();
  1.1245 +       histograms_->end() != it;
  1.1246 +       ++it) {
  1.1247 +    DCHECK_EQ(it->first, it->second->histogram_name());
  1.1248 +    output->push_back(it->second);
  1.1249 +  }
  1.1250 +}
  1.1251 +
  1.1252 +bool StatisticsRecorder::FindHistogram(const std::string& name,
  1.1253 +                                       Histogram** histogram) {
  1.1254 +  if (lock_ == NULL)
  1.1255 +    return false;
  1.1256 +  base::AutoLock auto_lock(*lock_);
  1.1257 +  if (!histograms_)
  1.1258 +    return false;
  1.1259 +  HistogramMap::iterator it = histograms_->find(name);
  1.1260 +  if (histograms_->end() == it)
  1.1261 +    return false;
  1.1262 +  *histogram = it->second;
  1.1263 +  return true;
  1.1264 +}
  1.1265 +
  1.1266 +// private static
  1.1267 +void StatisticsRecorder::GetSnapshot(const std::string& query,
  1.1268 +                                     Histograms* snapshot) {
  1.1269 +  if (lock_ == NULL)
  1.1270 +    return;
  1.1271 +  base::AutoLock auto_lock(*lock_);
  1.1272 +  if (!histograms_)
  1.1273 +    return;
  1.1274 +  for (HistogramMap::iterator it = histograms_->begin();
  1.1275 +       histograms_->end() != it;
  1.1276 +       ++it) {
  1.1277 +    if (it->first.find(query) != std::string::npos)
  1.1278 +      snapshot->push_back(it->second);
  1.1279 +  }
  1.1280 +}
  1.1281 +
  1.1282 +// static
  1.1283 +StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
  1.1284 +// static
  1.1285 +base::Lock* StatisticsRecorder::lock_ = NULL;
  1.1286 +// static
  1.1287 +bool StatisticsRecorder::dump_on_exit_ = false;
  1.1288 +
  1.1289 +}  // namespace base

mercurial