blob: 3a5a5b275cee3fed6d413725ead225865c2e3310 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commitd7cae122008-07-26 21:49:384
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/histogram.h"
11
12#include <math.h>
13#include <string>
14
15#include "base/logging.h"
[email protected]3f383852009-04-03 18:18:5516#include "base/pickle.h"
initial.commitd7cae122008-07-26 21:49:3817#include "base/string_util.h"
18
[email protected]e1acf6f2008-10-27 20:43:3319using base::TimeDelta;
20
initial.commitd7cae122008-07-26 21:49:3821typedef Histogram::Count Count;
22
[email protected]b16ef312008-08-19 18:36:2323// static
24const int Histogram::kHexRangePrintingFlag = 0x8000;
25
[email protected]e8829a192009-12-06 00:09:3726scoped_refptr<Histogram> Histogram::HistogramFactoryGet(
27 const std::string& name, Sample minimum, Sample maximum,
28 size_t bucket_count) {
29 scoped_refptr<Histogram> histogram(NULL);
30
31 // Defensive code.
32 if (minimum <= 0)
33 minimum = 1;
34 if (maximum >= kSampleType_MAX)
35 maximum = kSampleType_MAX - 1;
36
37 if (StatisticsRecorder::FindHistogram(name, &histogram)) {
38 DCHECK(histogram.get() != NULL);
39 } else {
40 histogram = new Histogram(name, minimum, maximum, bucket_count);
41 scoped_refptr<Histogram> registered_histogram(NULL);
42 StatisticsRecorder::FindHistogram(name, &registered_histogram);
43 // Allow a NULL return to mean that the StatisticsRecorder was not started.
44 if (registered_histogram.get() != NULL &&
45 registered_histogram.get() != histogram.get())
46 histogram = registered_histogram;
47 }
48
49 DCHECK(HISTOGRAM == histogram->histogram_type());
50 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
51 return histogram;
52}
53
54scoped_refptr<Histogram> Histogram::HistogramFactoryGet(
55 const std::string& name, base::TimeDelta minimum, base::TimeDelta maximum,
56 size_t bucket_count) {
57 return HistogramFactoryGet(name,
58 minimum.InMilliseconds(), maximum.InMilliseconds(), bucket_count);
59}
60
61Histogram::Histogram(const std::string& name, Sample minimum,
initial.commitd7cae122008-07-26 21:49:3862 Sample maximum, size_t bucket_count)
[email protected]e6f02ab2009-04-10 22:29:2963 : histogram_name_(name),
initial.commitd7cae122008-07-26 21:49:3864 declared_min_(minimum),
65 declared_max_(maximum),
66 bucket_count_(bucket_count),
67 flags_(0),
68 ranges_(bucket_count + 1, 0),
[email protected]e8829a192009-12-06 00:09:3769 sample_() {
initial.commitd7cae122008-07-26 21:49:3870 Initialize();
71}
72
[email protected]e8829a192009-12-06 00:09:3773Histogram::Histogram(const std::string& name, TimeDelta minimum,
initial.commitd7cae122008-07-26 21:49:3874 TimeDelta maximum, size_t bucket_count)
[email protected]e6f02ab2009-04-10 22:29:2975 : histogram_name_(name),
initial.commitd7cae122008-07-26 21:49:3876 declared_min_(static_cast<int> (minimum.InMilliseconds())),
77 declared_max_(static_cast<int> (maximum.InMilliseconds())),
78 bucket_count_(bucket_count),
79 flags_(0),
80 ranges_(bucket_count + 1, 0),
[email protected]e8829a192009-12-06 00:09:3781 sample_() {
initial.commitd7cae122008-07-26 21:49:3882 Initialize();
83}
84
85Histogram::~Histogram() {
[email protected]e8829a192009-12-06 00:09:3786 DCHECK(!(kPlannedLeakFlag & flags_));
87 if (StatisticsRecorder::dump_on_exit()) {
88 std::string output;
89 WriteAscii(true, "\n", &output);
90 LOG(INFO) << output;
91 }
92
initial.commitd7cae122008-07-26 21:49:3893 // Just to make sure most derived class did this properly...
94 DCHECK(ValidateBucketRanges());
95}
96
initial.commitd7cae122008-07-26 21:49:3897void Histogram::Add(int value) {
initial.commitd7cae122008-07-26 21:49:3898 if (value >= kSampleType_MAX)
99 value = kSampleType_MAX - 1;
initial.commitd7cae122008-07-26 21:49:38100 if (value < 0)
101 value = 0;
102 size_t index = BucketIndex(value);
103 DCHECK(value >= ranges(index));
104 DCHECK(value < ranges(index + 1));
105 Accumulate(value, 1, index);
106}
107
[email protected]55e57d42009-02-25 06:10:17108void Histogram::AddSampleSet(const SampleSet& sample) {
109 sample_.Add(sample);
110}
111
initial.commitd7cae122008-07-26 21:49:38112// The following methods provide a graphical histogram display.
113void Histogram::WriteHTMLGraph(std::string* output) const {
114 // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
115 output->append("<PRE>");
116 WriteAscii(true, "<br>", output);
117 output->append("</PRE>");
118}
119
120void Histogram::WriteAscii(bool graph_it, const std::string& newline,
121 std::string* output) const {
122 // Get local (stack) copies of all effectively volatile class data so that we
123 // are consistent across our output activities.
124 SampleSet snapshot;
125 SnapshotSample(&snapshot);
126 Count sample_count = snapshot.TotalCount();
127
128 WriteAsciiHeader(snapshot, sample_count, output);
129 output->append(newline);
130
131 // Prepare to normalize graphical rendering of bucket contents.
132 double max_size = 0;
133 if (graph_it)
134 max_size = GetPeakBucketSize(snapshot);
135
136 // Calculate space needed to print bucket range numbers. Leave room to print
137 // nearly the largest bucket range without sliding over the histogram.
[email protected]e2951cf2008-09-24 23:51:25138 size_t largest_non_empty_bucket = bucket_count() - 1;
139 while (0 == snapshot.counts(largest_non_empty_bucket)) {
initial.commitd7cae122008-07-26 21:49:38140 if (0 == largest_non_empty_bucket)
141 break; // All buckets are empty.
[email protected]55e57d42009-02-25 06:10:17142 --largest_non_empty_bucket;
initial.commitd7cae122008-07-26 21:49:38143 }
144
145 // Calculate largest print width needed for any of our bucket range displays.
146 size_t print_width = 1;
[email protected]e2951cf2008-09-24 23:51:25147 for (size_t i = 0; i < bucket_count(); ++i) {
initial.commitd7cae122008-07-26 21:49:38148 if (snapshot.counts(i)) {
149 size_t width = GetAsciiBucketRange(i).size() + 1;
150 if (width > print_width)
151 print_width = width;
152 }
153 }
154
155 int64 remaining = sample_count;
156 int64 past = 0;
157 // Output the actual histogram graph.
[email protected]55e57d42009-02-25 06:10:17158 for (size_t i = 0; i < bucket_count(); ++i) {
initial.commitd7cae122008-07-26 21:49:38159 Count current = snapshot.counts(i);
160 if (!current && !PrintEmptyBucket(i))
161 continue;
162 remaining -= current;
[email protected]34b2b002009-11-20 06:53:28163 std::string range = GetAsciiBucketRange(i);
164 output->append(range);
165 for (size_t j = 0; range.size() + j < print_width + 1; ++j)
166 output->push_back(' ');
[email protected]e2951cf2008-09-24 23:51:25167 if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
168 while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
[email protected]55e57d42009-02-25 06:10:17169 ++i;
initial.commitd7cae122008-07-26 21:49:38170 output->append("... ");
171 output->append(newline);
172 continue; // No reason to plot emptiness.
173 }
174 double current_size = GetBucketSize(current, i);
175 if (graph_it)
176 WriteAsciiBucketGraph(current_size, max_size, output);
177 WriteAsciiBucketContext(past, current, remaining, i, output);
178 output->append(newline);
179 past += current;
180 }
181 DCHECK(past == sample_count);
182}
183
184bool Histogram::ValidateBucketRanges() const {
185 // Standard assertions that all bucket ranges should satisfy.
186 DCHECK(ranges_.size() == bucket_count_ + 1);
187 DCHECK(0 == ranges_[0]);
188 DCHECK(declared_min() == ranges_[1]);
189 DCHECK(declared_max() == ranges_[bucket_count_ - 1]);
190 DCHECK(kSampleType_MAX == ranges_[bucket_count_]);
191 return true;
192}
193
194void Histogram::Initialize() {
195 sample_.Resize(*this);
196 if (declared_min_ <= 0)
197 declared_min_ = 1;
198 if (declared_max_ >= kSampleType_MAX)
199 declared_max_ = kSampleType_MAX - 1;
200 DCHECK(declared_min_ > 0); // We provide underflow bucket.
[email protected]e2951cf2008-09-24 23:51:25201 DCHECK(declared_min_ <= declared_max_);
initial.commitd7cae122008-07-26 21:49:38202 DCHECK(1 < bucket_count_);
203 size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
204 DCHECK(bucket_count_ <= maximal_bucket_count);
205 DCHECK(0 == ranges_[0]);
206 ranges_[bucket_count_] = kSampleType_MAX;
207 InitializeBucketRange();
208 DCHECK(ValidateBucketRanges());
[email protected]e8829a192009-12-06 00:09:37209 StatisticsRecorder::Register(this);
initial.commitd7cae122008-07-26 21:49:38210}
211
212// Calculate what range of values are held in each bucket.
213// We have to be careful that we don't pick a ratio between starting points in
214// consecutive buckets that is sooo small, that the integer bounds are the same
215// (effectively making one bucket get no values). We need to avoid:
216// (ranges_[i] == ranges_[i + 1]
217// To avoid that, we just do a fine-grained bucket width as far as we need to
218// until we get a ratio that moves us along at least 2 units at a time. From
219// that bucket onward we do use the exponential growth of buckets.
220void Histogram::InitializeBucketRange() {
221 double log_max = log(static_cast<double>(declared_max()));
222 double log_ratio;
223 double log_next;
224 size_t bucket_index = 1;
225 Sample current = declared_min();
226 SetBucketRange(bucket_index, current);
227 while (bucket_count() > ++bucket_index) {
228 double log_current;
229 log_current = log(static_cast<double>(current));
230 // Calculate the count'th root of the range.
231 log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
232 // See where the next bucket would start.
233 log_next = log_current + log_ratio;
234 int next;
235 next = static_cast<int>(floor(exp(log_next) + 0.5));
236 if (next > current)
237 current = next;
238 else
[email protected]55e57d42009-02-25 06:10:17239 ++current; // Just do a narrow bucket, and keep trying.
initial.commitd7cae122008-07-26 21:49:38240 SetBucketRange(bucket_index, current);
241 }
242
243 DCHECK(bucket_count() == bucket_index);
244}
245
246size_t Histogram::BucketIndex(Sample value) const {
247 // Use simple binary search. This is very general, but there are better
248 // approaches if we knew that the buckets were linearly distributed.
249 DCHECK(ranges(0) <= value);
250 DCHECK(ranges(bucket_count()) > value);
251 size_t under = 0;
252 size_t over = bucket_count();
253 size_t mid;
254
255 do {
256 DCHECK(over >= under);
257 mid = (over + under)/2;
258 if (mid == under)
259 break;
260 if (ranges(mid) <= value)
261 under = mid;
262 else
263 over = mid;
264 } while (true);
265
266 DCHECK(ranges(mid) <= value && ranges(mid+1) > value);
267 return mid;
268}
269
270// Use the actual bucket widths (like a linear histogram) until the widths get
271// over some transition value, and then use that transition width. Exponentials
272// get so big so fast (and we don't expect to see a lot of entries in the large
273// buckets), so we need this to make it possible to see what is going on and
274// not have 0-graphical-height buckets.
275double Histogram::GetBucketSize(Count current, size_t i) const {
276 DCHECK(ranges(i + 1) > ranges(i));
277 static const double kTransitionWidth = 5;
278 double denominator = ranges(i + 1) - ranges(i);
279 if (denominator > kTransitionWidth)
280 denominator = kTransitionWidth; // Stop trying to normalize.
281 return current/denominator;
282}
283
284//------------------------------------------------------------------------------
285// The following two methods can be overridden to provide a thread safe
286// version of this class. The cost of locking is low... but an error in each
287// of these methods has minimal impact. For now, I'll leave this unlocked,
288// and I don't believe I can loose more than a count or two.
289// The vectors are NOT reallocated, so there is no risk of them moving around.
290
291// Update histogram data with new sample.
292void Histogram::Accumulate(Sample value, Count count, size_t index) {
293 // Note locking not done in this version!!!
294 sample_.Accumulate(value, count, index);
295}
296
297// Do a safe atomic snapshot of sample data.
298// This implementation assumes we are on a safe single thread.
299void Histogram::SnapshotSample(SampleSet* sample) const {
300 // Note locking not done in this version!!!
301 *sample = sample_;
302}
303
304//------------------------------------------------------------------------------
305// Accessor methods
306
307void Histogram::SetBucketRange(size_t i, Sample value) {
308 DCHECK(bucket_count_ > i);
309 ranges_[i] = value;
310}
311
312//------------------------------------------------------------------------------
313// Private methods
314
315double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
316 double max = 0;
[email protected]55e57d42009-02-25 06:10:17317 for (size_t i = 0; i < bucket_count() ; ++i) {
initial.commitd7cae122008-07-26 21:49:38318 double current_size = GetBucketSize(snapshot.counts(i), i);
319 if (current_size > max)
320 max = current_size;
321 }
322 return max;
323}
324
325void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
326 Count sample_count,
327 std::string* output) const {
328 StringAppendF(output,
[email protected]34b2b002009-11-20 06:53:28329 "Histogram: %s recorded %d samples",
initial.commitd7cae122008-07-26 21:49:38330 histogram_name().c_str(),
331 sample_count);
332 if (0 == sample_count) {
333 DCHECK(0 == snapshot.sum());
334 } else {
335 double average = static_cast<float>(snapshot.sum()) / sample_count;
336 double variance = static_cast<float>(snapshot.square_sum())/sample_count
337 - average * average;
338 double standard_deviation = sqrt(variance);
339
340 StringAppendF(output,
341 ", average = %.1f, standard deviation = %.1f",
342 average, standard_deviation);
343 }
344 if (flags_ & ~kHexRangePrintingFlag )
345 StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
346}
347
348void Histogram::WriteAsciiBucketContext(const int64 past,
349 const Count current,
350 const int64 remaining,
351 const size_t i,
352 std::string* output) const {
353 double scaled_sum = (past + current + remaining) / 100.0;
354 WriteAsciiBucketValue(current, scaled_sum, output);
355 if (0 < i) {
356 double percentage = past / scaled_sum;
357 StringAppendF(output, " {%3.1f%%}", percentage);
358 }
359}
360
361const std::string Histogram::GetAsciiBucketRange(size_t i) const {
362 std::string result;
363 if (kHexRangePrintingFlag & flags_)
[email protected]e2951cf2008-09-24 23:51:25364 StringAppendF(&result, "%#x", ranges(i));
initial.commitd7cae122008-07-26 21:49:38365 else
[email protected]e2951cf2008-09-24 23:51:25366 StringAppendF(&result, "%d", ranges(i));
initial.commitd7cae122008-07-26 21:49:38367 return result;
368}
369
370void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
371 std::string* output) const {
372 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
373}
374
375void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
376 std::string* output) const {
377 const int k_line_length = 72; // Maximal horizontal width of graph.
378 int x_count = static_cast<int>(k_line_length * (current_size / max_size)
379 + 0.5);
380 int x_remainder = k_line_length - x_count;
381
382 while (0 < x_count--)
383 output->append("-");
384 output->append("O");
385 while (0 < x_remainder--)
386 output->append(" ");
387}
388
[email protected]55e57d42009-02-25 06:10:17389// static
390std::string Histogram::SerializeHistogramInfo(const Histogram& histogram,
391 const SampleSet& snapshot) {
[email protected]e8829a192009-12-06 00:09:37392 DCHECK(histogram.histogram_type() != NOT_VALID_IN_RENDERER);
[email protected]55e57d42009-02-25 06:10:17393
[email protected]e8829a192009-12-06 00:09:37394 Pickle pickle;
[email protected]55e57d42009-02-25 06:10:17395 pickle.WriteString(histogram.histogram_name());
396 pickle.WriteInt(histogram.declared_min());
397 pickle.WriteInt(histogram.declared_max());
398 pickle.WriteSize(histogram.bucket_count());
399 pickle.WriteInt(histogram.histogram_type());
[email protected]e8829a192009-12-06 00:09:37400 pickle.WriteInt(histogram.flags() & ~kIPCSerializationSourceFlag);
[email protected]55e57d42009-02-25 06:10:17401
402 snapshot.Serialize(&pickle);
403 return std::string(static_cast<const char*>(pickle.data()), pickle.size());
404}
405
406// static
[email protected]55e57d42009-02-25 06:10:17407bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) {
408 if (histogram_info.empty()) {
409 return false;
410 }
411
412 Pickle pickle(histogram_info.data(),
413 static_cast<int>(histogram_info.size()));
414 void* iter = NULL;
415 size_t bucket_count;
416 int declared_min;
417 int declared_max;
418 int histogram_type;
419 int flags;
420 std::string histogram_name;
421 SampleSet sample;
422
423 if (!pickle.ReadString(&iter, &histogram_name) ||
424 !pickle.ReadInt(&iter, &declared_min) ||
425 !pickle.ReadInt(&iter, &declared_max) ||
426 !pickle.ReadSize(&iter, &bucket_count) ||
427 !pickle.ReadInt(&iter, &histogram_type) ||
428 !pickle.ReadInt(&iter, &flags) ||
429 !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) {
430 LOG(ERROR) << "Picke error decoding Histogram: " << histogram_name;
431 return false;
432 }
433
[email protected]e8829a192009-12-06 00:09:37434 DCHECK(histogram_type != NOT_VALID_IN_RENDERER);
[email protected]55e57d42009-02-25 06:10:17435
[email protected]e8829a192009-12-06 00:09:37436 scoped_refptr<Histogram> render_histogram(NULL);
437
438 if (histogram_type == HISTOGRAM) {
439 render_histogram = Histogram::HistogramFactoryGet(
440 histogram_name, declared_min, declared_max, bucket_count);
441 } else if (histogram_type == LINEAR_HISTOGRAM) {
442 render_histogram = LinearHistogram::LinearHistogramFactoryGet(
443 histogram_name, declared_min, declared_max, bucket_count);
444 } else if (histogram_type == BOOLEAN_HISTOGRAM) {
445 render_histogram = BooleanHistogram::BooleanHistogramFactoryGet(
446 histogram_name);
447 } else if (histogram_type == THREAD_SAFE_HISTOGRAM) {
448 render_histogram =
449 ThreadSafeHistogram::ThreadSafeHistogramFactoryGet(
450 histogram_name, declared_min, declared_max, bucket_count);
451 } else {
452 LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " <<
453 histogram_type;
454 return false;
[email protected]55e57d42009-02-25 06:10:17455 }
456
457 DCHECK(declared_min == render_histogram->declared_min());
458 DCHECK(declared_max == render_histogram->declared_max());
459 DCHECK(bucket_count == render_histogram->bucket_count());
460 DCHECK(histogram_type == render_histogram->histogram_type());
461
[email protected]e8829a192009-12-06 00:09:37462 if (render_histogram->flags() & kIPCSerializationSourceFlag) {
463 DLOG(INFO) << "Single process mode, histogram observed and not copied: " <<
[email protected]55e57d42009-02-25 06:10:17464 histogram_name;
[email protected]e8829a192009-12-06 00:09:37465 } else {
466 render_histogram->AddSampleSet(sample);
[email protected]55e57d42009-02-25 06:10:17467 }
468
469 return true;
470}
471
initial.commitd7cae122008-07-26 21:49:38472//------------------------------------------------------------------------------
473// Methods for the Histogram::SampleSet class
474//------------------------------------------------------------------------------
475
476Histogram::SampleSet::SampleSet()
477 : counts_(),
478 sum_(0),
479 square_sum_(0) {
480}
481
482void Histogram::SampleSet::Resize(const Histogram& histogram) {
483 counts_.resize(histogram.bucket_count(), 0);
484}
485
486void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
487 DCHECK(counts_.size() == histogram.bucket_count());
488}
489
490
491void Histogram::SampleSet::Accumulate(Sample value, Count count,
492 size_t index) {
493 DCHECK(count == 1 || count == -1);
494 counts_[index] += count;
495 sum_ += count * value;
496 square_sum_ += (count * value) * static_cast<int64>(value);
497 DCHECK(counts_[index] >= 0);
498 DCHECK(sum_ >= 0);
499 DCHECK(square_sum_ >= 0);
500}
501
502Count Histogram::SampleSet::TotalCount() const {
503 Count total = 0;
504 for (Counts::const_iterator it = counts_.begin();
505 it != counts_.end();
[email protected]55e57d42009-02-25 06:10:17506 ++it) {
initial.commitd7cae122008-07-26 21:49:38507 total += *it;
508 }
509 return total;
510}
511
512void Histogram::SampleSet::Add(const SampleSet& other) {
513 DCHECK(counts_.size() == other.counts_.size());
514 sum_ += other.sum_;
515 square_sum_ += other.square_sum_;
[email protected]55e57d42009-02-25 06:10:17516 for (size_t index = 0; index < counts_.size(); ++index)
initial.commitd7cae122008-07-26 21:49:38517 counts_[index] += other.counts_[index];
518}
519
520void Histogram::SampleSet::Subtract(const SampleSet& other) {
521 DCHECK(counts_.size() == other.counts_.size());
522 // Note: Race conditions in snapshotting a sum or square_sum may lead to
523 // (temporary) negative values when snapshots are later combined (and deltas
524 // calculated). As a result, we don't currently CHCEK() for positive values.
525 sum_ -= other.sum_;
526 square_sum_ -= other.square_sum_;
[email protected]55e57d42009-02-25 06:10:17527 for (size_t index = 0; index < counts_.size(); ++index) {
initial.commitd7cae122008-07-26 21:49:38528 counts_[index] -= other.counts_[index];
529 DCHECK(counts_[index] >= 0);
530 }
531}
532
[email protected]55e57d42009-02-25 06:10:17533bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
534 pickle->WriteInt64(sum_);
535 pickle->WriteInt64(square_sum_);
536 pickle->WriteSize(counts_.size());
537
538 for (size_t index = 0; index < counts_.size(); ++index) {
539 pickle->WriteInt(counts_[index]);
540 }
541
542 return true;
543}
544
545bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) {
546 DCHECK(counts_.size() == 0);
547 DCHECK(sum_ == 0);
548 DCHECK(square_sum_ == 0);
549
550 size_t counts_size;
551
552 if (!pickle.ReadInt64(iter, &sum_) ||
553 !pickle.ReadInt64(iter, &square_sum_) ||
554 !pickle.ReadSize(iter, &counts_size)) {
555 return false;
556 }
557
558 if (counts_size <= 0)
559 return false;
560
561 counts_.resize(counts_size, 0);
562 for (size_t index = 0; index < counts_size; ++index) {
563 if (!pickle.ReadInt(iter, &counts_[index])) {
564 return false;
565 }
566 }
567
568 return true;
569}
570
initial.commitd7cae122008-07-26 21:49:38571//------------------------------------------------------------------------------
572// LinearHistogram: This histogram uses a traditional set of evenly spaced
573// buckets.
574//------------------------------------------------------------------------------
575
[email protected]e8829a192009-12-06 00:09:37576scoped_refptr<Histogram> LinearHistogram::LinearHistogramFactoryGet(
577 const std::string& name, Sample minimum, Sample maximum,
578 size_t bucket_count) {
579 scoped_refptr<Histogram> histogram(NULL);
580
581 if (minimum <= 0)
582 minimum = 1;
583 if (maximum >= kSampleType_MAX)
584 maximum = kSampleType_MAX - 1;
585
586 if (StatisticsRecorder::FindHistogram(name, &histogram)) {
587 DCHECK(histogram.get() != NULL);
588 } else {
589 histogram = new LinearHistogram(name, minimum, maximum, bucket_count);
590 scoped_refptr<Histogram> registered_histogram(NULL);
591 StatisticsRecorder::FindHistogram(name, &registered_histogram);
592 if (registered_histogram.get() != NULL &&
593 registered_histogram.get() != histogram.get())
594 histogram = registered_histogram;
595 }
596
597 DCHECK(LINEAR_HISTOGRAM == histogram->histogram_type());
598 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
599
600 return histogram;
601}
602
603scoped_refptr<Histogram> LinearHistogram::LinearHistogramFactoryGet(
604 const std::string& name, base::TimeDelta minimum, base::TimeDelta maximum,
605 size_t bucket_count) {
606 return LinearHistogramFactoryGet(name, minimum.InMilliseconds(),
607 maximum.InMilliseconds(), bucket_count);
608}
609
610LinearHistogram::LinearHistogram(const std::string& name, Sample minimum,
[email protected]55e57d42009-02-25 06:10:17611 Sample maximum, size_t bucket_count)
initial.commitd7cae122008-07-26 21:49:38612 : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
613 InitializeBucketRange();
614 DCHECK(ValidateBucketRanges());
615}
616
[email protected]e8829a192009-12-06 00:09:37617LinearHistogram::LinearHistogram(const std::string& name,
initial.commitd7cae122008-07-26 21:49:38618 TimeDelta minimum, TimeDelta maximum, size_t bucket_count)
619 : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
620 minimum : TimeDelta::FromMilliseconds(1),
621 maximum, bucket_count) {
622 // Do a "better" (different) job at init than a base classes did...
623 InitializeBucketRange();
624 DCHECK(ValidateBucketRanges());
625}
626
[email protected]5059e3a2009-01-14 16:01:03627void LinearHistogram::SetRangeDescriptions(
628 const DescriptionPair descriptions[]) {
initial.commitd7cae122008-07-26 21:49:38629 for (int i =0; descriptions[i].description; ++i) {
630 bucket_description_[descriptions[i].sample] = descriptions[i].description;
631 }
632}
633
634const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
635 int range = ranges(i);
636 BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
637 if (it == bucket_description_.end())
638 return Histogram::GetAsciiBucketRange(i);
639 return it->second;
640}
641
642bool LinearHistogram::PrintEmptyBucket(size_t index) const {
643 return bucket_description_.find(ranges(index)) == bucket_description_.end();
644}
645
646
647void LinearHistogram::InitializeBucketRange() {
648 DCHECK(0 < declared_min()); // 0 is the underflow bucket here.
649 double min = declared_min();
650 double max = declared_max();
651 size_t i;
[email protected]55e57d42009-02-25 06:10:17652 for (i = 1; i < bucket_count(); ++i) {
initial.commitd7cae122008-07-26 21:49:38653 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
654 (bucket_count() - 2);
655 SetBucketRange(i, static_cast<int> (linear_range + 0.5));
656 }
657}
658
initial.commitd7cae122008-07-26 21:49:38659double LinearHistogram::GetBucketSize(Count current, size_t i) const {
660 DCHECK(ranges(i + 1) > ranges(i));
661 // Adjacent buckets with different widths would have "surprisingly" many (few)
662 // samples in a histogram if we didn't normalize this way.
663 double denominator = ranges(i + 1) - ranges(i);
664 return current/denominator;
665}
666
667//------------------------------------------------------------------------------
[email protected]e8829a192009-12-06 00:09:37668// This section provides implementation for BooleanHistogram.
669//------------------------------------------------------------------------------
670
671scoped_refptr<Histogram> BooleanHistogram::BooleanHistogramFactoryGet(
672 const std::string& name) {
673 scoped_refptr<Histogram> histogram(NULL);
674
675 if (StatisticsRecorder::FindHistogram(name, &histogram)) {
676 DCHECK(histogram.get() != NULL);
677 } else {
678 histogram = new BooleanHistogram(name);
679 scoped_refptr<Histogram> registered_histogram(NULL);
680 StatisticsRecorder::FindHistogram(name, &registered_histogram);
681 if (registered_histogram.get() != NULL &&
682 registered_histogram.get() != histogram.get())
683 histogram = registered_histogram;
684 }
685
686 DCHECK(BOOLEAN_HISTOGRAM == histogram->histogram_type());
687
688 return histogram;
689}
690
691//------------------------------------------------------------------------------
initial.commitd7cae122008-07-26 21:49:38692// This section provides implementation for ThreadSafeHistogram.
693//------------------------------------------------------------------------------
694
[email protected]e8829a192009-12-06 00:09:37695scoped_refptr<Histogram> ThreadSafeHistogram::ThreadSafeHistogramFactoryGet(
696 const std::string& name, Sample minimum, Sample maximum,
697 size_t bucket_count) {
698 scoped_refptr<Histogram> histogram(NULL);
699
700 if (minimum <= 0)
701 minimum = 1;
702 if (maximum >= kSampleType_MAX)
703 maximum = kSampleType_MAX - 1;
704
705 if (StatisticsRecorder::FindHistogram(name, &histogram)) {
706 DCHECK(histogram.get() != NULL);
707 } else {
708 histogram = new ThreadSafeHistogram(name, minimum, maximum, bucket_count);
709 scoped_refptr<Histogram> registered_histogram(NULL);
710 StatisticsRecorder::FindHistogram(name, &registered_histogram);
711 if (registered_histogram.get() != NULL &&
712 registered_histogram.get() != histogram.get())
713 histogram = registered_histogram;
714 }
715
716 DCHECK(THREAD_SAFE_HISTOGRAM == histogram->histogram_type());
717 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
718 return histogram;
719}
720
721ThreadSafeHistogram::ThreadSafeHistogram(const std::string& name,
722 Sample minimum, Sample maximum, size_t bucket_count)
initial.commitd7cae122008-07-26 21:49:38723 : Histogram(name, minimum, maximum, bucket_count),
724 lock_() {
725 }
726
727void ThreadSafeHistogram::Remove(int value) {
728 if (value >= kSampleType_MAX)
729 value = kSampleType_MAX - 1;
initial.commitd7cae122008-07-26 21:49:38730 size_t index = BucketIndex(value);
731 Accumulate(value, -1, index);
732}
733
734void ThreadSafeHistogram::Accumulate(Sample value, Count count, size_t index) {
735 AutoLock lock(lock_);
736 Histogram::Accumulate(value, count, index);
737}
738
[email protected]6df40742009-03-19 22:24:50739void ThreadSafeHistogram::SnapshotSample(SampleSet* sample) const {
initial.commitd7cae122008-07-26 21:49:38740 AutoLock lock(lock_);
741 Histogram::SnapshotSample(sample);
742};
743
744
745//------------------------------------------------------------------------------
746// The next section handles global (central) support for all histograms, as well
747// as startup/teardown of this service.
748//------------------------------------------------------------------------------
749
750// This singleton instance should be started during the single threaded portion
751// of main(), and hence it is not thread safe. It initializes globals to
752// provide support for all future calls.
753StatisticsRecorder::StatisticsRecorder() {
754 DCHECK(!histograms_);
755 lock_ = new Lock;
756 histograms_ = new HistogramMap;
757}
758
759StatisticsRecorder::~StatisticsRecorder() {
760 DCHECK(histograms_);
761
762 if (dump_on_exit_) {
763 std::string output;
764 WriteGraph("", &output);
765 LOG(INFO) << output;
766 }
767
768 // Clean up.
769 delete histograms_;
770 histograms_ = NULL;
771 delete lock_;
772 lock_ = NULL;
773}
774
775// static
776bool StatisticsRecorder::WasStarted() {
777 return NULL != histograms_;
778}
779
[email protected]e8829a192009-12-06 00:09:37780// Note: We can't accept a ref_ptr to |histogram| because we *might* not keep a
781// reference, and we are called while in the Histogram constructor. In that
782// scenario, a ref_ptr would have incremented the ref count when the histogram
783// was passed to us, decremented it when we returned, and the instance would be
784// destroyed before assignment (when value was returned by new).
initial.commitd7cae122008-07-26 21:49:38785// static
[email protected]e8829a192009-12-06 00:09:37786void StatisticsRecorder::Register(Histogram* histogram) {
initial.commitd7cae122008-07-26 21:49:38787 if (!histograms_)
788 return;
[email protected]55e57d42009-02-25 06:10:17789 const std::string name = histogram->histogram_name();
initial.commitd7cae122008-07-26 21:49:38790 AutoLock auto_lock(*lock_);
[email protected]e8829a192009-12-06 00:09:37791
792 DCHECK(histograms_->end() == histograms_->find(name));
793
794 (*histograms_)[name] = histogram;
795 return;
initial.commitd7cae122008-07-26 21:49:38796}
797
798// static
799void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
800 std::string* output) {
801 if (!histograms_)
802 return;
803 output->append("<html><head><title>About Histograms");
804 if (!query.empty())
805 output->append(" - " + query);
806 output->append("</title>"
807 // We'd like the following no-cache... but it doesn't work.
808 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
809 "</head><body>");
810
811 Histograms snapshot;
812 GetSnapshot(query, &snapshot);
813 for (Histograms::iterator it = snapshot.begin();
814 it != snapshot.end();
[email protected]55e57d42009-02-25 06:10:17815 ++it) {
initial.commitd7cae122008-07-26 21:49:38816 (*it)->WriteHTMLGraph(output);
817 output->append("<br><hr><br>");
818 }
819 output->append("</body></html>");
820}
821
822// static
823void StatisticsRecorder::WriteGraph(const std::string& query,
[email protected]55e57d42009-02-25 06:10:17824 std::string* output) {
initial.commitd7cae122008-07-26 21:49:38825 if (!histograms_)
826 return;
827 if (query.length())
828 StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
829 else
830 output->append("Collections of all histograms\n");
831
832 Histograms snapshot;
833 GetSnapshot(query, &snapshot);
834 for (Histograms::iterator it = snapshot.begin();
835 it != snapshot.end();
[email protected]55e57d42009-02-25 06:10:17836 ++it) {
initial.commitd7cae122008-07-26 21:49:38837 (*it)->WriteAscii(true, "\n", output);
838 output->append("\n");
839 }
840}
841
842// static
843void StatisticsRecorder::GetHistograms(Histograms* output) {
844 if (!histograms_)
845 return;
846 AutoLock auto_lock(*lock_);
847 for (HistogramMap::iterator it = histograms_->begin();
848 histograms_->end() != it;
[email protected]55e57d42009-02-25 06:10:17849 ++it) {
initial.commitd7cae122008-07-26 21:49:38850 output->push_back(it->second);
851 }
852}
853
[email protected]e8829a192009-12-06 00:09:37854// static
855void StatisticsRecorder::GetHistogramsForRenderer(Histograms* output) {
[email protected]55e57d42009-02-25 06:10:17856 if (!histograms_)
[email protected]e8829a192009-12-06 00:09:37857 return;
[email protected]55e57d42009-02-25 06:10:17858 AutoLock auto_lock(*lock_);
859 for (HistogramMap::iterator it = histograms_->begin();
860 histograms_->end() != it;
861 ++it) {
[email protected]e8829a192009-12-06 00:09:37862 scoped_refptr<Histogram> histogram = it->second;
863 if (!(histogram->flags() & kIPCSerializationSourceFlag))
864 histogram->SetFlags(kIPCSerializationSourceFlag);
865 output->push_back(histogram);
[email protected]55e57d42009-02-25 06:10:17866 }
[email protected]e8829a192009-12-06 00:09:37867}
868
869bool StatisticsRecorder::FindHistogram(const std::string& name,
870 scoped_refptr<Histogram>* histogram) {
871 if (!histograms_)
872 return false;
873 AutoLock auto_lock(*lock_);
874 HistogramMap::iterator it = histograms_->find(name);
875 if (histograms_->end() == it)
876 return false;
877 *histogram = it->second;
878 return true;
[email protected]55e57d42009-02-25 06:10:17879}
880
initial.commitd7cae122008-07-26 21:49:38881// private static
882void StatisticsRecorder::GetSnapshot(const std::string& query,
883 Histograms* snapshot) {
884 AutoLock auto_lock(*lock_);
885 for (HistogramMap::iterator it = histograms_->begin();
886 histograms_->end() != it;
[email protected]55e57d42009-02-25 06:10:17887 ++it) {
initial.commitd7cae122008-07-26 21:49:38888 if (it->first.find(query) != std::string::npos)
889 snapshot->push_back(it->second);
890 }
891}
892
893// static
894StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
895// static
896Lock* StatisticsRecorder::lock_ = NULL;
897// static
898bool StatisticsRecorder::dump_on_exit_ = false;