| 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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 46 | 46 |
| 47 scoped_refptr<Histogram> Histogram::FactoryTimeGet(const std::string& name, | 47 scoped_refptr<Histogram> Histogram::FactoryTimeGet(const std::string& name, |
| 48 TimeDelta minimum, | 48 TimeDelta minimum, |
| 49 TimeDelta maximum, | 49 TimeDelta maximum, |
| 50 size_t bucket_count, | 50 size_t bucket_count, |
| 51 Flags flags) { | 51 Flags flags) { |
| 52 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), | 52 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), |
| 53 bucket_count, flags); | 53 bucket_count, flags); |
| 54 } | 54 } |
| 55 | 55 |
| 56 Histogram::Histogram(const std::string& name, Sample minimum, | |
| 57 Sample maximum, size_t bucket_count) | |
| 58 : histogram_name_(name), | |
| 59 declared_min_(minimum), | |
| 60 declared_max_(maximum), | |
| 61 bucket_count_(bucket_count), | |
| 62 flags_(kNoFlags), | |
| 63 ranges_(bucket_count + 1, 0), | |
| 64 range_checksum_(0), | |
| 65 sample_() { | |
| 66 Initialize(); | |
| 67 } | |
| 68 | |
| 69 Histogram::Histogram(const std::string& name, TimeDelta minimum, | |
| 70 TimeDelta maximum, size_t bucket_count) | |
| 71 : histogram_name_(name), | |
| 72 declared_min_(static_cast<int> (minimum.InMilliseconds())), | |
| 73 declared_max_(static_cast<int> (maximum.InMilliseconds())), | |
| 74 bucket_count_(bucket_count), | |
| 75 flags_(kNoFlags), | |
| 76 ranges_(bucket_count + 1, 0), | |
| 77 range_checksum_(0), | |
| 78 sample_() { | |
| 79 Initialize(); | |
| 80 } | |
| 81 | |
| 82 Histogram::~Histogram() { | |
| 83 if (StatisticsRecorder::dump_on_exit()) { | |
| 84 std::string output; | |
| 85 WriteAscii(true, "\n", &output); | |
| 86 LOG(INFO) << output; | |
| 87 } | |
| 88 | |
| 89 // Just to make sure most derived class did this properly... | |
| 90 DCHECK(ValidateBucketRanges()); | |
| 91 DCHECK(HasValidRangeChecksum()); | |
| 92 } | |
| 93 | |
| 94 bool Histogram::PrintEmptyBucket(size_t index) const { | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 void Histogram::Add(int value) { | 56 void Histogram::Add(int value) { |
| 99 if (value > kSampleType_MAX - 1) | 57 if (value > kSampleType_MAX - 1) |
| 100 value = kSampleType_MAX - 1; | 58 value = kSampleType_MAX - 1; |
| 101 if (value < 0) | 59 if (value < 0) |
| 102 value = 0; | 60 value = 0; |
| 103 size_t index = BucketIndex(value); | 61 size_t index = BucketIndex(value); |
| 104 DCHECK_GE(value, ranges(index)); | 62 DCHECK_GE(value, ranges(index)); |
| 105 DCHECK_LT(value, ranges(index + 1)); | 63 DCHECK_LT(value, ranges(index + 1)); |
| 106 Accumulate(value, 1, index); | 64 Accumulate(value, 1, index); |
| 107 } | 65 } |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 double current_size = GetBucketSize(current, i); | 141 double current_size = GetBucketSize(current, i); |
| 184 if (graph_it) | 142 if (graph_it) |
| 185 WriteAsciiBucketGraph(current_size, max_size, output); | 143 WriteAsciiBucketGraph(current_size, max_size, output); |
| 186 WriteAsciiBucketContext(past, current, remaining, i, output); | 144 WriteAsciiBucketContext(past, current, remaining, i, output); |
| 187 output->append(newline); | 145 output->append(newline); |
| 188 past += current; | 146 past += current; |
| 189 } | 147 } |
| 190 DCHECK_EQ(sample_count, past); | 148 DCHECK_EQ(sample_count, past); |
| 191 } | 149 } |
| 192 | 150 |
| 193 bool Histogram::ValidateBucketRanges() const { | 151 // static |
| 194 // Standard assertions that all bucket ranges should satisfy. | 152 std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, |
| 195 DCHECK_EQ(bucket_count_ + 1, ranges_.size()); | 153 const SampleSet& snapshot) { |
| 196 DCHECK_EQ(0, ranges_[0]); | 154 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); |
| 197 DCHECK_EQ(declared_min(), ranges_[1]); | 155 |
| 198 DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); | 156 Pickle pickle; |
| 199 DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); | 157 pickle.WriteString(histogram.histogram_name()); |
| 158 pickle.WriteInt(histogram.declared_min()); |
| 159 pickle.WriteInt(histogram.declared_max()); |
| 160 pickle.WriteSize(histogram.bucket_count()); |
| 161 pickle.WriteInt(histogram.range_checksum()); |
| 162 pickle.WriteInt(histogram.histogram_type()); |
| 163 pickle.WriteInt(histogram.flags()); |
| 164 |
| 165 snapshot.Serialize(&pickle); |
| 166 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); |
| 167 } |
| 168 |
| 169 // static |
| 170 bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { |
| 171 if (histogram_info.empty()) { |
| 172 return false; |
| 173 } |
| 174 |
| 175 Pickle pickle(histogram_info.data(), |
| 176 static_cast<int>(histogram_info.size())); |
| 177 std::string histogram_name; |
| 178 int declared_min; |
| 179 int declared_max; |
| 180 size_t bucket_count; |
| 181 int range_checksum; |
| 182 int histogram_type; |
| 183 int pickle_flags; |
| 184 SampleSet sample; |
| 185 |
| 186 void* iter = NULL; |
| 187 if (!pickle.ReadString(&iter, &histogram_name) || |
| 188 !pickle.ReadInt(&iter, &declared_min) || |
| 189 !pickle.ReadInt(&iter, &declared_max) || |
| 190 !pickle.ReadSize(&iter, &bucket_count) || |
| 191 !pickle.ReadInt(&iter, &range_checksum) || |
| 192 !pickle.ReadInt(&iter, &histogram_type) || |
| 193 !pickle.ReadInt(&iter, &pickle_flags) || |
| 194 !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { |
| 195 LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; |
| 196 return false; |
| 197 } |
| 198 DCHECK(pickle_flags & kIPCSerializationSourceFlag); |
| 199 // Since these fields may have come from an untrusted renderer, do additional |
| 200 // checks above and beyond those in Histogram::Initialize() |
| 201 if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || |
| 202 INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { |
| 203 LOG(ERROR) << "Values error decoding Histogram: " << histogram_name; |
| 204 return false; |
| 205 } |
| 206 |
| 207 Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); |
| 208 |
| 209 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type); |
| 210 |
| 211 scoped_refptr<Histogram> render_histogram(NULL); |
| 212 |
| 213 if (histogram_type == HISTOGRAM) { |
| 214 render_histogram = Histogram::FactoryGet( |
| 215 histogram_name, declared_min, declared_max, bucket_count, flags); |
| 216 } else if (histogram_type == LINEAR_HISTOGRAM) { |
| 217 render_histogram = LinearHistogram::FactoryGet( |
| 218 histogram_name, declared_min, declared_max, bucket_count, flags); |
| 219 } else if (histogram_type == BOOLEAN_HISTOGRAM) { |
| 220 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); |
| 221 } else { |
| 222 LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " |
| 223 << histogram_type; |
| 224 return false; |
| 225 } |
| 226 |
| 227 DCHECK_EQ(render_histogram->declared_min(), declared_min); |
| 228 DCHECK_EQ(render_histogram->declared_max(), declared_max); |
| 229 DCHECK_EQ(render_histogram->bucket_count(), bucket_count); |
| 230 DCHECK_EQ(render_histogram->range_checksum(), range_checksum); |
| 231 DCHECK_EQ(render_histogram->histogram_type(), histogram_type); |
| 232 |
| 233 if (render_histogram->flags() & kIPCSerializationSourceFlag) { |
| 234 DVLOG(1) << "Single process mode, histogram observed and not copied: " |
| 235 << histogram_name; |
| 236 } else { |
| 237 DCHECK_EQ(flags & render_histogram->flags(), flags); |
| 238 render_histogram->AddSampleSet(sample); |
| 239 } |
| 240 |
| 200 return true; | 241 return true; |
| 201 } | 242 } |
| 202 | 243 |
| 203 void Histogram::Initialize() { | 244 //------------------------------------------------------------------------------ |
| 204 sample_.Resize(*this); | 245 // Methods for the validating a sample and a related histogram. |
| 205 if (declared_min_ < 1) | 246 //------------------------------------------------------------------------------ |
| 206 declared_min_ = 1; | 247 |
| 207 if (declared_max_ > kSampleType_MAX - 1) | 248 Histogram::Inconsistencies Histogram::FindCorruption( |
| 208 declared_max_ = kSampleType_MAX - 1; | 249 const SampleSet& snapshot) const { |
| 209 DCHECK_LE(declared_min_, declared_max_); | 250 int inconsistencies = NO_INCONSISTENCIES; |
| 210 DCHECK_GT(bucket_count_, 1u); | 251 Sample previous_range = -1; // Bottom range is always 0. |
| 211 size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; | 252 Sample checksum = 0; |
| 212 DCHECK_LE(bucket_count_, maximal_bucket_count); | 253 int64 count = 0; |
| 213 DCHECK_EQ(0, ranges_[0]); | 254 for (size_t index = 0; index < bucket_count(); ++index) { |
| 214 ranges_[bucket_count_] = kSampleType_MAX; | 255 count += snapshot.counts(index); |
| 215 InitializeBucketRange(); | 256 int new_range = ranges(index); |
| 257 checksum += new_range; |
| 258 if (previous_range >= new_range) |
| 259 inconsistencies |= BUCKET_ORDER_ERROR; |
| 260 previous_range = new_range; |
| 261 } |
| 262 |
| 263 if (checksum != range_checksum_) |
| 264 inconsistencies |= RANGE_CHECKSUM_ERROR; |
| 265 |
| 266 int64 delta64 = snapshot.redundant_count() - count; |
| 267 if (delta64 != 0) { |
| 268 int delta = static_cast<int>(delta64); |
| 269 if (delta != delta64) |
| 270 delta = INT_MAX; // Flag all giant errors as INT_MAX. |
| 271 // Since snapshots of histograms are taken asynchronously relative to |
| 272 // sampling (and snapped from different threads), it is pretty likely that |
| 273 // we'll catch a redundant count that doesn't match the sample count. We |
| 274 // allow for a certain amount of slop before flagging this as an |
| 275 // inconsistency. Even with an inconsistency, we'll snapshot it again (for |
| 276 // UMA in about a half hour, so we'll eventually get the data, if it was |
| 277 // not the result of a corruption. If histograms show that 1 is "too tight" |
| 278 // then we may try to use 2 or 3 for this slop value. |
| 279 const int kCommonRaceBasedCountMismatch = 1; |
| 280 if (delta > 0) { |
| 281 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); |
| 282 if (delta > kCommonRaceBasedCountMismatch) |
| 283 inconsistencies |= COUNT_HIGH_ERROR; |
| 284 } else { |
| 285 DCHECK_GT(0, delta); |
| 286 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); |
| 287 if (-delta > kCommonRaceBasedCountMismatch) |
| 288 inconsistencies |= COUNT_LOW_ERROR; |
| 289 } |
| 290 } |
| 291 return static_cast<Inconsistencies>(inconsistencies); |
| 292 } |
| 293 |
| 294 Histogram::ClassType Histogram::histogram_type() const { |
| 295 return HISTOGRAM; |
| 296 } |
| 297 |
| 298 Histogram::Sample Histogram::ranges(size_t i) const { |
| 299 return ranges_[i]; |
| 300 } |
| 301 |
| 302 size_t Histogram::bucket_count() const { |
| 303 return bucket_count_; |
| 304 } |
| 305 |
| 306 // Do a safe atomic snapshot of sample data. |
| 307 // This implementation assumes we are on a safe single thread. |
| 308 void Histogram::SnapshotSample(SampleSet* sample) const { |
| 309 // Note locking not done in this version!!! |
| 310 *sample = sample_; |
| 311 } |
| 312 |
| 313 bool Histogram::HasConstructorArguments(Sample minimum, |
| 314 Sample maximum, |
| 315 size_t bucket_count) { |
| 316 return ((minimum == declared_min_) && (maximum == declared_max_) && |
| 317 (bucket_count == bucket_count_)); |
| 318 } |
| 319 |
| 320 bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, |
| 321 TimeDelta maximum, |
| 322 size_t bucket_count) { |
| 323 return ((minimum.InMilliseconds() == declared_min_) && |
| 324 (maximum.InMilliseconds() == declared_max_) && |
| 325 (bucket_count == bucket_count_)); |
| 326 } |
| 327 |
| 328 Histogram::Histogram(const std::string& name, Sample minimum, |
| 329 Sample maximum, size_t bucket_count) |
| 330 : histogram_name_(name), |
| 331 declared_min_(minimum), |
| 332 declared_max_(maximum), |
| 333 bucket_count_(bucket_count), |
| 334 flags_(kNoFlags), |
| 335 ranges_(bucket_count + 1, 0), |
| 336 range_checksum_(0), |
| 337 sample_() { |
| 338 Initialize(); |
| 339 } |
| 340 |
| 341 Histogram::Histogram(const std::string& name, TimeDelta minimum, |
| 342 TimeDelta maximum, size_t bucket_count) |
| 343 : histogram_name_(name), |
| 344 declared_min_(static_cast<int> (minimum.InMilliseconds())), |
| 345 declared_max_(static_cast<int> (maximum.InMilliseconds())), |
| 346 bucket_count_(bucket_count), |
| 347 flags_(kNoFlags), |
| 348 ranges_(bucket_count + 1, 0), |
| 349 range_checksum_(0), |
| 350 sample_() { |
| 351 Initialize(); |
| 352 } |
| 353 |
| 354 Histogram::~Histogram() { |
| 355 if (StatisticsRecorder::dump_on_exit()) { |
| 356 std::string output; |
| 357 WriteAscii(true, "\n", &output); |
| 358 LOG(INFO) << output; |
| 359 } |
| 360 |
| 361 // Just to make sure most derived class did this properly... |
| 216 DCHECK(ValidateBucketRanges()); | 362 DCHECK(ValidateBucketRanges()); |
| 217 StatisticsRecorder::Register(this); | 363 DCHECK(HasValidRangeChecksum()); |
| 364 } |
| 365 |
| 366 bool Histogram::PrintEmptyBucket(size_t index) const { |
| 367 return true; |
| 218 } | 368 } |
| 219 | 369 |
| 220 // Calculate what range of values are held in each bucket. | 370 // Calculate what range of values are held in each bucket. |
| 221 // We have to be careful that we don't pick a ratio between starting points in | 371 // We have to be careful that we don't pick a ratio between starting points in |
| 222 // consecutive buckets that is sooo small, that the integer bounds are the same | 372 // consecutive buckets that is sooo small, that the integer bounds are the same |
| 223 // (effectively making one bucket get no values). We need to avoid: | 373 // (effectively making one bucket get no values). We need to avoid: |
| 224 // ranges_[i] == ranges_[i + 1] | 374 // ranges_[i] == ranges_[i + 1] |
| 225 // To avoid that, we just do a fine-grained bucket width as far as we need to | 375 // To avoid that, we just do a fine-grained bucket width as far as we need to |
| 226 // until we get a ratio that moves us along at least 2 units at a time. From | 376 // until we get a ratio that moves us along at least 2 units at a time. From |
| 227 // that bucket onward we do use the exponential growth of buckets. | 377 // that bucket onward we do use the exponential growth of buckets. |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 288 double denominator = ranges(i + 1) - ranges(i); | 438 double denominator = ranges(i + 1) - ranges(i); |
| 289 if (denominator > kTransitionWidth) | 439 if (denominator > kTransitionWidth) |
| 290 denominator = kTransitionWidth; // Stop trying to normalize. | 440 denominator = kTransitionWidth; // Stop trying to normalize. |
| 291 return current/denominator; | 441 return current/denominator; |
| 292 } | 442 } |
| 293 | 443 |
| 294 void Histogram::ResetRangeChecksum() { | 444 void Histogram::ResetRangeChecksum() { |
| 295 range_checksum_ = CalculateRangeChecksum(); | 445 range_checksum_ = CalculateRangeChecksum(); |
| 296 } | 446 } |
| 297 | 447 |
| 448 const std::string Histogram::GetAsciiBucketRange(size_t i) const { |
| 449 std::string result; |
| 450 if (kHexRangePrintingFlag & flags_) |
| 451 StringAppendF(&result, "%#x", ranges(i)); |
| 452 else |
| 453 StringAppendF(&result, "%d", ranges(i)); |
| 454 return result; |
| 455 } |
| 456 |
| 457 // Update histogram data with new sample. |
| 458 void Histogram::Accumulate(Sample value, Count count, size_t index) { |
| 459 // Note locking not done in this version!!! |
| 460 sample_.Accumulate(value, count, index); |
| 461 } |
| 462 |
| 463 void Histogram::SetBucketRange(size_t i, Sample value) { |
| 464 DCHECK_GT(bucket_count_, i); |
| 465 ranges_[i] = value; |
| 466 } |
| 467 |
| 468 bool Histogram::ValidateBucketRanges() const { |
| 469 // Standard assertions that all bucket ranges should satisfy. |
| 470 DCHECK_EQ(bucket_count_ + 1, ranges_.size()); |
| 471 DCHECK_EQ(0, ranges_[0]); |
| 472 DCHECK_EQ(declared_min(), ranges_[1]); |
| 473 DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); |
| 474 DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); |
| 475 return true; |
| 476 } |
| 477 |
| 478 void Histogram::Initialize() { |
| 479 sample_.Resize(*this); |
| 480 if (declared_min_ < 1) |
| 481 declared_min_ = 1; |
| 482 if (declared_max_ > kSampleType_MAX - 1) |
| 483 declared_max_ = kSampleType_MAX - 1; |
| 484 DCHECK_LE(declared_min_, declared_max_); |
| 485 DCHECK_GT(bucket_count_, 1u); |
| 486 size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; |
| 487 DCHECK_LE(bucket_count_, maximal_bucket_count); |
| 488 DCHECK_EQ(0, ranges_[0]); |
| 489 ranges_[bucket_count_] = kSampleType_MAX; |
| 490 InitializeBucketRange(); |
| 491 DCHECK(ValidateBucketRanges()); |
| 492 StatisticsRecorder::Register(this); |
| 493 } |
| 494 |
| 298 bool Histogram::HasValidRangeChecksum() const { | 495 bool Histogram::HasValidRangeChecksum() const { |
| 299 return CalculateRangeChecksum() == range_checksum_; | 496 return CalculateRangeChecksum() == range_checksum_; |
| 300 } | 497 } |
| 301 | 498 |
| 302 Histogram::Sample Histogram::CalculateRangeChecksum() const { | 499 Histogram::Sample Histogram::CalculateRangeChecksum() const { |
| 303 DCHECK_EQ(ranges_.size(), bucket_count() + 1); | 500 DCHECK_EQ(ranges_.size(), bucket_count() + 1); |
| 304 Sample checksum = 0; | 501 Sample checksum = 0; |
| 305 for (size_t index = 0; index < bucket_count(); ++index) { | 502 for (size_t index = 0; index < bucket_count(); ++index) { |
| 306 checksum += ranges(index); | 503 checksum += ranges(index); |
| 307 } | 504 } |
| 308 return checksum; | 505 return checksum; |
| 309 } | 506 } |
| 310 | 507 |
| 311 //------------------------------------------------------------------------------ | 508 //------------------------------------------------------------------------------ |
| 312 // The following two methods can be overridden to provide a thread safe | |
| 313 // version of this class. The cost of locking is low... but an error in each | |
| 314 // of these methods has minimal impact. For now, I'll leave this unlocked, | |
| 315 // and I don't believe I can loose more than a count or two. | |
| 316 // The vectors are NOT reallocated, so there is no risk of them moving around. | |
| 317 | |
| 318 // Update histogram data with new sample. | |
| 319 void Histogram::Accumulate(Sample value, Count count, size_t index) { | |
| 320 // Note locking not done in this version!!! | |
| 321 sample_.Accumulate(value, count, index); | |
| 322 } | |
| 323 | |
| 324 // Do a safe atomic snapshot of sample data. | |
| 325 // This implementation assumes we are on a safe single thread. | |
| 326 void Histogram::SnapshotSample(SampleSet* sample) const { | |
| 327 // Note locking not done in this version!!! | |
| 328 *sample = sample_; | |
| 329 } | |
| 330 | |
| 331 bool Histogram::HasConstructorArguments(Sample minimum, | |
| 332 Sample maximum, | |
| 333 size_t bucket_count) { | |
| 334 return ((minimum == declared_min_) && (maximum == declared_max_) && | |
| 335 (bucket_count == bucket_count_)); | |
| 336 } | |
| 337 | |
| 338 bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, | |
| 339 TimeDelta maximum, | |
| 340 size_t bucket_count) { | |
| 341 return ((minimum.InMilliseconds() == declared_min_) && | |
| 342 (maximum.InMilliseconds() == declared_max_) && | |
| 343 (bucket_count == bucket_count_)); | |
| 344 } | |
| 345 | |
| 346 //------------------------------------------------------------------------------ | |
| 347 // Accessor methods | |
| 348 | |
| 349 void Histogram::SetBucketRange(size_t i, Sample value) { | |
| 350 DCHECK_GT(bucket_count_, i); | |
| 351 ranges_[i] = value; | |
| 352 } | |
| 353 | |
| 354 //------------------------------------------------------------------------------ | |
| 355 // Private methods | 509 // Private methods |
| 356 | 510 |
| 357 double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { | 511 double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { |
| 358 double max = 0; | 512 double max = 0; |
| 359 for (size_t i = 0; i < bucket_count() ; ++i) { | 513 for (size_t i = 0; i < bucket_count() ; ++i) { |
| 360 double current_size = GetBucketSize(snapshot.counts(i), i); | 514 double current_size = GetBucketSize(snapshot.counts(i), i); |
| 361 if (current_size > max) | 515 if (current_size > max) |
| 362 max = current_size; | 516 max = current_size; |
| 363 } | 517 } |
| 364 return max; | 518 return max; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 393 const size_t i, | 547 const size_t i, |
| 394 std::string* output) const { | 548 std::string* output) const { |
| 395 double scaled_sum = (past + current + remaining) / 100.0; | 549 double scaled_sum = (past + current + remaining) / 100.0; |
| 396 WriteAsciiBucketValue(current, scaled_sum, output); | 550 WriteAsciiBucketValue(current, scaled_sum, output); |
| 397 if (0 < i) { | 551 if (0 < i) { |
| 398 double percentage = past / scaled_sum; | 552 double percentage = past / scaled_sum; |
| 399 StringAppendF(output, " {%3.1f%%}", percentage); | 553 StringAppendF(output, " {%3.1f%%}", percentage); |
| 400 } | 554 } |
| 401 } | 555 } |
| 402 | 556 |
| 403 const std::string Histogram::GetAsciiBucketRange(size_t i) const { | |
| 404 std::string result; | |
| 405 if (kHexRangePrintingFlag & flags_) | |
| 406 StringAppendF(&result, "%#x", ranges(i)); | |
| 407 else | |
| 408 StringAppendF(&result, "%d", ranges(i)); | |
| 409 return result; | |
| 410 } | |
| 411 | |
| 412 void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, | 557 void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, |
| 413 std::string* output) const { | 558 std::string* output) const { |
| 414 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); | 559 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); |
| 415 } | 560 } |
| 416 | 561 |
| 417 void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, | 562 void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, |
| 418 std::string* output) const { | 563 std::string* output) const { |
| 419 const int k_line_length = 72; // Maximal horizontal width of graph. | 564 const int k_line_length = 72; // Maximal horizontal width of graph. |
| 420 int x_count = static_cast<int>(k_line_length * (current_size / max_size) | 565 int x_count = static_cast<int>(k_line_length * (current_size / max_size) |
| 421 + 0.5); | 566 + 0.5); |
| 422 int x_remainder = k_line_length - x_count; | 567 int x_remainder = k_line_length - x_count; |
| 423 | 568 |
| 424 while (0 < x_count--) | 569 while (0 < x_count--) |
| 425 output->append("-"); | 570 output->append("-"); |
| 426 output->append("O"); | 571 output->append("O"); |
| 427 while (0 < x_remainder--) | 572 while (0 < x_remainder--) |
| 428 output->append(" "); | 573 output->append(" "); |
| 429 } | 574 } |
| 430 | 575 |
| 431 // static | |
| 432 std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, | |
| 433 const SampleSet& snapshot) { | |
| 434 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); | |
| 435 | |
| 436 Pickle pickle; | |
| 437 pickle.WriteString(histogram.histogram_name()); | |
| 438 pickle.WriteInt(histogram.declared_min()); | |
| 439 pickle.WriteInt(histogram.declared_max()); | |
| 440 pickle.WriteSize(histogram.bucket_count()); | |
| 441 pickle.WriteInt(histogram.range_checksum()); | |
| 442 pickle.WriteInt(histogram.histogram_type()); | |
| 443 pickle.WriteInt(histogram.flags()); | |
| 444 | |
| 445 snapshot.Serialize(&pickle); | |
| 446 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); | |
| 447 } | |
| 448 | |
| 449 // static | |
| 450 bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { | |
| 451 if (histogram_info.empty()) { | |
| 452 return false; | |
| 453 } | |
| 454 | |
| 455 Pickle pickle(histogram_info.data(), | |
| 456 static_cast<int>(histogram_info.size())); | |
| 457 std::string histogram_name; | |
| 458 int declared_min; | |
| 459 int declared_max; | |
| 460 size_t bucket_count; | |
| 461 int range_checksum; | |
| 462 int histogram_type; | |
| 463 int pickle_flags; | |
| 464 SampleSet sample; | |
| 465 | |
| 466 void* iter = NULL; | |
| 467 if (!pickle.ReadString(&iter, &histogram_name) || | |
| 468 !pickle.ReadInt(&iter, &declared_min) || | |
| 469 !pickle.ReadInt(&iter, &declared_max) || | |
| 470 !pickle.ReadSize(&iter, &bucket_count) || | |
| 471 !pickle.ReadInt(&iter, &range_checksum) || | |
| 472 !pickle.ReadInt(&iter, &histogram_type) || | |
| 473 !pickle.ReadInt(&iter, &pickle_flags) || | |
| 474 !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { | |
| 475 LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; | |
| 476 return false; | |
| 477 } | |
| 478 DCHECK(pickle_flags & kIPCSerializationSourceFlag); | |
| 479 // Since these fields may have come from an untrusted renderer, do additional | |
| 480 // checks above and beyond those in Histogram::Initialize() | |
| 481 if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || | |
| 482 INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { | |
| 483 LOG(ERROR) << "Values error decoding Histogram: " << histogram_name; | |
| 484 return false; | |
| 485 } | |
| 486 | |
| 487 Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); | |
| 488 | |
| 489 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type); | |
| 490 | |
| 491 scoped_refptr<Histogram> render_histogram(NULL); | |
| 492 | |
| 493 if (histogram_type == HISTOGRAM) { | |
| 494 render_histogram = Histogram::FactoryGet( | |
| 495 histogram_name, declared_min, declared_max, bucket_count, flags); | |
| 496 } else if (histogram_type == LINEAR_HISTOGRAM) { | |
| 497 render_histogram = LinearHistogram::FactoryGet( | |
| 498 histogram_name, declared_min, declared_max, bucket_count, flags); | |
| 499 } else if (histogram_type == BOOLEAN_HISTOGRAM) { | |
| 500 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); | |
| 501 } else { | |
| 502 LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " | |
| 503 << histogram_type; | |
| 504 return false; | |
| 505 } | |
| 506 | |
| 507 DCHECK_EQ(render_histogram->declared_min(), declared_min); | |
| 508 DCHECK_EQ(render_histogram->declared_max(), declared_max); | |
| 509 DCHECK_EQ(render_histogram->bucket_count(), bucket_count); | |
| 510 DCHECK_EQ(render_histogram->range_checksum(), range_checksum); | |
| 511 DCHECK_EQ(render_histogram->histogram_type(), histogram_type); | |
| 512 | |
| 513 if (render_histogram->flags() & kIPCSerializationSourceFlag) { | |
| 514 DVLOG(1) << "Single process mode, histogram observed and not copied: " | |
| 515 << histogram_name; | |
| 516 } else { | |
| 517 DCHECK_EQ(flags & render_histogram->flags(), flags); | |
| 518 render_histogram->AddSampleSet(sample); | |
| 519 } | |
| 520 | |
| 521 return true; | |
| 522 } | |
| 523 | |
| 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 Histogram::ClassType Histogram::histogram_type() const { | |
| 575 return HISTOGRAM; | |
| 576 } | |
| 577 | |
| 578 Histogram::Sample Histogram::ranges(size_t i) const { | |
| 579 return ranges_[i]; | |
| 580 } | |
| 581 | |
| 582 size_t Histogram::bucket_count() const { | |
| 583 return bucket_count_; | |
| 584 } | |
| 585 | |
| 586 //------------------------------------------------------------------------------ | 576 //------------------------------------------------------------------------------ |
| 587 // Methods for the Histogram::SampleSet class | 577 // Methods for the Histogram::SampleSet class |
| 588 //------------------------------------------------------------------------------ | 578 //------------------------------------------------------------------------------ |
| 589 | 579 |
| 590 Histogram::SampleSet::SampleSet() | 580 Histogram::SampleSet::SampleSet() |
| 591 : counts_(), | 581 : counts_(), |
| 592 sum_(0), | 582 sum_(0), |
| 593 square_sum_(0), | 583 square_sum_(0), |
| 594 redundant_count_(0) { | 584 redundant_count_(0) { |
| 595 } | 585 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 693 } | 683 } |
| 694 DCHECK_EQ(count, redundant_count_); | 684 DCHECK_EQ(count, redundant_count_); |
| 695 return count == redundant_count_; | 685 return count == redundant_count_; |
| 696 } | 686 } |
| 697 | 687 |
| 698 //------------------------------------------------------------------------------ | 688 //------------------------------------------------------------------------------ |
| 699 // LinearHistogram: This histogram uses a traditional set of evenly spaced | 689 // LinearHistogram: This histogram uses a traditional set of evenly spaced |
| 700 // buckets. | 690 // buckets. |
| 701 //------------------------------------------------------------------------------ | 691 //------------------------------------------------------------------------------ |
| 702 | 692 |
| 693 LinearHistogram::~LinearHistogram() { |
| 694 } |
| 695 |
| 703 scoped_refptr<Histogram> LinearHistogram::FactoryGet(const std::string& name, | 696 scoped_refptr<Histogram> LinearHistogram::FactoryGet(const std::string& name, |
| 704 Sample minimum, | 697 Sample minimum, |
| 705 Sample maximum, | 698 Sample maximum, |
| 706 size_t bucket_count, | 699 size_t bucket_count, |
| 707 Flags flags) { | 700 Flags flags) { |
| 708 scoped_refptr<Histogram> histogram(NULL); | 701 scoped_refptr<Histogram> histogram(NULL); |
| 709 | 702 |
| 710 if (minimum < 1) | 703 if (minimum < 1) |
| 711 minimum = 1; | 704 minimum = 1; |
| 712 if (maximum > kSampleType_MAX - 1) | 705 if (maximum > kSampleType_MAX - 1) |
| (...skipping 13 matching lines...) Expand all Loading... |
| 726 scoped_refptr<Histogram> LinearHistogram::FactoryTimeGet( | 719 scoped_refptr<Histogram> LinearHistogram::FactoryTimeGet( |
| 727 const std::string& name, | 720 const std::string& name, |
| 728 TimeDelta minimum, | 721 TimeDelta minimum, |
| 729 TimeDelta maximum, | 722 TimeDelta maximum, |
| 730 size_t bucket_count, | 723 size_t bucket_count, |
| 731 Flags flags) { | 724 Flags flags) { |
| 732 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), | 725 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), |
| 733 bucket_count, flags); | 726 bucket_count, flags); |
| 734 } | 727 } |
| 735 | 728 |
| 736 LinearHistogram::~LinearHistogram() { | 729 Histogram::ClassType LinearHistogram::histogram_type() const { |
| 730 return LINEAR_HISTOGRAM; |
| 731 } |
| 732 |
| 733 void LinearHistogram::SetRangeDescriptions( |
| 734 const DescriptionPair descriptions[]) { |
| 735 for (int i =0; descriptions[i].description; ++i) { |
| 736 bucket_description_[descriptions[i].sample] = descriptions[i].description; |
| 737 } |
| 737 } | 738 } |
| 738 | 739 |
| 739 LinearHistogram::LinearHistogram(const std::string& name, | 740 LinearHistogram::LinearHistogram(const std::string& name, |
| 740 Sample minimum, | 741 Sample minimum, |
| 741 Sample maximum, | 742 Sample maximum, |
| 742 size_t bucket_count) | 743 size_t bucket_count) |
| 743 : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { | 744 : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { |
| 744 InitializeBucketRange(); | 745 InitializeBucketRange(); |
| 745 DCHECK(ValidateBucketRanges()); | 746 DCHECK(ValidateBucketRanges()); |
| 746 } | 747 } |
| 747 | 748 |
| 748 LinearHistogram::LinearHistogram(const std::string& name, | 749 LinearHistogram::LinearHistogram(const std::string& name, |
| 749 TimeDelta minimum, | 750 TimeDelta minimum, |
| 750 TimeDelta maximum, | 751 TimeDelta maximum, |
| 751 size_t bucket_count) | 752 size_t bucket_count) |
| 752 : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? | 753 : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? |
| 753 minimum : TimeDelta::FromMilliseconds(1), | 754 minimum : TimeDelta::FromMilliseconds(1), |
| 754 maximum, bucket_count) { | 755 maximum, bucket_count) { |
| 755 // Do a "better" (different) job at init than a base classes did... | 756 // Do a "better" (different) job at init than a base classes did... |
| 756 InitializeBucketRange(); | 757 InitializeBucketRange(); |
| 757 DCHECK(ValidateBucketRanges()); | 758 DCHECK(ValidateBucketRanges()); |
| 758 } | 759 } |
| 759 | 760 |
| 760 Histogram::ClassType LinearHistogram::histogram_type() const { | |
| 761 return LINEAR_HISTOGRAM; | |
| 762 } | |
| 763 | |
| 764 void LinearHistogram::SetRangeDescriptions( | |
| 765 const DescriptionPair descriptions[]) { | |
| 766 for (int i =0; descriptions[i].description; ++i) { | |
| 767 bucket_description_[descriptions[i].sample] = descriptions[i].description; | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { | |
| 772 int range = ranges(i); | |
| 773 BucketDescriptionMap::const_iterator it = bucket_description_.find(range); | |
| 774 if (it == bucket_description_.end()) | |
| 775 return Histogram::GetAsciiBucketRange(i); | |
| 776 return it->second; | |
| 777 } | |
| 778 | |
| 779 bool LinearHistogram::PrintEmptyBucket(size_t index) const { | |
| 780 return bucket_description_.find(ranges(index)) == bucket_description_.end(); | |
| 781 } | |
| 782 | |
| 783 | |
| 784 void LinearHistogram::InitializeBucketRange() { | 761 void LinearHistogram::InitializeBucketRange() { |
| 785 DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. | 762 DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. |
| 786 double min = declared_min(); | 763 double min = declared_min(); |
| 787 double max = declared_max(); | 764 double max = declared_max(); |
| 788 size_t i; | 765 size_t i; |
| 789 for (i = 1; i < bucket_count(); ++i) { | 766 for (i = 1; i < bucket_count(); ++i) { |
| 790 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / | 767 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / |
| 791 (bucket_count() - 2); | 768 (bucket_count() - 2); |
| 792 SetBucketRange(i, static_cast<int> (linear_range + 0.5)); | 769 SetBucketRange(i, static_cast<int> (linear_range + 0.5)); |
| 793 } | 770 } |
| 794 ResetRangeChecksum(); | 771 ResetRangeChecksum(); |
| 795 } | 772 } |
| 796 | 773 |
| 797 double LinearHistogram::GetBucketSize(Count current, size_t i) const { | 774 double LinearHistogram::GetBucketSize(Count current, size_t i) const { |
| 798 DCHECK_GT(ranges(i + 1), ranges(i)); | 775 DCHECK_GT(ranges(i + 1), ranges(i)); |
| 799 // Adjacent buckets with different widths would have "surprisingly" many (few) | 776 // Adjacent buckets with different widths would have "surprisingly" many (few) |
| 800 // samples in a histogram if we didn't normalize this way. | 777 // samples in a histogram if we didn't normalize this way. |
| 801 double denominator = ranges(i + 1) - ranges(i); | 778 double denominator = ranges(i + 1) - ranges(i); |
| 802 return current/denominator; | 779 return current/denominator; |
| 803 } | 780 } |
| 804 | 781 |
| 782 const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { |
| 783 int range = ranges(i); |
| 784 BucketDescriptionMap::const_iterator it = bucket_description_.find(range); |
| 785 if (it == bucket_description_.end()) |
| 786 return Histogram::GetAsciiBucketRange(i); |
| 787 return it->second; |
| 788 } |
| 789 |
| 790 bool LinearHistogram::PrintEmptyBucket(size_t index) const { |
| 791 return bucket_description_.find(ranges(index)) == bucket_description_.end(); |
| 792 } |
| 793 |
| 794 |
| 805 //------------------------------------------------------------------------------ | 795 //------------------------------------------------------------------------------ |
| 806 // This section provides implementation for BooleanHistogram. | 796 // This section provides implementation for BooleanHistogram. |
| 807 //------------------------------------------------------------------------------ | 797 //------------------------------------------------------------------------------ |
| 808 | 798 |
| 809 scoped_refptr<Histogram> BooleanHistogram::FactoryGet(const std::string& name, | 799 scoped_refptr<Histogram> BooleanHistogram::FactoryGet(const std::string& name, |
| 810 Flags flags) { | 800 Flags flags) { |
| 811 scoped_refptr<Histogram> histogram(NULL); | 801 scoped_refptr<Histogram> histogram(NULL); |
| 812 | 802 |
| 813 if (!StatisticsRecorder::FindHistogram(name, &histogram)) { | 803 if (!StatisticsRecorder::FindHistogram(name, &histogram)) { |
| 814 histogram = new BooleanHistogram(name); | 804 histogram = new BooleanHistogram(name); |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1053 } | 1043 } |
| 1054 | 1044 |
| 1055 // static | 1045 // static |
| 1056 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; | 1046 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; |
| 1057 // static | 1047 // static |
| 1058 base::Lock* StatisticsRecorder::lock_ = NULL; | 1048 base::Lock* StatisticsRecorder::lock_ = NULL; |
| 1059 // static | 1049 // static |
| 1060 bool StatisticsRecorder::dump_on_exit_ = false; | 1050 bool StatisticsRecorder::dump_on_exit_ = false; |
| 1061 | 1051 |
| 1062 } // namespace base | 1052 } // namespace base |
| OLD | NEW |