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