| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Histogram is an object that aggregates statistics, and can summarize them in | 5 // Histogram is an object that aggregates statistics, and can summarize them in |
| 6 // various forms, including ASCII graphical, HTML, and numerically (as a | 6 // various forms, including ASCII graphical, HTML, and numerically (as a |
| 7 // vector of numbers corresponding to each of the aggregating buckets). | 7 // vector of numbers corresponding to each of the aggregating buckets). |
| 8 // See header file for details and examples. | 8 // See header file for details and examples. |
| 9 | 9 |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 } | 54 } |
| 55 | 55 |
| 56 Histogram::Histogram(const std::string& name, Sample minimum, | 56 Histogram::Histogram(const std::string& name, Sample minimum, |
| 57 Sample maximum, size_t bucket_count) | 57 Sample maximum, size_t bucket_count) |
| 58 : histogram_name_(name), | 58 : histogram_name_(name), |
| 59 declared_min_(minimum), | 59 declared_min_(minimum), |
| 60 declared_max_(maximum), | 60 declared_max_(maximum), |
| 61 bucket_count_(bucket_count), | 61 bucket_count_(bucket_count), |
| 62 flags_(kNoFlags), | 62 flags_(kNoFlags), |
| 63 ranges_(bucket_count + 1, 0), | 63 ranges_(bucket_count + 1, 0), |
| 64 range_checksum_(0), |
| 64 sample_() { | 65 sample_() { |
| 65 Initialize(); | 66 Initialize(); |
| 66 } | 67 } |
| 67 | 68 |
| 68 Histogram::Histogram(const std::string& name, TimeDelta minimum, | 69 Histogram::Histogram(const std::string& name, TimeDelta minimum, |
| 69 TimeDelta maximum, size_t bucket_count) | 70 TimeDelta maximum, size_t bucket_count) |
| 70 : histogram_name_(name), | 71 : histogram_name_(name), |
| 71 declared_min_(static_cast<int> (minimum.InMilliseconds())), | 72 declared_min_(static_cast<int> (minimum.InMilliseconds())), |
| 72 declared_max_(static_cast<int> (maximum.InMilliseconds())), | 73 declared_max_(static_cast<int> (maximum.InMilliseconds())), |
| 73 bucket_count_(bucket_count), | 74 bucket_count_(bucket_count), |
| 74 flags_(kNoFlags), | 75 flags_(kNoFlags), |
| 75 ranges_(bucket_count + 1, 0), | 76 ranges_(bucket_count + 1, 0), |
| 77 range_checksum_(0), |
| 76 sample_() { | 78 sample_() { |
| 77 Initialize(); | 79 Initialize(); |
| 78 } | 80 } |
| 79 | 81 |
| 80 Histogram::~Histogram() { | 82 Histogram::~Histogram() { |
| 81 if (StatisticsRecorder::dump_on_exit()) { | 83 if (StatisticsRecorder::dump_on_exit()) { |
| 82 std::string output; | 84 std::string output; |
| 83 WriteAscii(true, "\n", &output); | 85 WriteAscii(true, "\n", &output); |
| 84 LOG(INFO) << output; | 86 LOG(INFO) << output; |
| 85 } | 87 } |
| 86 | 88 |
| 87 // Just to make sure most derived class did this properly... | 89 // Just to make sure most derived class did this properly... |
| 88 DCHECK(ValidateBucketRanges()); | 90 DCHECK(ValidateBucketRanges()); |
| 91 DCHECK(HasValidRangeChecksum()); |
| 89 } | 92 } |
| 90 | 93 |
| 91 bool Histogram::PrintEmptyBucket(size_t index) const { | 94 bool Histogram::PrintEmptyBucket(size_t index) const { |
| 92 return true; | 95 return true; |
| 93 } | 96 } |
| 94 | 97 |
| 95 void Histogram::Add(int value) { | 98 void Histogram::Add(int value) { |
| 96 if (value > kSampleType_MAX - 1) | 99 if (value > kSampleType_MAX - 1) |
| 97 value = kSampleType_MAX - 1; | 100 value = kSampleType_MAX - 1; |
| 98 if (value < 0) | 101 if (value < 0) |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 ranges_[bucket_count_] = kSampleType_MAX; | 214 ranges_[bucket_count_] = kSampleType_MAX; |
| 212 InitializeBucketRange(); | 215 InitializeBucketRange(); |
| 213 DCHECK(ValidateBucketRanges()); | 216 DCHECK(ValidateBucketRanges()); |
| 214 StatisticsRecorder::Register(this); | 217 StatisticsRecorder::Register(this); |
| 215 } | 218 } |
| 216 | 219 |
| 217 // Calculate what range of values are held in each bucket. | 220 // Calculate what range of values are held in each bucket. |
| 218 // We have to be careful that we don't pick a ratio between starting points in | 221 // We have to be careful that we don't pick a ratio between starting points in |
| 219 // consecutive buckets that is sooo small, that the integer bounds are the same | 222 // consecutive buckets that is sooo small, that the integer bounds are the same |
| 220 // (effectively making one bucket get no values). We need to avoid: | 223 // (effectively making one bucket get no values). We need to avoid: |
| 221 // (ranges_[i] == ranges_[i + 1] | 224 // ranges_[i] == ranges_[i + 1] |
| 222 // To avoid that, we just do a fine-grained bucket width as far as we need to | 225 // To avoid that, we just do a fine-grained bucket width as far as we need to |
| 223 // until we get a ratio that moves us along at least 2 units at a time. From | 226 // until we get a ratio that moves us along at least 2 units at a time. From |
| 224 // that bucket onward we do use the exponential growth of buckets. | 227 // that bucket onward we do use the exponential growth of buckets. |
| 225 void Histogram::InitializeBucketRange() { | 228 void Histogram::InitializeBucketRange() { |
| 226 double log_max = log(static_cast<double>(declared_max())); | 229 double log_max = log(static_cast<double>(declared_max())); |
| 227 double log_ratio; | 230 double log_ratio; |
| 228 double log_next; | 231 double log_next; |
| 229 size_t bucket_index = 1; | 232 size_t bucket_index = 1; |
| 230 Sample current = declared_min(); | 233 Sample current = declared_min(); |
| 231 SetBucketRange(bucket_index, current); | 234 SetBucketRange(bucket_index, current); |
| 232 while (bucket_count() > ++bucket_index) { | 235 while (bucket_count() > ++bucket_index) { |
| 233 double log_current; | 236 double log_current; |
| 234 log_current = log(static_cast<double>(current)); | 237 log_current = log(static_cast<double>(current)); |
| 235 // Calculate the count'th root of the range. | 238 // Calculate the count'th root of the range. |
| 236 log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); | 239 log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); |
| 237 // See where the next bucket would start. | 240 // See where the next bucket would start. |
| 238 log_next = log_current + log_ratio; | 241 log_next = log_current + log_ratio; |
| 239 int next; | 242 int next; |
| 240 next = static_cast<int>(floor(exp(log_next) + 0.5)); | 243 next = static_cast<int>(floor(exp(log_next) + 0.5)); |
| 241 if (next > current) | 244 if (next > current) |
| 242 current = next; | 245 current = next; |
| 243 else | 246 else |
| 244 ++current; // Just do a narrow bucket, and keep trying. | 247 ++current; // Just do a narrow bucket, and keep trying. |
| 245 SetBucketRange(bucket_index, current); | 248 SetBucketRange(bucket_index, current); |
| 246 } | 249 } |
| 250 ResetRangeChecksum(); |
| 247 | 251 |
| 248 DCHECK_EQ(bucket_count(), bucket_index); | 252 DCHECK_EQ(bucket_count(), bucket_index); |
| 249 } | 253 } |
| 250 | 254 |
| 251 size_t Histogram::BucketIndex(Sample value) const { | 255 size_t Histogram::BucketIndex(Sample value) const { |
| 252 // Use simple binary search. This is very general, but there are better | 256 // Use simple binary search. This is very general, but there are better |
| 253 // approaches if we knew that the buckets were linearly distributed. | 257 // approaches if we knew that the buckets were linearly distributed. |
| 254 DCHECK_LE(ranges(0), value); | 258 DCHECK_LE(ranges(0), value); |
| 255 DCHECK_GT(ranges(bucket_count()), value); | 259 DCHECK_GT(ranges(bucket_count()), value); |
| 256 size_t under = 0; | 260 size_t under = 0; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 280 // not have 0-graphical-height buckets. | 284 // not have 0-graphical-height buckets. |
| 281 double Histogram::GetBucketSize(Count current, size_t i) const { | 285 double Histogram::GetBucketSize(Count current, size_t i) const { |
| 282 DCHECK_GT(ranges(i + 1), ranges(i)); | 286 DCHECK_GT(ranges(i + 1), ranges(i)); |
| 283 static const double kTransitionWidth = 5; | 287 static const double kTransitionWidth = 5; |
| 284 double denominator = ranges(i + 1) - ranges(i); | 288 double denominator = ranges(i + 1) - ranges(i); |
| 285 if (denominator > kTransitionWidth) | 289 if (denominator > kTransitionWidth) |
| 286 denominator = kTransitionWidth; // Stop trying to normalize. | 290 denominator = kTransitionWidth; // Stop trying to normalize. |
| 287 return current/denominator; | 291 return current/denominator; |
| 288 } | 292 } |
| 289 | 293 |
| 294 void Histogram::ResetRangeChecksum() { |
| 295 range_checksum_ = CalculateRangeChecksum(); |
| 296 } |
| 297 |
| 298 bool Histogram::HasValidRangeChecksum() const { |
| 299 return CalculateRangeChecksum() == range_checksum_; |
| 300 } |
| 301 |
| 302 Histogram::Sample Histogram::CalculateRangeChecksum() const { |
| 303 DCHECK_EQ(ranges_.size(), bucket_count() + 1); |
| 304 Sample checksum = 0; |
| 305 for (size_t index = 0; index < bucket_count(); ++index) { |
| 306 checksum += ranges(index); |
| 307 } |
| 308 return checksum; |
| 309 } |
| 310 |
| 290 //------------------------------------------------------------------------------ | 311 //------------------------------------------------------------------------------ |
| 291 // The following two methods can be overridden to provide a thread safe | 312 // The following two methods can be overridden to provide a thread safe |
| 292 // version of this class. The cost of locking is low... but an error in each | 313 // version of this class. The cost of locking is low... but an error in each |
| 293 // of these methods has minimal impact. For now, I'll leave this unlocked, | 314 // of these methods has minimal impact. For now, I'll leave this unlocked, |
| 294 // and I don't believe I can loose more than a count or two. | 315 // and I don't believe I can loose more than a count or two. |
| 295 // The vectors are NOT reallocated, so there is no risk of them moving around. | 316 // The vectors are NOT reallocated, so there is no risk of them moving around. |
| 296 | 317 |
| 297 // Update histogram data with new sample. | 318 // Update histogram data with new sample. |
| 298 void Histogram::Accumulate(Sample value, Count count, size_t index) { | 319 void Histogram::Accumulate(Sample value, Count count, size_t index) { |
| 299 // Note locking not done in this version!!! | 320 // Note locking not done in this version!!! |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 410 // static | 431 // static |
| 411 std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, | 432 std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, |
| 412 const SampleSet& snapshot) { | 433 const SampleSet& snapshot) { |
| 413 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); | 434 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); |
| 414 | 435 |
| 415 Pickle pickle; | 436 Pickle pickle; |
| 416 pickle.WriteString(histogram.histogram_name()); | 437 pickle.WriteString(histogram.histogram_name()); |
| 417 pickle.WriteInt(histogram.declared_min()); | 438 pickle.WriteInt(histogram.declared_min()); |
| 418 pickle.WriteInt(histogram.declared_max()); | 439 pickle.WriteInt(histogram.declared_max()); |
| 419 pickle.WriteSize(histogram.bucket_count()); | 440 pickle.WriteSize(histogram.bucket_count()); |
| 441 pickle.WriteInt(histogram.range_checksum()); |
| 420 pickle.WriteInt(histogram.histogram_type()); | 442 pickle.WriteInt(histogram.histogram_type()); |
| 421 pickle.WriteInt(histogram.flags()); | 443 pickle.WriteInt(histogram.flags()); |
| 422 | 444 |
| 423 snapshot.Serialize(&pickle); | 445 snapshot.Serialize(&pickle); |
| 424 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); | 446 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); |
| 425 } | 447 } |
| 426 | 448 |
| 427 // static | 449 // static |
| 428 bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { | 450 bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { |
| 429 if (histogram_info.empty()) { | 451 if (histogram_info.empty()) { |
| 430 return false; | 452 return false; |
| 431 } | 453 } |
| 432 | 454 |
| 433 Pickle pickle(histogram_info.data(), | 455 Pickle pickle(histogram_info.data(), |
| 434 static_cast<int>(histogram_info.size())); | 456 static_cast<int>(histogram_info.size())); |
| 435 void* iter = NULL; | 457 std::string histogram_name; |
| 436 size_t bucket_count; | |
| 437 int declared_min; | 458 int declared_min; |
| 438 int declared_max; | 459 int declared_max; |
| 460 size_t bucket_count; |
| 461 int range_checksum; |
| 439 int histogram_type; | 462 int histogram_type; |
| 440 int pickle_flags; | 463 int pickle_flags; |
| 441 std::string histogram_name; | |
| 442 SampleSet sample; | 464 SampleSet sample; |
| 443 | 465 |
| 466 void* iter = NULL; |
| 444 if (!pickle.ReadString(&iter, &histogram_name) || | 467 if (!pickle.ReadString(&iter, &histogram_name) || |
| 445 !pickle.ReadInt(&iter, &declared_min) || | 468 !pickle.ReadInt(&iter, &declared_min) || |
| 446 !pickle.ReadInt(&iter, &declared_max) || | 469 !pickle.ReadInt(&iter, &declared_max) || |
| 447 !pickle.ReadSize(&iter, &bucket_count) || | 470 !pickle.ReadSize(&iter, &bucket_count) || |
| 471 !pickle.ReadInt(&iter, &range_checksum) || |
| 448 !pickle.ReadInt(&iter, &histogram_type) || | 472 !pickle.ReadInt(&iter, &histogram_type) || |
| 449 !pickle.ReadInt(&iter, &pickle_flags) || | 473 !pickle.ReadInt(&iter, &pickle_flags) || |
| 450 !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { | 474 !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { |
| 451 LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; | 475 LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; |
| 452 return false; | 476 return false; |
| 453 } | 477 } |
| 454 DCHECK(pickle_flags & kIPCSerializationSourceFlag); | 478 DCHECK(pickle_flags & kIPCSerializationSourceFlag); |
| 455 // Since these fields may have come from an untrusted renderer, do additional | 479 // Since these fields may have come from an untrusted renderer, do additional |
| 456 // checks above and beyond those in Histogram::Initialize() | 480 // checks above and beyond those in Histogram::Initialize() |
| 457 if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || | 481 if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || |
| (...skipping 18 matching lines...) Expand all Loading... |
| 476 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); | 500 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); |
| 477 } else { | 501 } else { |
| 478 LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " | 502 LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " |
| 479 << histogram_type; | 503 << histogram_type; |
| 480 return false; | 504 return false; |
| 481 } | 505 } |
| 482 | 506 |
| 483 DCHECK_EQ(render_histogram->declared_min(), declared_min); | 507 DCHECK_EQ(render_histogram->declared_min(), declared_min); |
| 484 DCHECK_EQ(render_histogram->declared_max(), declared_max); | 508 DCHECK_EQ(render_histogram->declared_max(), declared_max); |
| 485 DCHECK_EQ(render_histogram->bucket_count(), bucket_count); | 509 DCHECK_EQ(render_histogram->bucket_count(), bucket_count); |
| 510 DCHECK_EQ(render_histogram->range_checksum(), range_checksum); |
| 486 DCHECK_EQ(render_histogram->histogram_type(), histogram_type); | 511 DCHECK_EQ(render_histogram->histogram_type(), histogram_type); |
| 487 | 512 |
| 488 if (render_histogram->flags() & kIPCSerializationSourceFlag) { | 513 if (render_histogram->flags() & kIPCSerializationSourceFlag) { |
| 489 DVLOG(1) << "Single process mode, histogram observed and not copied: " | 514 DVLOG(1) << "Single process mode, histogram observed and not copied: " |
| 490 << histogram_name; | 515 << histogram_name; |
| 491 } else { | 516 } else { |
| 492 DCHECK_EQ(flags & render_histogram->flags(), flags); | 517 DCHECK_EQ(flags & render_histogram->flags(), flags); |
| 493 render_histogram->AddSampleSet(sample); | 518 render_histogram->AddSampleSet(sample); |
| 494 } | 519 } |
| 495 | 520 |
| 496 return true; | 521 return true; |
| 497 } | 522 } |
| 498 | 523 |
| 499 //------------------------------------------------------------------------------ | 524 //------------------------------------------------------------------------------ |
| 525 // Methods for the validating a sample and a related histogram. |
| 526 //------------------------------------------------------------------------------ |
| 527 |
| 528 Histogram::Inconsistencies Histogram::FindCorruption( |
| 529 const SampleSet& snapshot) const { |
| 530 int inconsistencies = NO_INCONSISTENCIES; |
| 531 Sample previous_range = -1; // Bottom range is always 0. |
| 532 Sample checksum = 0; |
| 533 int64 count = 0; |
| 534 for (size_t index = 0; index < bucket_count(); ++index) { |
| 535 count += snapshot.counts(index); |
| 536 int new_range = ranges(index); |
| 537 checksum += new_range; |
| 538 if (previous_range >= new_range) |
| 539 inconsistencies |= BUCKET_ORDER_ERROR; |
| 540 previous_range = new_range; |
| 541 } |
| 542 |
| 543 if (checksum != range_checksum_) |
| 544 inconsistencies |= RANGE_CHECKSUM_ERROR; |
| 545 |
| 546 int64 delta64 = snapshot.redundant_count() - count; |
| 547 if (delta64 != 0) { |
| 548 int delta = static_cast<int>(delta64); |
| 549 if (delta != delta64) |
| 550 delta = INT_MAX; // Flag all giant errors as INT_MAX. |
| 551 // Since snapshots of histograms are taken asynchronously relative to |
| 552 // sampling (and snapped from different threads), it is pretty likely that |
| 553 // we'll catch a redundant count that doesn't match the sample count. We |
| 554 // allow for a certain amount of slop before flagging this as an |
| 555 // inconsistency. Even with an inconsistency, we'll snapshot it again (for |
| 556 // UMA in about a half hour, so we'll eventually get the data, if it was |
| 557 // not the result of a corruption. If histograms show that 1 is "too tight" |
| 558 // then we may try to use 2 or 3 for this slop value. |
| 559 const int kCommonRaceBasedCountMismatch = 1; |
| 560 if (delta > 0) { |
| 561 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); |
| 562 if (delta > kCommonRaceBasedCountMismatch) |
| 563 inconsistencies |= COUNT_HIGH_ERROR; |
| 564 } else { |
| 565 DCHECK_GT(0, delta); |
| 566 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); |
| 567 if (-delta > kCommonRaceBasedCountMismatch) |
| 568 inconsistencies |= COUNT_LOW_ERROR; |
| 569 } |
| 570 } |
| 571 return static_cast<Inconsistencies>(inconsistencies); |
| 572 } |
| 573 |
| 574 //------------------------------------------------------------------------------ |
| 500 // Methods for the Histogram::SampleSet class | 575 // Methods for the Histogram::SampleSet class |
| 501 //------------------------------------------------------------------------------ | 576 //------------------------------------------------------------------------------ |
| 502 | 577 |
| 503 Histogram::SampleSet::SampleSet() | 578 Histogram::SampleSet::SampleSet() |
| 504 : counts_(), | 579 : counts_(), |
| 505 sum_(0), | 580 sum_(0), |
| 506 square_sum_(0) { | 581 square_sum_(0), |
| 582 redundant_count_(0) { |
| 507 } | 583 } |
| 508 | 584 |
| 509 Histogram::SampleSet::~SampleSet() { | 585 Histogram::SampleSet::~SampleSet() { |
| 510 } | 586 } |
| 511 | 587 |
| 512 void Histogram::SampleSet::Resize(const Histogram& histogram) { | 588 void Histogram::SampleSet::Resize(const Histogram& histogram) { |
| 513 counts_.resize(histogram.bucket_count(), 0); | 589 counts_.resize(histogram.bucket_count(), 0); |
| 514 } | 590 } |
| 515 | 591 |
| 516 void Histogram::SampleSet::CheckSize(const Histogram& histogram) const { | 592 void Histogram::SampleSet::CheckSize(const Histogram& histogram) const { |
| 517 DCHECK_EQ(histogram.bucket_count(), counts_.size()); | 593 DCHECK_EQ(histogram.bucket_count(), counts_.size()); |
| 518 } | 594 } |
| 519 | 595 |
| 520 | 596 |
| 521 void Histogram::SampleSet::Accumulate(Sample value, Count count, | 597 void Histogram::SampleSet::Accumulate(Sample value, Count count, |
| 522 size_t index) { | 598 size_t index) { |
| 523 DCHECK(count == 1 || count == -1); | 599 DCHECK(count == 1 || count == -1); |
| 524 counts_[index] += count; | 600 counts_[index] += count; |
| 525 sum_ += count * value; | 601 sum_ += count * value; |
| 526 square_sum_ += (count * value) * static_cast<int64>(value); | 602 square_sum_ += (count * value) * static_cast<int64>(value); |
| 603 redundant_count_ += count; |
| 527 DCHECK_GE(counts_[index], 0); | 604 DCHECK_GE(counts_[index], 0); |
| 528 DCHECK_GE(sum_, 0); | 605 DCHECK_GE(sum_, 0); |
| 529 DCHECK_GE(square_sum_, 0); | 606 DCHECK_GE(square_sum_, 0); |
| 607 DCHECK_GE(redundant_count_, 0); |
| 530 } | 608 } |
| 531 | 609 |
| 532 Count Histogram::SampleSet::TotalCount() const { | 610 Count Histogram::SampleSet::TotalCount() const { |
| 533 Count total = 0; | 611 Count total = 0; |
| 534 for (Counts::const_iterator it = counts_.begin(); | 612 for (Counts::const_iterator it = counts_.begin(); |
| 535 it != counts_.end(); | 613 it != counts_.end(); |
| 536 ++it) { | 614 ++it) { |
| 537 total += *it; | 615 total += *it; |
| 538 } | 616 } |
| 617 DCHECK_EQ(total, redundant_count_); |
| 539 return total; | 618 return total; |
| 540 } | 619 } |
| 541 | 620 |
| 542 void Histogram::SampleSet::Add(const SampleSet& other) { | 621 void Histogram::SampleSet::Add(const SampleSet& other) { |
| 543 DCHECK_EQ(counts_.size(), other.counts_.size()); | 622 DCHECK_EQ(counts_.size(), other.counts_.size()); |
| 544 sum_ += other.sum_; | 623 sum_ += other.sum_; |
| 545 square_sum_ += other.square_sum_; | 624 square_sum_ += other.square_sum_; |
| 625 redundant_count_ += other.redundant_count_; |
| 546 for (size_t index = 0; index < counts_.size(); ++index) | 626 for (size_t index = 0; index < counts_.size(); ++index) |
| 547 counts_[index] += other.counts_[index]; | 627 counts_[index] += other.counts_[index]; |
| 548 } | 628 } |
| 549 | 629 |
| 550 void Histogram::SampleSet::Subtract(const SampleSet& other) { | 630 void Histogram::SampleSet::Subtract(const SampleSet& other) { |
| 551 DCHECK_EQ(counts_.size(), other.counts_.size()); | 631 DCHECK_EQ(counts_.size(), other.counts_.size()); |
| 552 // Note: Race conditions in snapshotting a sum or square_sum may lead to | 632 // Note: Race conditions in snapshotting a sum or square_sum may lead to |
| 553 // (temporary) negative values when snapshots are later combined (and deltas | 633 // (temporary) negative values when snapshots are later combined (and deltas |
| 554 // calculated). As a result, we don't currently CHCEK() for positive values. | 634 // calculated). As a result, we don't currently CHCEK() for positive values. |
| 555 sum_ -= other.sum_; | 635 sum_ -= other.sum_; |
| 556 square_sum_ -= other.square_sum_; | 636 square_sum_ -= other.square_sum_; |
| 637 redundant_count_ -= other.redundant_count_; |
| 557 for (size_t index = 0; index < counts_.size(); ++index) { | 638 for (size_t index = 0; index < counts_.size(); ++index) { |
| 558 counts_[index] -= other.counts_[index]; | 639 counts_[index] -= other.counts_[index]; |
| 559 DCHECK_GE(counts_[index], 0); | 640 DCHECK_GE(counts_[index], 0); |
| 560 } | 641 } |
| 561 } | 642 } |
| 562 | 643 |
| 563 bool Histogram::SampleSet::Serialize(Pickle* pickle) const { | 644 bool Histogram::SampleSet::Serialize(Pickle* pickle) const { |
| 564 pickle->WriteInt64(sum_); | 645 pickle->WriteInt64(sum_); |
| 565 pickle->WriteInt64(square_sum_); | 646 pickle->WriteInt64(square_sum_); |
| 647 pickle->WriteInt64(redundant_count_); |
| 566 pickle->WriteSize(counts_.size()); | 648 pickle->WriteSize(counts_.size()); |
| 567 | 649 |
| 568 for (size_t index = 0; index < counts_.size(); ++index) { | 650 for (size_t index = 0; index < counts_.size(); ++index) { |
| 569 pickle->WriteInt(counts_[index]); | 651 pickle->WriteInt(counts_[index]); |
| 570 } | 652 } |
| 571 | 653 |
| 572 return true; | 654 return true; |
| 573 } | 655 } |
| 574 | 656 |
| 575 bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { | 657 bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { |
| 576 DCHECK_EQ(counts_.size(), 0u); | 658 DCHECK_EQ(counts_.size(), 0u); |
| 577 DCHECK_EQ(sum_, 0); | 659 DCHECK_EQ(sum_, 0); |
| 578 DCHECK_EQ(square_sum_, 0); | 660 DCHECK_EQ(square_sum_, 0); |
| 661 DCHECK_EQ(redundant_count_, 0); |
| 579 | 662 |
| 580 size_t counts_size; | 663 size_t counts_size; |
| 581 | 664 |
| 582 if (!pickle.ReadInt64(iter, &sum_) || | 665 if (!pickle.ReadInt64(iter, &sum_) || |
| 583 !pickle.ReadInt64(iter, &square_sum_) || | 666 !pickle.ReadInt64(iter, &square_sum_) || |
| 667 !pickle.ReadInt64(iter, &redundant_count_) || |
| 584 !pickle.ReadSize(iter, &counts_size)) { | 668 !pickle.ReadSize(iter, &counts_size)) { |
| 585 return false; | 669 return false; |
| 586 } | 670 } |
| 587 | 671 |
| 588 if (counts_size == 0) | 672 if (counts_size == 0) |
| 589 return false; | 673 return false; |
| 590 | 674 |
| 675 int count = 0; |
| 591 for (size_t index = 0; index < counts_size; ++index) { | 676 for (size_t index = 0; index < counts_size; ++index) { |
| 592 int i; | 677 int i; |
| 593 if (!pickle.ReadInt(iter, &i)) | 678 if (!pickle.ReadInt(iter, &i)) |
| 594 return false; | 679 return false; |
| 595 counts_.push_back(i); | 680 counts_.push_back(i); |
| 681 count += i; |
| 596 } | 682 } |
| 597 | 683 DCHECK_EQ(count, redundant_count_); |
| 598 return true; | 684 return count == redundant_count_; |
| 599 } | 685 } |
| 600 | 686 |
| 601 //------------------------------------------------------------------------------ | 687 //------------------------------------------------------------------------------ |
| 602 // LinearHistogram: This histogram uses a traditional set of evenly spaced | 688 // LinearHistogram: This histogram uses a traditional set of evenly spaced |
| 603 // buckets. | 689 // buckets. |
| 604 //------------------------------------------------------------------------------ | 690 //------------------------------------------------------------------------------ |
| 605 | 691 |
| 606 scoped_refptr<Histogram> LinearHistogram::FactoryGet(const std::string& name, | 692 scoped_refptr<Histogram> LinearHistogram::FactoryGet(const std::string& name, |
| 607 Sample minimum, | 693 Sample minimum, |
| 608 Sample maximum, | 694 Sample maximum, |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 687 void LinearHistogram::InitializeBucketRange() { | 773 void LinearHistogram::InitializeBucketRange() { |
| 688 DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. | 774 DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. |
| 689 double min = declared_min(); | 775 double min = declared_min(); |
| 690 double max = declared_max(); | 776 double max = declared_max(); |
| 691 size_t i; | 777 size_t i; |
| 692 for (i = 1; i < bucket_count(); ++i) { | 778 for (i = 1; i < bucket_count(); ++i) { |
| 693 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / | 779 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / |
| 694 (bucket_count() - 2); | 780 (bucket_count() - 2); |
| 695 SetBucketRange(i, static_cast<int> (linear_range + 0.5)); | 781 SetBucketRange(i, static_cast<int> (linear_range + 0.5)); |
| 696 } | 782 } |
| 783 ResetRangeChecksum(); |
| 697 } | 784 } |
| 698 | 785 |
| 699 double LinearHistogram::GetBucketSize(Count current, size_t i) const { | 786 double LinearHistogram::GetBucketSize(Count current, size_t i) const { |
| 700 DCHECK_GT(ranges(i + 1), ranges(i)); | 787 DCHECK_GT(ranges(i + 1), ranges(i)); |
| 701 // Adjacent buckets with different widths would have "surprisingly" many (few) | 788 // Adjacent buckets with different widths would have "surprisingly" many (few) |
| 702 // samples in a histogram if we didn't normalize this way. | 789 // samples in a histogram if we didn't normalize this way. |
| 703 double denominator = ranges(i + 1) - ranges(i); | 790 double denominator = ranges(i + 1) - ranges(i); |
| 704 return current/denominator; | 791 return current/denominator; |
| 705 } | 792 } |
| 706 | 793 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 733 BooleanHistogram::BooleanHistogram(const std::string& name) | 820 BooleanHistogram::BooleanHistogram(const std::string& name) |
| 734 : LinearHistogram(name, 1, 2, 3) { | 821 : LinearHistogram(name, 1, 2, 3) { |
| 735 } | 822 } |
| 736 | 823 |
| 737 //------------------------------------------------------------------------------ | 824 //------------------------------------------------------------------------------ |
| 738 // CustomHistogram: | 825 // CustomHistogram: |
| 739 //------------------------------------------------------------------------------ | 826 //------------------------------------------------------------------------------ |
| 740 | 827 |
| 741 scoped_refptr<Histogram> CustomHistogram::FactoryGet( | 828 scoped_refptr<Histogram> CustomHistogram::FactoryGet( |
| 742 const std::string& name, | 829 const std::string& name, |
| 743 const std::vector<int>& custom_ranges, | 830 const std::vector<Sample>& custom_ranges, |
| 744 Flags flags) { | 831 Flags flags) { |
| 745 scoped_refptr<Histogram> histogram(NULL); | 832 scoped_refptr<Histogram> histogram(NULL); |
| 746 | 833 |
| 747 // Remove the duplicates in the custom ranges array. | 834 // Remove the duplicates in the custom ranges array. |
| 748 std::vector<int> ranges = custom_ranges; | 835 std::vector<int> ranges = custom_ranges; |
| 749 ranges.push_back(0); // Ensure we have a zero value. | 836 ranges.push_back(0); // Ensure we have a zero value. |
| 750 std::sort(ranges.begin(), ranges.end()); | 837 std::sort(ranges.begin(), ranges.end()); |
| 751 ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); | 838 ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); |
| 752 if (ranges.size() <= 1) { | 839 if (ranges.size() <= 1) { |
| 753 DCHECK(false); | 840 DCHECK(false); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 767 ranges.size())); | 854 ranges.size())); |
| 768 histogram->SetFlags(flags); | 855 histogram->SetFlags(flags); |
| 769 return histogram; | 856 return histogram; |
| 770 } | 857 } |
| 771 | 858 |
| 772 Histogram::ClassType CustomHistogram::histogram_type() const { | 859 Histogram::ClassType CustomHistogram::histogram_type() const { |
| 773 return CUSTOM_HISTOGRAM; | 860 return CUSTOM_HISTOGRAM; |
| 774 } | 861 } |
| 775 | 862 |
| 776 CustomHistogram::CustomHistogram(const std::string& name, | 863 CustomHistogram::CustomHistogram(const std::string& name, |
| 777 const std::vector<int>& custom_ranges) | 864 const std::vector<Sample>& custom_ranges) |
| 778 : Histogram(name, custom_ranges[1], custom_ranges.back(), | 865 : Histogram(name, custom_ranges[1], custom_ranges.back(), |
| 779 custom_ranges.size()) { | 866 custom_ranges.size()) { |
| 780 DCHECK_GT(custom_ranges.size(), 1u); | 867 DCHECK_GT(custom_ranges.size(), 1u); |
| 781 DCHECK_EQ(custom_ranges[0], 0); | 868 DCHECK_EQ(custom_ranges[0], 0); |
| 782 ranges_vector_ = &custom_ranges; | 869 ranges_vector_ = &custom_ranges; |
| 783 InitializeBucketRange(); | 870 InitializeBucketRange(); |
| 784 ranges_vector_ = NULL; | 871 ranges_vector_ = NULL; |
| 785 DCHECK(ValidateBucketRanges()); | 872 DCHECK(ValidateBucketRanges()); |
| 786 } | 873 } |
| 787 | 874 |
| 788 void CustomHistogram::InitializeBucketRange() { | 875 void CustomHistogram::InitializeBucketRange() { |
| 789 DCHECK_LE(ranges_vector_->size(), bucket_count()); | 876 DCHECK_LE(ranges_vector_->size(), bucket_count()); |
| 790 for (size_t index = 0; index < ranges_vector_->size(); ++index) | 877 for (size_t index = 0; index < ranges_vector_->size(); ++index) |
| 791 SetBucketRange(index, (*ranges_vector_)[index]); | 878 SetBucketRange(index, (*ranges_vector_)[index]); |
| 879 ResetRangeChecksum(); |
| 792 } | 880 } |
| 793 | 881 |
| 794 double CustomHistogram::GetBucketSize(Count current, size_t i) const { | 882 double CustomHistogram::GetBucketSize(Count current, size_t i) const { |
| 795 return 1; | 883 return 1; |
| 796 } | 884 } |
| 797 | 885 |
| 798 //------------------------------------------------------------------------------ | 886 //------------------------------------------------------------------------------ |
| 799 // The next section handles global (central) support for all histograms, as well | 887 // The next section handles global (central) support for all histograms, as well |
| 800 // as startup/teardown of this service. | 888 // as startup/teardown of this service. |
| 801 //------------------------------------------------------------------------------ | 889 //------------------------------------------------------------------------------ |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 927 } | 1015 } |
| 928 | 1016 |
| 929 // static | 1017 // static |
| 930 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; | 1018 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; |
| 931 // static | 1019 // static |
| 932 Lock* StatisticsRecorder::lock_ = NULL; | 1020 Lock* StatisticsRecorder::lock_ = NULL; |
| 933 // static | 1021 // static |
| 934 bool StatisticsRecorder::dump_on_exit_ = false; | 1022 bool StatisticsRecorder::dump_on_exit_ = false; |
| 935 | 1023 |
| 936 } // namespace base | 1024 } // namespace base |
| OLD | NEW |