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