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 |