ipc/chromium/src/base/histogram.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 // Histogram is an object that aggregates statistics, and can summarize them in
michael@0 6 // various forms, including ASCII graphical, HTML, and numerically (as a
michael@0 7 // vector of numbers corresponding to each of the aggregating buckets).
michael@0 8 // See header file for details and examples.
michael@0 9
michael@0 10 #include "base/histogram.h"
michael@0 11
michael@0 12 #include <math.h>
michael@0 13
michael@0 14 #include <algorithm>
michael@0 15 #include <string>
michael@0 16
michael@0 17 #include "base/logging.h"
michael@0 18 #include "base/pickle.h"
michael@0 19 #include "base/string_util.h"
michael@0 20 #include "base/logging.h"
michael@0 21
michael@0 22 namespace base {
michael@0 23
michael@0 24 #define DVLOG(x) CHROMIUM_LOG(ERROR)
michael@0 25 #define CHECK_GT DCHECK_GT
michael@0 26 #define CHECK_LT DCHECK_LT
michael@0 27 typedef ::Lock Lock;
michael@0 28 typedef ::AutoLock AutoLock;
michael@0 29
michael@0 30 // Static table of checksums for all possible 8 bit bytes.
michael@0 31 const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
michael@0 32 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
michael@0 33 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
michael@0 34 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL,
michael@0 35 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL,
michael@0 36 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L,
michael@0 37 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL,
michael@0 38 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
michael@0 39 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L,
michael@0 40 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL,
michael@0 41 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL,
michael@0 42 0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L,
michael@0 43 0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL,
michael@0 44 0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L,
michael@0 45 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L,
michael@0 46 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL,
michael@0 47 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L,
michael@0 48 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
michael@0 49 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL,
michael@0 50 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L,
michael@0 51 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L,
michael@0 52 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L,
michael@0 53 0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
michael@0 54 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL,
michael@0 55 0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L,
michael@0 56 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L,
michael@0 57 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L,
michael@0 58 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
michael@0 59 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L,
michael@0 60 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL,
michael@0 61 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L,
michael@0 62 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L,
michael@0 63 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
michael@0 64 0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L,
michael@0 65 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L,
michael@0 66 0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL,
michael@0 67 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL,
michael@0 68 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
michael@0 69 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL,
michael@0 70 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L,
michael@0 71 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L,
michael@0 72 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL,
michael@0 73 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
michael@0 74 0x2d02ef8dL,
michael@0 75 };
michael@0 76
michael@0 77 typedef Histogram::Count Count;
michael@0 78
michael@0 79 // static
michael@0 80 const size_t Histogram::kBucketCount_MAX = 16384u;
michael@0 81
michael@0 82 Histogram* Histogram::FactoryGet(const std::string& name,
michael@0 83 Sample minimum,
michael@0 84 Sample maximum,
michael@0 85 size_t bucket_count,
michael@0 86 Flags flags) {
michael@0 87 Histogram* histogram(NULL);
michael@0 88
michael@0 89 // Defensive code.
michael@0 90 if (minimum < 1)
michael@0 91 minimum = 1;
michael@0 92 if (maximum > kSampleType_MAX - 1)
michael@0 93 maximum = kSampleType_MAX - 1;
michael@0 94
michael@0 95 if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
michael@0 96 // Extra variable is not needed... but this keeps this section basically
michael@0 97 // identical to other derived classes in this file (and compiler will
michael@0 98 // optimize away the extra variable.
michael@0 99 Histogram* tentative_histogram =
michael@0 100 new Histogram(name, minimum, maximum, bucket_count);
michael@0 101 tentative_histogram->InitializeBucketRange();
michael@0 102 tentative_histogram->SetFlags(flags);
michael@0 103 histogram =
michael@0 104 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
michael@0 105 }
michael@0 106
michael@0 107 DCHECK_EQ(HISTOGRAM, histogram->histogram_type());
michael@0 108 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
michael@0 109 return histogram;
michael@0 110 }
michael@0 111
michael@0 112 Histogram* Histogram::FactoryTimeGet(const std::string& name,
michael@0 113 TimeDelta minimum,
michael@0 114 TimeDelta maximum,
michael@0 115 size_t bucket_count,
michael@0 116 Flags flags) {
michael@0 117 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
michael@0 118 bucket_count, flags);
michael@0 119 }
michael@0 120
michael@0 121 void Histogram::Add(int value) {
michael@0 122 if (value > kSampleType_MAX - 1)
michael@0 123 value = kSampleType_MAX - 1;
michael@0 124 if (value < 0)
michael@0 125 value = 0;
michael@0 126 size_t index = BucketIndex(value);
michael@0 127 DCHECK_GE(value, ranges(index));
michael@0 128 DCHECK_LT(value, ranges(index + 1));
michael@0 129 Accumulate(value, 1, index);
michael@0 130 }
michael@0 131
michael@0 132 void Histogram::Subtract(int value) {
michael@0 133 if (value > kSampleType_MAX - 1)
michael@0 134 value = kSampleType_MAX - 1;
michael@0 135 if (value < 0)
michael@0 136 value = 0;
michael@0 137 size_t index = BucketIndex(value);
michael@0 138 DCHECK_GE(value, ranges(index));
michael@0 139 DCHECK_LT(value, ranges(index + 1));
michael@0 140 Accumulate(value, -1, index);
michael@0 141 }
michael@0 142
michael@0 143 void Histogram::AddBoolean(bool value) {
michael@0 144 DCHECK(false);
michael@0 145 }
michael@0 146
michael@0 147 void Histogram::AddSampleSet(const SampleSet& sample) {
michael@0 148 sample_.Add(sample);
michael@0 149 }
michael@0 150
michael@0 151 void Histogram::Clear() {
michael@0 152 SampleSet ss;
michael@0 153 ss.Resize(*this);
michael@0 154 sample_ = ss;
michael@0 155 }
michael@0 156
michael@0 157 void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
michael@0 158 DCHECK(false);
michael@0 159 }
michael@0 160
michael@0 161 // The following methods provide a graphical histogram display.
michael@0 162 void Histogram::WriteHTMLGraph(std::string* output) const {
michael@0 163 // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
michael@0 164 output->append("<PRE>");
michael@0 165 WriteAscii(true, "<br>", output);
michael@0 166 output->append("</PRE>");
michael@0 167 }
michael@0 168
michael@0 169 void Histogram::WriteAscii(bool graph_it, const std::string& newline,
michael@0 170 std::string* output) const {
michael@0 171 // Get local (stack) copies of all effectively volatile class data so that we
michael@0 172 // are consistent across our output activities.
michael@0 173 SampleSet snapshot;
michael@0 174 SnapshotSample(&snapshot);
michael@0 175 Count sample_count = snapshot.TotalCount();
michael@0 176
michael@0 177 WriteAsciiHeader(snapshot, sample_count, output);
michael@0 178 output->append(newline);
michael@0 179
michael@0 180 // Prepare to normalize graphical rendering of bucket contents.
michael@0 181 double max_size = 0;
michael@0 182 if (graph_it)
michael@0 183 max_size = GetPeakBucketSize(snapshot);
michael@0 184
michael@0 185 // Calculate space needed to print bucket range numbers. Leave room to print
michael@0 186 // nearly the largest bucket range without sliding over the histogram.
michael@0 187 size_t largest_non_empty_bucket = bucket_count() - 1;
michael@0 188 while (0 == snapshot.counts(largest_non_empty_bucket)) {
michael@0 189 if (0 == largest_non_empty_bucket)
michael@0 190 break; // All buckets are empty.
michael@0 191 --largest_non_empty_bucket;
michael@0 192 }
michael@0 193
michael@0 194 // Calculate largest print width needed for any of our bucket range displays.
michael@0 195 size_t print_width = 1;
michael@0 196 for (size_t i = 0; i < bucket_count(); ++i) {
michael@0 197 if (snapshot.counts(i)) {
michael@0 198 size_t width = GetAsciiBucketRange(i).size() + 1;
michael@0 199 if (width > print_width)
michael@0 200 print_width = width;
michael@0 201 }
michael@0 202 }
michael@0 203
michael@0 204 int64_t remaining = sample_count;
michael@0 205 int64_t past = 0;
michael@0 206 // Output the actual histogram graph.
michael@0 207 for (size_t i = 0; i < bucket_count(); ++i) {
michael@0 208 Count current = snapshot.counts(i);
michael@0 209 if (!current && !PrintEmptyBucket(i))
michael@0 210 continue;
michael@0 211 remaining -= current;
michael@0 212 std::string range = GetAsciiBucketRange(i);
michael@0 213 output->append(range);
michael@0 214 for (size_t j = 0; range.size() + j < print_width + 1; ++j)
michael@0 215 output->push_back(' ');
michael@0 216 if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
michael@0 217 while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
michael@0 218 ++i;
michael@0 219 output->append("... ");
michael@0 220 output->append(newline);
michael@0 221 continue; // No reason to plot emptiness.
michael@0 222 }
michael@0 223 double current_size = GetBucketSize(current, i);
michael@0 224 if (graph_it)
michael@0 225 WriteAsciiBucketGraph(current_size, max_size, output);
michael@0 226 WriteAsciiBucketContext(past, current, remaining, i, output);
michael@0 227 output->append(newline);
michael@0 228 past += current;
michael@0 229 }
michael@0 230 DCHECK_EQ(sample_count, past);
michael@0 231 }
michael@0 232
michael@0 233 // static
michael@0 234 std::string Histogram::SerializeHistogramInfo(const Histogram& histogram,
michael@0 235 const SampleSet& snapshot) {
michael@0 236 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type());
michael@0 237
michael@0 238 Pickle pickle;
michael@0 239 pickle.WriteString(histogram.histogram_name());
michael@0 240 pickle.WriteInt(histogram.declared_min());
michael@0 241 pickle.WriteInt(histogram.declared_max());
michael@0 242 pickle.WriteSize(histogram.bucket_count());
michael@0 243 pickle.WriteUInt32(histogram.range_checksum());
michael@0 244 pickle.WriteInt(histogram.histogram_type());
michael@0 245 pickle.WriteInt(histogram.flags());
michael@0 246
michael@0 247 snapshot.Serialize(&pickle);
michael@0 248 return std::string(static_cast<const char*>(pickle.data()), pickle.size());
michael@0 249 }
michael@0 250
michael@0 251 // static
michael@0 252 bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) {
michael@0 253 if (histogram_info.empty()) {
michael@0 254 return false;
michael@0 255 }
michael@0 256
michael@0 257 Pickle pickle(histogram_info.data(),
michael@0 258 static_cast<int>(histogram_info.size()));
michael@0 259 std::string histogram_name;
michael@0 260 int declared_min;
michael@0 261 int declared_max;
michael@0 262 size_t bucket_count;
michael@0 263 uint32_t range_checksum;
michael@0 264 int histogram_type;
michael@0 265 int pickle_flags;
michael@0 266 SampleSet sample;
michael@0 267
michael@0 268 void* iter = NULL;
michael@0 269 if (!pickle.ReadString(&iter, &histogram_name) ||
michael@0 270 !pickle.ReadInt(&iter, &declared_min) ||
michael@0 271 !pickle.ReadInt(&iter, &declared_max) ||
michael@0 272 !pickle.ReadSize(&iter, &bucket_count) ||
michael@0 273 !pickle.ReadUInt32(&iter, &range_checksum) ||
michael@0 274 !pickle.ReadInt(&iter, &histogram_type) ||
michael@0 275 !pickle.ReadInt(&iter, &pickle_flags) ||
michael@0 276 !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) {
michael@0 277 CHROMIUM_LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
michael@0 278 return false;
michael@0 279 }
michael@0 280 DCHECK(pickle_flags & kIPCSerializationSourceFlag);
michael@0 281 // Since these fields may have come from an untrusted renderer, do additional
michael@0 282 // checks above and beyond those in Histogram::Initialize()
michael@0 283 if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min ||
michael@0 284 INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) {
michael@0 285 CHROMIUM_LOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
michael@0 286 return false;
michael@0 287 }
michael@0 288
michael@0 289 Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag);
michael@0 290
michael@0 291 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type);
michael@0 292
michael@0 293 Histogram* render_histogram(NULL);
michael@0 294
michael@0 295 if (histogram_type == HISTOGRAM) {
michael@0 296 render_histogram = Histogram::FactoryGet(
michael@0 297 histogram_name, declared_min, declared_max, bucket_count, flags);
michael@0 298 } else if (histogram_type == LINEAR_HISTOGRAM) {
michael@0 299 render_histogram = LinearHistogram::FactoryGet(
michael@0 300 histogram_name, declared_min, declared_max, bucket_count, flags);
michael@0 301 } else if (histogram_type == BOOLEAN_HISTOGRAM) {
michael@0 302 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags);
michael@0 303 } else {
michael@0 304 CHROMIUM_LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: "
michael@0 305 << histogram_type;
michael@0 306 return false;
michael@0 307 }
michael@0 308
michael@0 309 DCHECK_EQ(render_histogram->declared_min(), declared_min);
michael@0 310 DCHECK_EQ(render_histogram->declared_max(), declared_max);
michael@0 311 DCHECK_EQ(render_histogram->bucket_count(), bucket_count);
michael@0 312 DCHECK_EQ(render_histogram->range_checksum(), range_checksum);
michael@0 313 DCHECK_EQ(render_histogram->histogram_type(), histogram_type);
michael@0 314
michael@0 315 if (render_histogram->flags() & kIPCSerializationSourceFlag) {
michael@0 316 DVLOG(1) << "Single process mode, histogram observed and not copied: "
michael@0 317 << histogram_name;
michael@0 318 } else {
michael@0 319 DCHECK_EQ(flags & render_histogram->flags(), flags);
michael@0 320 render_histogram->AddSampleSet(sample);
michael@0 321 }
michael@0 322
michael@0 323 return true;
michael@0 324 }
michael@0 325
michael@0 326 //------------------------------------------------------------------------------
michael@0 327 // Methods for the validating a sample and a related histogram.
michael@0 328 //------------------------------------------------------------------------------
michael@0 329
michael@0 330 Histogram::Inconsistencies Histogram::FindCorruption(
michael@0 331 const SampleSet& snapshot) const {
michael@0 332 int inconsistencies = NO_INCONSISTENCIES;
michael@0 333 Sample previous_range = -1; // Bottom range is always 0.
michael@0 334 int64_t count = 0;
michael@0 335 for (size_t index = 0; index < bucket_count(); ++index) {
michael@0 336 count += snapshot.counts(index);
michael@0 337 int new_range = ranges(index);
michael@0 338 if (previous_range >= new_range)
michael@0 339 inconsistencies |= BUCKET_ORDER_ERROR;
michael@0 340 previous_range = new_range;
michael@0 341 }
michael@0 342
michael@0 343 if (!HasValidRangeChecksum())
michael@0 344 inconsistencies |= RANGE_CHECKSUM_ERROR;
michael@0 345
michael@0 346 int64_t delta64 = snapshot.redundant_count() - count;
michael@0 347 if (delta64 != 0) {
michael@0 348 int delta = static_cast<int>(delta64);
michael@0 349 if (delta != delta64)
michael@0 350 delta = INT_MAX; // Flag all giant errors as INT_MAX.
michael@0 351 // Since snapshots of histograms are taken asynchronously relative to
michael@0 352 // sampling (and snapped from different threads), it is pretty likely that
michael@0 353 // we'll catch a redundant count that doesn't match the sample count. We
michael@0 354 // allow for a certain amount of slop before flagging this as an
michael@0 355 // inconsistency. Even with an inconsistency, we'll snapshot it again (for
michael@0 356 // UMA in about a half hour, so we'll eventually get the data, if it was
michael@0 357 // not the result of a corruption. If histograms show that 1 is "too tight"
michael@0 358 // then we may try to use 2 or 3 for this slop value.
michael@0 359 const int kCommonRaceBasedCountMismatch = 1;
michael@0 360 if (delta > 0) {
michael@0 361 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
michael@0 362 if (delta > kCommonRaceBasedCountMismatch)
michael@0 363 inconsistencies |= COUNT_HIGH_ERROR;
michael@0 364 } else {
michael@0 365 DCHECK_GT(0, delta);
michael@0 366 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta);
michael@0 367 if (-delta > kCommonRaceBasedCountMismatch)
michael@0 368 inconsistencies |= COUNT_LOW_ERROR;
michael@0 369 }
michael@0 370 }
michael@0 371 return static_cast<Inconsistencies>(inconsistencies);
michael@0 372 }
michael@0 373
michael@0 374 Histogram::ClassType Histogram::histogram_type() const {
michael@0 375 return HISTOGRAM;
michael@0 376 }
michael@0 377
michael@0 378 Histogram::Sample Histogram::ranges(size_t i) const {
michael@0 379 return ranges_[i];
michael@0 380 }
michael@0 381
michael@0 382 size_t Histogram::bucket_count() const {
michael@0 383 return bucket_count_;
michael@0 384 }
michael@0 385
michael@0 386 // Do a safe atomic snapshot of sample data.
michael@0 387 // This implementation assumes we are on a safe single thread.
michael@0 388 void Histogram::SnapshotSample(SampleSet* sample) const {
michael@0 389 // Note locking not done in this version!!!
michael@0 390 *sample = sample_;
michael@0 391 }
michael@0 392
michael@0 393 bool Histogram::HasConstructorArguments(Sample minimum,
michael@0 394 Sample maximum,
michael@0 395 size_t bucket_count) {
michael@0 396 return ((minimum == declared_min_) && (maximum == declared_max_) &&
michael@0 397 (bucket_count == bucket_count_));
michael@0 398 }
michael@0 399
michael@0 400 bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum,
michael@0 401 TimeDelta maximum,
michael@0 402 size_t bucket_count) {
michael@0 403 return ((minimum.InMilliseconds() == declared_min_) &&
michael@0 404 (maximum.InMilliseconds() == declared_max_) &&
michael@0 405 (bucket_count == bucket_count_));
michael@0 406 }
michael@0 407
michael@0 408 bool Histogram::HasValidRangeChecksum() const {
michael@0 409 return CalculateRangeChecksum() == range_checksum_;
michael@0 410 }
michael@0 411
michael@0 412 size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
michael@0 413 {
michael@0 414 size_t n = 0;
michael@0 415 n += aMallocSizeOf(this);
michael@0 416 // We're not allowed to do deep dives into STL data structures. This
michael@0 417 // is as close as we can get to measuring this array.
michael@0 418 n += aMallocSizeOf(&ranges_[0]);
michael@0 419 n += sample_.SizeOfExcludingThis(aMallocSizeOf);
michael@0 420 return n;
michael@0 421 }
michael@0 422
michael@0 423 size_t Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
michael@0 424 {
michael@0 425 // We're not allowed to do deep dives into STL data structures. This
michael@0 426 // is as close as we can get to measuring this array.
michael@0 427 return aMallocSizeOf(&counts_[0]);
michael@0 428 }
michael@0 429
michael@0 430 Histogram::Histogram(const std::string& name, Sample minimum,
michael@0 431 Sample maximum, size_t bucket_count)
michael@0 432 : sample_(),
michael@0 433 histogram_name_(name),
michael@0 434 declared_min_(minimum),
michael@0 435 declared_max_(maximum),
michael@0 436 bucket_count_(bucket_count),
michael@0 437 flags_(kNoFlags),
michael@0 438 ranges_(bucket_count + 1, 0),
michael@0 439 range_checksum_(0) {
michael@0 440 Initialize();
michael@0 441 }
michael@0 442
michael@0 443 Histogram::Histogram(const std::string& name, TimeDelta minimum,
michael@0 444 TimeDelta maximum, size_t bucket_count)
michael@0 445 : sample_(),
michael@0 446 histogram_name_(name),
michael@0 447 declared_min_(static_cast<int> (minimum.InMilliseconds())),
michael@0 448 declared_max_(static_cast<int> (maximum.InMilliseconds())),
michael@0 449 bucket_count_(bucket_count),
michael@0 450 flags_(kNoFlags),
michael@0 451 ranges_(bucket_count + 1, 0),
michael@0 452 range_checksum_(0) {
michael@0 453 Initialize();
michael@0 454 }
michael@0 455
michael@0 456 Histogram::~Histogram() {
michael@0 457 if (StatisticsRecorder::dump_on_exit()) {
michael@0 458 std::string output;
michael@0 459 WriteAscii(true, "\n", &output);
michael@0 460 CHROMIUM_LOG(INFO) << output;
michael@0 461 }
michael@0 462
michael@0 463 // Just to make sure most derived class did this properly...
michael@0 464 DCHECK(ValidateBucketRanges());
michael@0 465 }
michael@0 466
michael@0 467 // Calculate what range of values are held in each bucket.
michael@0 468 // We have to be careful that we don't pick a ratio between starting points in
michael@0 469 // consecutive buckets that is sooo small, that the integer bounds are the same
michael@0 470 // (effectively making one bucket get no values). We need to avoid:
michael@0 471 // ranges_[i] == ranges_[i + 1]
michael@0 472 // To avoid that, we just do a fine-grained bucket width as far as we need to
michael@0 473 // until we get a ratio that moves us along at least 2 units at a time. From
michael@0 474 // that bucket onward we do use the exponential growth of buckets.
michael@0 475 void Histogram::InitializeBucketRange() {
michael@0 476 double log_max = log(static_cast<double>(declared_max()));
michael@0 477 double log_ratio;
michael@0 478 double log_next;
michael@0 479 size_t bucket_index = 1;
michael@0 480 Sample current = declared_min();
michael@0 481 SetBucketRange(bucket_index, current);
michael@0 482 while (bucket_count() > ++bucket_index) {
michael@0 483 double log_current;
michael@0 484 log_current = log(static_cast<double>(current));
michael@0 485 // Calculate the count'th root of the range.
michael@0 486 log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
michael@0 487 // See where the next bucket would start.
michael@0 488 log_next = log_current + log_ratio;
michael@0 489 int next;
michael@0 490 next = static_cast<int>(floor(exp(log_next) + 0.5));
michael@0 491 if (next > current)
michael@0 492 current = next;
michael@0 493 else
michael@0 494 ++current; // Just do a narrow bucket, and keep trying.
michael@0 495 SetBucketRange(bucket_index, current);
michael@0 496 }
michael@0 497 ResetRangeChecksum();
michael@0 498
michael@0 499 DCHECK_EQ(bucket_count(), bucket_index);
michael@0 500 }
michael@0 501
michael@0 502 bool Histogram::PrintEmptyBucket(size_t index) const {
michael@0 503 return true;
michael@0 504 }
michael@0 505
michael@0 506 size_t Histogram::BucketIndex(Sample value) const {
michael@0 507 // Use simple binary search. This is very general, but there are better
michael@0 508 // approaches if we knew that the buckets were linearly distributed.
michael@0 509 DCHECK_LE(ranges(0), value);
michael@0 510 DCHECK_GT(ranges(bucket_count()), value);
michael@0 511 size_t under = 0;
michael@0 512 size_t over = bucket_count();
michael@0 513 size_t mid;
michael@0 514
michael@0 515 do {
michael@0 516 DCHECK_GE(over, under);
michael@0 517 mid = under + (over - under)/2;
michael@0 518 if (mid == under)
michael@0 519 break;
michael@0 520 if (ranges(mid) <= value)
michael@0 521 under = mid;
michael@0 522 else
michael@0 523 over = mid;
michael@0 524 } while (true);
michael@0 525
michael@0 526 DCHECK_LE(ranges(mid), value);
michael@0 527 CHECK_GT(ranges(mid+1), value);
michael@0 528 return mid;
michael@0 529 }
michael@0 530
michael@0 531 // Use the actual bucket widths (like a linear histogram) until the widths get
michael@0 532 // over some transition value, and then use that transition width. Exponentials
michael@0 533 // get so big so fast (and we don't expect to see a lot of entries in the large
michael@0 534 // buckets), so we need this to make it possible to see what is going on and
michael@0 535 // not have 0-graphical-height buckets.
michael@0 536 double Histogram::GetBucketSize(Count current, size_t i) const {
michael@0 537 DCHECK_GT(ranges(i + 1), ranges(i));
michael@0 538 static const double kTransitionWidth = 5;
michael@0 539 double denominator = ranges(i + 1) - ranges(i);
michael@0 540 if (denominator > kTransitionWidth)
michael@0 541 denominator = kTransitionWidth; // Stop trying to normalize.
michael@0 542 return current/denominator;
michael@0 543 }
michael@0 544
michael@0 545 void Histogram::ResetRangeChecksum() {
michael@0 546 range_checksum_ = CalculateRangeChecksum();
michael@0 547 }
michael@0 548
michael@0 549 const std::string Histogram::GetAsciiBucketRange(size_t i) const {
michael@0 550 std::string result;
michael@0 551 if (kHexRangePrintingFlag & flags_)
michael@0 552 StringAppendF(&result, "%#x", ranges(i));
michael@0 553 else
michael@0 554 StringAppendF(&result, "%d", ranges(i));
michael@0 555 return result;
michael@0 556 }
michael@0 557
michael@0 558 // Update histogram data with new sample.
michael@0 559 void Histogram::Accumulate(Sample value, Count count, size_t index) {
michael@0 560 // Note locking not done in this version!!!
michael@0 561 sample_.AccumulateWithExponentialStats(value, count, index,
michael@0 562 flags_ & kExtendedStatisticsFlag);
michael@0 563 }
michael@0 564
michael@0 565 void Histogram::SetBucketRange(size_t i, Sample value) {
michael@0 566 DCHECK_GT(bucket_count_, i);
michael@0 567 ranges_[i] = value;
michael@0 568 }
michael@0 569
michael@0 570 bool Histogram::ValidateBucketRanges() const {
michael@0 571 // Standard assertions that all bucket ranges should satisfy.
michael@0 572 DCHECK_EQ(bucket_count_ + 1, ranges_.size());
michael@0 573 DCHECK_EQ(0, ranges_[0]);
michael@0 574 DCHECK_EQ(declared_min(), ranges_[1]);
michael@0 575 DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]);
michael@0 576 DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]);
michael@0 577 return true;
michael@0 578 }
michael@0 579
michael@0 580 uint32_t Histogram::CalculateRangeChecksum() const {
michael@0 581 DCHECK_EQ(ranges_.size(), bucket_count() + 1);
michael@0 582 uint32_t checksum = static_cast<uint32_t>(ranges_.size()); // Seed checksum.
michael@0 583 for (size_t index = 0; index < bucket_count(); ++index)
michael@0 584 checksum = Crc32(checksum, ranges(index));
michael@0 585 return checksum;
michael@0 586 }
michael@0 587
michael@0 588 void Histogram::Initialize() {
michael@0 589 sample_.Resize(*this);
michael@0 590 if (declared_min_ < 1)
michael@0 591 declared_min_ = 1;
michael@0 592 if (declared_max_ > kSampleType_MAX - 1)
michael@0 593 declared_max_ = kSampleType_MAX - 1;
michael@0 594 DCHECK_LE(declared_min_, declared_max_);
michael@0 595 DCHECK_GT(bucket_count_, 1u);
michael@0 596 CHECK_LT(bucket_count_, kBucketCount_MAX);
michael@0 597 size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
michael@0 598 DCHECK_LE(bucket_count_, maximal_bucket_count);
michael@0 599 DCHECK_EQ(0, ranges_[0]);
michael@0 600 ranges_[bucket_count_] = kSampleType_MAX;
michael@0 601 }
michael@0 602
michael@0 603 // We generate the CRC-32 using the low order bits to select whether to XOR in
michael@0 604 // the reversed polynomial 0xedb88320L. This is nice and simple, and allows us
michael@0 605 // to keep the quotient in a uint32_t. Since we're not concerned about the nature
michael@0 606 // of corruptions (i.e., we don't care about bit sequencing, since we are
michael@0 607 // handling memory changes, which are more grotesque) so we don't bother to
michael@0 608 // get the CRC correct for big-endian vs little-ending calculations. All we
michael@0 609 // need is a nice hash, that tends to depend on all the bits of the sample, with
michael@0 610 // very little chance of changes in one place impacting changes in another
michael@0 611 // place.
michael@0 612 uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
michael@0 613 const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats.
michael@0 614 if (kUseRealCrc) {
michael@0 615 union {
michael@0 616 Histogram::Sample range;
michael@0 617 unsigned char bytes[sizeof(Histogram::Sample)];
michael@0 618 } converter;
michael@0 619 converter.range = range;
michael@0 620 for (size_t i = 0; i < sizeof(converter); ++i)
michael@0 621 sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
michael@0 622 } else {
michael@0 623 // Use hash techniques provided in ReallyFastHash, except we don't care
michael@0 624 // about "avalanching" (which would worsten the hash, and add collisions),
michael@0 625 // and we don't care about edge cases since we have an even number of bytes.
michael@0 626 union {
michael@0 627 Histogram::Sample range;
michael@0 628 uint16_t ints[sizeof(Histogram::Sample) / 2];
michael@0 629 } converter;
michael@0 630 DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter));
michael@0 631 converter.range = range;
michael@0 632 sum += converter.ints[0];
michael@0 633 sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11);
michael@0 634 sum += sum >> 11;
michael@0 635 }
michael@0 636 return sum;
michael@0 637 }
michael@0 638
michael@0 639 //------------------------------------------------------------------------------
michael@0 640 // Private methods
michael@0 641
michael@0 642 double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
michael@0 643 double max = 0;
michael@0 644 for (size_t i = 0; i < bucket_count() ; ++i) {
michael@0 645 double current_size = GetBucketSize(snapshot.counts(i), i);
michael@0 646 if (current_size > max)
michael@0 647 max = current_size;
michael@0 648 }
michael@0 649 return max;
michael@0 650 }
michael@0 651
michael@0 652 void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
michael@0 653 Count sample_count,
michael@0 654 std::string* output) const {
michael@0 655 StringAppendF(output,
michael@0 656 "Histogram: %s recorded %d samples",
michael@0 657 histogram_name().c_str(),
michael@0 658 sample_count);
michael@0 659 if (0 == sample_count) {
michael@0 660 DCHECK_EQ(snapshot.sum(), 0);
michael@0 661 } else {
michael@0 662 double average = static_cast<float>(snapshot.sum()) / sample_count;
michael@0 663
michael@0 664 StringAppendF(output, ", average = %.1f", average);
michael@0 665 }
michael@0 666 if (flags_ & ~kHexRangePrintingFlag)
michael@0 667 StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
michael@0 668 }
michael@0 669
michael@0 670 void Histogram::WriteAsciiBucketContext(const int64_t past,
michael@0 671 const Count current,
michael@0 672 const int64_t remaining,
michael@0 673 const size_t i,
michael@0 674 std::string* output) const {
michael@0 675 double scaled_sum = (past + current + remaining) / 100.0;
michael@0 676 WriteAsciiBucketValue(current, scaled_sum, output);
michael@0 677 if (0 < i) {
michael@0 678 double percentage = past / scaled_sum;
michael@0 679 StringAppendF(output, " {%3.1f%%}", percentage);
michael@0 680 }
michael@0 681 }
michael@0 682
michael@0 683 void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
michael@0 684 std::string* output) const {
michael@0 685 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
michael@0 686 }
michael@0 687
michael@0 688 void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
michael@0 689 std::string* output) const {
michael@0 690 const int k_line_length = 72; // Maximal horizontal width of graph.
michael@0 691 int x_count = static_cast<int>(k_line_length * (current_size / max_size)
michael@0 692 + 0.5);
michael@0 693 int x_remainder = k_line_length - x_count;
michael@0 694
michael@0 695 while (0 < x_count--)
michael@0 696 output->append("-");
michael@0 697 output->append("O");
michael@0 698 while (0 < x_remainder--)
michael@0 699 output->append(" ");
michael@0 700 }
michael@0 701
michael@0 702 //------------------------------------------------------------------------------
michael@0 703 // Methods for the Histogram::SampleSet class
michael@0 704 //------------------------------------------------------------------------------
michael@0 705
michael@0 706 Histogram::SampleSet::SampleSet()
michael@0 707 : counts_(),
michael@0 708 sum_(0),
michael@0 709 sum_squares_(0),
michael@0 710 log_sum_(0),
michael@0 711 log_sum_squares_(0),
michael@0 712 redundant_count_(0) {
michael@0 713 }
michael@0 714
michael@0 715 Histogram::SampleSet::~SampleSet() {
michael@0 716 }
michael@0 717
michael@0 718 void Histogram::SampleSet::Resize(const Histogram& histogram) {
michael@0 719 counts_.resize(histogram.bucket_count(), 0);
michael@0 720 }
michael@0 721
michael@0 722 void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
michael@0 723 DCHECK_EQ(histogram.bucket_count(), counts_.size());
michael@0 724 }
michael@0 725
michael@0 726 void Histogram::SampleSet::Accumulate(Sample value, Count count,
michael@0 727 size_t index) {
michael@0 728 DCHECK(count == 1 || count == -1);
michael@0 729 counts_[index] += count;
michael@0 730 redundant_count_ += count;
michael@0 731 sum_ += static_cast<int64_t>(count) * value;
michael@0 732 DCHECK_GE(counts_[index], 0);
michael@0 733 DCHECK_GE(sum_, 0);
michael@0 734 DCHECK_GE(redundant_count_, 0);
michael@0 735 }
michael@0 736
michael@0 737 void Histogram::SampleSet::AccumulateWithLinearStats(Sample value,
michael@0 738 Count count,
michael@0 739 size_t index) {
michael@0 740 Accumulate(value, count, index);
michael@0 741 sum_squares_ += static_cast<int64_t>(count) * value * value;
michael@0 742 }
michael@0 743
michael@0 744 void Histogram::SampleSet::AccumulateWithExponentialStats(Sample value,
michael@0 745 Count count,
michael@0 746 size_t index,
michael@0 747 bool computeExtendedStatistics) {
michael@0 748 Accumulate(value, count, index);
michael@0 749 if (computeExtendedStatistics) {
michael@0 750 DCHECK_GE(value, 0);
michael@0 751 float value_log = logf(static_cast<float>(value) + 1.0f);
michael@0 752 log_sum_ += count * value_log;
michael@0 753 log_sum_squares_ += count * value_log * value_log;
michael@0 754 }
michael@0 755 }
michael@0 756
michael@0 757 Count Histogram::SampleSet::TotalCount() const {
michael@0 758 Count total = 0;
michael@0 759 for (Counts::const_iterator it = counts_.begin();
michael@0 760 it != counts_.end();
michael@0 761 ++it) {
michael@0 762 total += *it;
michael@0 763 }
michael@0 764 return total;
michael@0 765 }
michael@0 766
michael@0 767 void Histogram::SampleSet::Add(const SampleSet& other) {
michael@0 768 DCHECK_EQ(counts_.size(), other.counts_.size());
michael@0 769 sum_ += other.sum_;
michael@0 770 sum_squares_ += other.sum_squares_;
michael@0 771 log_sum_ += other.log_sum_;
michael@0 772 log_sum_squares_ += other.log_sum_squares_;
michael@0 773 redundant_count_ += other.redundant_count_;
michael@0 774 for (size_t index = 0; index < counts_.size(); ++index)
michael@0 775 counts_[index] += other.counts_[index];
michael@0 776 }
michael@0 777
michael@0 778 void Histogram::SampleSet::Subtract(const SampleSet& other) {
michael@0 779 DCHECK_EQ(counts_.size(), other.counts_.size());
michael@0 780 // Note: Race conditions in snapshotting a sum may lead to (temporary)
michael@0 781 // negative values when snapshots are later combined (and deltas calculated).
michael@0 782 // As a result, we don't currently CHCEK() for positive values.
michael@0 783 sum_ -= other.sum_;
michael@0 784 sum_squares_ -= other.sum_squares_;
michael@0 785 log_sum_ -= other.log_sum_;
michael@0 786 log_sum_squares_ -= other.log_sum_squares_;
michael@0 787 redundant_count_ -= other.redundant_count_;
michael@0 788 for (size_t index = 0; index < counts_.size(); ++index) {
michael@0 789 counts_[index] -= other.counts_[index];
michael@0 790 DCHECK_GE(counts_[index], 0);
michael@0 791 }
michael@0 792 }
michael@0 793
michael@0 794 bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
michael@0 795 pickle->WriteInt64(sum_);
michael@0 796 pickle->WriteInt64(redundant_count_);
michael@0 797 pickle->WriteSize(counts_.size());
michael@0 798
michael@0 799 for (size_t index = 0; index < counts_.size(); ++index) {
michael@0 800 pickle->WriteInt(counts_[index]);
michael@0 801 }
michael@0 802
michael@0 803 return true;
michael@0 804 }
michael@0 805
michael@0 806 bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) {
michael@0 807 DCHECK_EQ(counts_.size(), 0u);
michael@0 808 DCHECK_EQ(sum_, 0);
michael@0 809 DCHECK_EQ(redundant_count_, 0);
michael@0 810
michael@0 811 size_t counts_size;
michael@0 812
michael@0 813 if (!pickle.ReadInt64(iter, &sum_) ||
michael@0 814 !pickle.ReadInt64(iter, &redundant_count_) ||
michael@0 815 !pickle.ReadSize(iter, &counts_size)) {
michael@0 816 return false;
michael@0 817 }
michael@0 818
michael@0 819 if (counts_size == 0)
michael@0 820 return false;
michael@0 821
michael@0 822 int count = 0;
michael@0 823 for (size_t index = 0; index < counts_size; ++index) {
michael@0 824 int i;
michael@0 825 if (!pickle.ReadInt(iter, &i))
michael@0 826 return false;
michael@0 827 counts_.push_back(i);
michael@0 828 count += i;
michael@0 829 }
michael@0 830
michael@0 831 return true;
michael@0 832 }
michael@0 833
michael@0 834 //------------------------------------------------------------------------------
michael@0 835 // LinearHistogram: This histogram uses a traditional set of evenly spaced
michael@0 836 // buckets.
michael@0 837 //------------------------------------------------------------------------------
michael@0 838
michael@0 839 LinearHistogram::~LinearHistogram() {
michael@0 840 }
michael@0 841
michael@0 842 Histogram* LinearHistogram::FactoryGet(const std::string& name,
michael@0 843 Sample minimum,
michael@0 844 Sample maximum,
michael@0 845 size_t bucket_count,
michael@0 846 Flags flags) {
michael@0 847 Histogram* histogram(NULL);
michael@0 848
michael@0 849 if (minimum < 1)
michael@0 850 minimum = 1;
michael@0 851 if (maximum > kSampleType_MAX - 1)
michael@0 852 maximum = kSampleType_MAX - 1;
michael@0 853
michael@0 854 if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
michael@0 855 LinearHistogram* tentative_histogram =
michael@0 856 new LinearHistogram(name, minimum, maximum, bucket_count);
michael@0 857 tentative_histogram->InitializeBucketRange();
michael@0 858 tentative_histogram->SetFlags(flags);
michael@0 859 histogram =
michael@0 860 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
michael@0 861 }
michael@0 862
michael@0 863 DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type());
michael@0 864 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
michael@0 865 return histogram;
michael@0 866 }
michael@0 867
michael@0 868 Histogram* LinearHistogram::FactoryTimeGet(const std::string& name,
michael@0 869 TimeDelta minimum,
michael@0 870 TimeDelta maximum,
michael@0 871 size_t bucket_count,
michael@0 872 Flags flags) {
michael@0 873 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
michael@0 874 bucket_count, flags);
michael@0 875 }
michael@0 876
michael@0 877 Histogram::ClassType LinearHistogram::histogram_type() const {
michael@0 878 return LINEAR_HISTOGRAM;
michael@0 879 }
michael@0 880
michael@0 881 void LinearHistogram::Accumulate(Sample value, Count count, size_t index) {
michael@0 882 sample_.AccumulateWithLinearStats(value, count, index);
michael@0 883 }
michael@0 884
michael@0 885 void LinearHistogram::SetRangeDescriptions(
michael@0 886 const DescriptionPair descriptions[]) {
michael@0 887 for (int i =0; descriptions[i].description; ++i) {
michael@0 888 bucket_description_[descriptions[i].sample] = descriptions[i].description;
michael@0 889 }
michael@0 890 }
michael@0 891
michael@0 892 LinearHistogram::LinearHistogram(const std::string& name,
michael@0 893 Sample minimum,
michael@0 894 Sample maximum,
michael@0 895 size_t bucket_count)
michael@0 896 : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
michael@0 897 }
michael@0 898
michael@0 899 LinearHistogram::LinearHistogram(const std::string& name,
michael@0 900 TimeDelta minimum,
michael@0 901 TimeDelta maximum,
michael@0 902 size_t bucket_count)
michael@0 903 : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
michael@0 904 minimum : TimeDelta::FromMilliseconds(1),
michael@0 905 maximum, bucket_count) {
michael@0 906 }
michael@0 907
michael@0 908 void LinearHistogram::InitializeBucketRange() {
michael@0 909 DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here.
michael@0 910 double min = declared_min();
michael@0 911 double max = declared_max();
michael@0 912 size_t i;
michael@0 913 for (i = 1; i < bucket_count(); ++i) {
michael@0 914 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
michael@0 915 (bucket_count() - 2);
michael@0 916 SetBucketRange(i, static_cast<int> (linear_range + 0.5));
michael@0 917 }
michael@0 918 ResetRangeChecksum();
michael@0 919 }
michael@0 920
michael@0 921 double LinearHistogram::GetBucketSize(Count current, size_t i) const {
michael@0 922 DCHECK_GT(ranges(i + 1), ranges(i));
michael@0 923 // Adjacent buckets with different widths would have "surprisingly" many (few)
michael@0 924 // samples in a histogram if we didn't normalize this way.
michael@0 925 double denominator = ranges(i + 1) - ranges(i);
michael@0 926 return current/denominator;
michael@0 927 }
michael@0 928
michael@0 929 const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
michael@0 930 int range = ranges(i);
michael@0 931 BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
michael@0 932 if (it == bucket_description_.end())
michael@0 933 return Histogram::GetAsciiBucketRange(i);
michael@0 934 return it->second;
michael@0 935 }
michael@0 936
michael@0 937 bool LinearHistogram::PrintEmptyBucket(size_t index) const {
michael@0 938 return bucket_description_.find(ranges(index)) == bucket_description_.end();
michael@0 939 }
michael@0 940
michael@0 941
michael@0 942 //------------------------------------------------------------------------------
michael@0 943 // This section provides implementation for BooleanHistogram.
michael@0 944 //------------------------------------------------------------------------------
michael@0 945
michael@0 946 Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) {
michael@0 947 Histogram* histogram(NULL);
michael@0 948
michael@0 949 if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
michael@0 950 BooleanHistogram* tentative_histogram = new BooleanHistogram(name);
michael@0 951 tentative_histogram->InitializeBucketRange();
michael@0 952 tentative_histogram->SetFlags(flags);
michael@0 953 histogram =
michael@0 954 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
michael@0 955 }
michael@0 956
michael@0 957 DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type());
michael@0 958 return histogram;
michael@0 959 }
michael@0 960
michael@0 961 Histogram::ClassType BooleanHistogram::histogram_type() const {
michael@0 962 return BOOLEAN_HISTOGRAM;
michael@0 963 }
michael@0 964
michael@0 965 void BooleanHistogram::AddBoolean(bool value) {
michael@0 966 Add(value ? 1 : 0);
michael@0 967 }
michael@0 968
michael@0 969 BooleanHistogram::BooleanHistogram(const std::string& name)
michael@0 970 : LinearHistogram(name, 1, 2, 3) {
michael@0 971 }
michael@0 972
michael@0 973 void
michael@0 974 BooleanHistogram::Accumulate(Sample value, Count count, size_t index)
michael@0 975 {
michael@0 976 // Callers will have computed index based on the non-booleanified value.
michael@0 977 // So we need to adjust the index manually.
michael@0 978 LinearHistogram::Accumulate(!!value, count, value ? 1 : 0);
michael@0 979 }
michael@0 980
michael@0 981 //------------------------------------------------------------------------------
michael@0 982 // FlagHistogram:
michael@0 983 //------------------------------------------------------------------------------
michael@0 984
michael@0 985 Histogram *
michael@0 986 FlagHistogram::FactoryGet(const std::string &name, Flags flags)
michael@0 987 {
michael@0 988 Histogram *h(nullptr);
michael@0 989
michael@0 990 if (!StatisticsRecorder::FindHistogram(name, &h)) {
michael@0 991 FlagHistogram *fh = new FlagHistogram(name);
michael@0 992 fh->InitializeBucketRange();
michael@0 993 fh->SetFlags(flags);
michael@0 994 size_t zero_index = fh->BucketIndex(0);
michael@0 995 fh->LinearHistogram::Accumulate(0, 1, zero_index);
michael@0 996 h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh);
michael@0 997 }
michael@0 998
michael@0 999 return h;
michael@0 1000 }
michael@0 1001
michael@0 1002 FlagHistogram::FlagHistogram(const std::string &name)
michael@0 1003 : BooleanHistogram(name), mSwitched(false) {
michael@0 1004 }
michael@0 1005
michael@0 1006 Histogram::ClassType
michael@0 1007 FlagHistogram::histogram_type() const
michael@0 1008 {
michael@0 1009 return FLAG_HISTOGRAM;
michael@0 1010 }
michael@0 1011
michael@0 1012 void
michael@0 1013 FlagHistogram::Accumulate(Sample value, Count count, size_t index)
michael@0 1014 {
michael@0 1015 if (mSwitched) {
michael@0 1016 return;
michael@0 1017 }
michael@0 1018
michael@0 1019 mSwitched = true;
michael@0 1020 DCHECK_EQ(value, 1);
michael@0 1021 LinearHistogram::Accumulate(value, 1, index);
michael@0 1022 size_t zero_index = BucketIndex(0);
michael@0 1023 LinearHistogram::Accumulate(0, -1, zero_index);
michael@0 1024 }
michael@0 1025
michael@0 1026 void
michael@0 1027 FlagHistogram::AddSampleSet(const SampleSet& sample) {
michael@0 1028 DCHECK_EQ(bucket_count(), sample.size());
michael@0 1029 // We can't be sure the SampleSet provided came from another FlagHistogram,
michael@0 1030 // so we take the following steps:
michael@0 1031 // - If our flag has already been set do nothing.
michael@0 1032 // - Set our flag if the following hold:
michael@0 1033 // - The sum of the counts in the provided SampleSet is 1.
michael@0 1034 // - The bucket index for that single value is the same as the index where we
michael@0 1035 // would place our set flag.
michael@0 1036 // - Otherwise, take no action.
michael@0 1037
michael@0 1038 if (mSwitched) {
michael@0 1039 return;
michael@0 1040 }
michael@0 1041
michael@0 1042 if (sample.sum() != 1) {
michael@0 1043 return;
michael@0 1044 }
michael@0 1045
michael@0 1046 size_t one_index = BucketIndex(1);
michael@0 1047 if (sample.counts(one_index) == 1) {
michael@0 1048 Accumulate(1, 1, one_index);
michael@0 1049 }
michael@0 1050 }
michael@0 1051 //------------------------------------------------------------------------------
michael@0 1052 // CustomHistogram:
michael@0 1053 //------------------------------------------------------------------------------
michael@0 1054
michael@0 1055 Histogram* CustomHistogram::FactoryGet(const std::string& name,
michael@0 1056 const std::vector<Sample>& custom_ranges,
michael@0 1057 Flags flags) {
michael@0 1058 Histogram* histogram(NULL);
michael@0 1059
michael@0 1060 // Remove the duplicates in the custom ranges array.
michael@0 1061 std::vector<int> ranges = custom_ranges;
michael@0 1062 ranges.push_back(0); // Ensure we have a zero value.
michael@0 1063 std::sort(ranges.begin(), ranges.end());
michael@0 1064 ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
michael@0 1065 if (ranges.size() <= 1) {
michael@0 1066 DCHECK(false);
michael@0 1067 // Note that we pushed a 0 in above, so for defensive code....
michael@0 1068 ranges.push_back(1); // Put in some data so we can index to [1].
michael@0 1069 }
michael@0 1070
michael@0 1071 DCHECK_LT(ranges.back(), kSampleType_MAX);
michael@0 1072
michael@0 1073 if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
michael@0 1074 CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges);
michael@0 1075 tentative_histogram->InitializedCustomBucketRange(ranges);
michael@0 1076 tentative_histogram->SetFlags(flags);
michael@0 1077 histogram =
michael@0 1078 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
michael@0 1079 }
michael@0 1080
michael@0 1081 DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM);
michael@0 1082 DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(),
michael@0 1083 ranges.size()));
michael@0 1084 return histogram;
michael@0 1085 }
michael@0 1086
michael@0 1087 Histogram::ClassType CustomHistogram::histogram_type() const {
michael@0 1088 return CUSTOM_HISTOGRAM;
michael@0 1089 }
michael@0 1090
michael@0 1091 CustomHistogram::CustomHistogram(const std::string& name,
michael@0 1092 const std::vector<Sample>& custom_ranges)
michael@0 1093 : Histogram(name, custom_ranges[1], custom_ranges.back(),
michael@0 1094 custom_ranges.size()) {
michael@0 1095 DCHECK_GT(custom_ranges.size(), 1u);
michael@0 1096 DCHECK_EQ(custom_ranges[0], 0);
michael@0 1097 }
michael@0 1098
michael@0 1099 void CustomHistogram::InitializedCustomBucketRange(
michael@0 1100 const std::vector<Sample>& custom_ranges) {
michael@0 1101 DCHECK_GT(custom_ranges.size(), 1u);
michael@0 1102 DCHECK_EQ(custom_ranges[0], 0);
michael@0 1103 DCHECK_LE(custom_ranges.size(), bucket_count());
michael@0 1104 for (size_t index = 0; index < custom_ranges.size(); ++index)
michael@0 1105 SetBucketRange(index, custom_ranges[index]);
michael@0 1106 ResetRangeChecksum();
michael@0 1107 }
michael@0 1108
michael@0 1109 double CustomHistogram::GetBucketSize(Count current, size_t i) const {
michael@0 1110 return 1;
michael@0 1111 }
michael@0 1112
michael@0 1113 //------------------------------------------------------------------------------
michael@0 1114 // The next section handles global (central) support for all histograms, as well
michael@0 1115 // as startup/teardown of this service.
michael@0 1116 //------------------------------------------------------------------------------
michael@0 1117
michael@0 1118 // This singleton instance should be started during the single threaded portion
michael@0 1119 // of main(), and hence it is not thread safe. It initializes globals to
michael@0 1120 // provide support for all future calls.
michael@0 1121 StatisticsRecorder::StatisticsRecorder() {
michael@0 1122 DCHECK(!histograms_);
michael@0 1123 if (lock_ == NULL) {
michael@0 1124 // This will leak on purpose. It's the only way to make sure we won't race
michael@0 1125 // against the static uninitialization of the module while one of our
michael@0 1126 // static methods relying on the lock get called at an inappropriate time
michael@0 1127 // during the termination phase. Since it's a static data member, we will
michael@0 1128 // leak one per process, which would be similar to the instance allocated
michael@0 1129 // during static initialization and released only on process termination.
michael@0 1130 lock_ = new base::Lock;
michael@0 1131 }
michael@0 1132 base::AutoLock auto_lock(*lock_);
michael@0 1133 histograms_ = new HistogramMap;
michael@0 1134 }
michael@0 1135
michael@0 1136 StatisticsRecorder::~StatisticsRecorder() {
michael@0 1137 DCHECK(histograms_ && lock_);
michael@0 1138
michael@0 1139 if (dump_on_exit_) {
michael@0 1140 std::string output;
michael@0 1141 WriteGraph("", &output);
michael@0 1142 CHROMIUM_LOG(INFO) << output;
michael@0 1143 }
michael@0 1144 // Clean up.
michael@0 1145 HistogramMap* histograms = NULL;
michael@0 1146 {
michael@0 1147 base::AutoLock auto_lock(*lock_);
michael@0 1148 histograms = histograms_;
michael@0 1149 histograms_ = NULL;
michael@0 1150 for (HistogramMap::iterator it = histograms->begin();
michael@0 1151 histograms->end() != it;
michael@0 1152 ++it) {
michael@0 1153 // No other clients permanently hold Histogram references, so we
michael@0 1154 // have the only one and it is safe to delete it.
michael@0 1155 delete it->second;
michael@0 1156 }
michael@0 1157 }
michael@0 1158 delete histograms;
michael@0 1159 // We don't delete lock_ on purpose to avoid having to properly protect
michael@0 1160 // against it going away after we checked for NULL in the static methods.
michael@0 1161 }
michael@0 1162
michael@0 1163 // static
michael@0 1164 bool StatisticsRecorder::IsActive() {
michael@0 1165 if (lock_ == NULL)
michael@0 1166 return false;
michael@0 1167 base::AutoLock auto_lock(*lock_);
michael@0 1168 return NULL != histograms_;
michael@0 1169 }
michael@0 1170
michael@0 1171 Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) {
michael@0 1172 DCHECK(histogram->HasValidRangeChecksum());
michael@0 1173 if (lock_ == NULL)
michael@0 1174 return histogram;
michael@0 1175 base::AutoLock auto_lock(*lock_);
michael@0 1176 if (!histograms_)
michael@0 1177 return histogram;
michael@0 1178 const std::string name = histogram->histogram_name();
michael@0 1179 HistogramMap::iterator it = histograms_->find(name);
michael@0 1180 // Avoid overwriting a previous registration.
michael@0 1181 if (histograms_->end() == it) {
michael@0 1182 (*histograms_)[name] = histogram;
michael@0 1183 } else {
michael@0 1184 delete histogram; // We already have one by this name.
michael@0 1185 histogram = it->second;
michael@0 1186 }
michael@0 1187 return histogram;
michael@0 1188 }
michael@0 1189
michael@0 1190 // static
michael@0 1191 void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
michael@0 1192 std::string* output) {
michael@0 1193 if (!IsActive())
michael@0 1194 return;
michael@0 1195 output->append("<html><head><title>About Histograms");
michael@0 1196 if (!query.empty())
michael@0 1197 output->append(" - " + query);
michael@0 1198 output->append("</title>"
michael@0 1199 // We'd like the following no-cache... but it doesn't work.
michael@0 1200 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
michael@0 1201 "</head><body>");
michael@0 1202
michael@0 1203 Histograms snapshot;
michael@0 1204 GetSnapshot(query, &snapshot);
michael@0 1205 for (Histograms::iterator it = snapshot.begin();
michael@0 1206 it != snapshot.end();
michael@0 1207 ++it) {
michael@0 1208 (*it)->WriteHTMLGraph(output);
michael@0 1209 output->append("<br><hr><br>");
michael@0 1210 }
michael@0 1211 output->append("</body></html>");
michael@0 1212 }
michael@0 1213
michael@0 1214 // static
michael@0 1215 void StatisticsRecorder::WriteGraph(const std::string& query,
michael@0 1216 std::string* output) {
michael@0 1217 if (!IsActive())
michael@0 1218 return;
michael@0 1219 if (query.length())
michael@0 1220 StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
michael@0 1221 else
michael@0 1222 output->append("Collections of all histograms\n");
michael@0 1223
michael@0 1224 Histograms snapshot;
michael@0 1225 GetSnapshot(query, &snapshot);
michael@0 1226 for (Histograms::iterator it = snapshot.begin();
michael@0 1227 it != snapshot.end();
michael@0 1228 ++it) {
michael@0 1229 (*it)->WriteAscii(true, "\n", output);
michael@0 1230 output->append("\n");
michael@0 1231 }
michael@0 1232 }
michael@0 1233
michael@0 1234 // static
michael@0 1235 void StatisticsRecorder::GetHistograms(Histograms* output) {
michael@0 1236 if (lock_ == NULL)
michael@0 1237 return;
michael@0 1238 base::AutoLock auto_lock(*lock_);
michael@0 1239 if (!histograms_)
michael@0 1240 return;
michael@0 1241 for (HistogramMap::iterator it = histograms_->begin();
michael@0 1242 histograms_->end() != it;
michael@0 1243 ++it) {
michael@0 1244 DCHECK_EQ(it->first, it->second->histogram_name());
michael@0 1245 output->push_back(it->second);
michael@0 1246 }
michael@0 1247 }
michael@0 1248
michael@0 1249 bool StatisticsRecorder::FindHistogram(const std::string& name,
michael@0 1250 Histogram** histogram) {
michael@0 1251 if (lock_ == NULL)
michael@0 1252 return false;
michael@0 1253 base::AutoLock auto_lock(*lock_);
michael@0 1254 if (!histograms_)
michael@0 1255 return false;
michael@0 1256 HistogramMap::iterator it = histograms_->find(name);
michael@0 1257 if (histograms_->end() == it)
michael@0 1258 return false;
michael@0 1259 *histogram = it->second;
michael@0 1260 return true;
michael@0 1261 }
michael@0 1262
michael@0 1263 // private static
michael@0 1264 void StatisticsRecorder::GetSnapshot(const std::string& query,
michael@0 1265 Histograms* snapshot) {
michael@0 1266 if (lock_ == NULL)
michael@0 1267 return;
michael@0 1268 base::AutoLock auto_lock(*lock_);
michael@0 1269 if (!histograms_)
michael@0 1270 return;
michael@0 1271 for (HistogramMap::iterator it = histograms_->begin();
michael@0 1272 histograms_->end() != it;
michael@0 1273 ++it) {
michael@0 1274 if (it->first.find(query) != std::string::npos)
michael@0 1275 snapshot->push_back(it->second);
michael@0 1276 }
michael@0 1277 }
michael@0 1278
michael@0 1279 // static
michael@0 1280 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
michael@0 1281 // static
michael@0 1282 base::Lock* StatisticsRecorder::lock_ = NULL;
michael@0 1283 // static
michael@0 1284 bool StatisticsRecorder::dump_on_exit_ = false;
michael@0 1285
michael@0 1286 } // namespace base

mercurial