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