Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : // Copyright (c) 2011 The Chromium Authors. All rights reserved.
4 : // Use of this source code is governed by a BSD-style license that can be
5 : // found in the LICENSE file.
6 :
7 : // Histogram is an object that aggregates statistics, and can summarize them in
8 : // various forms, including ASCII graphical, HTML, and numerically (as a
9 : // vector of numbers corresponding to each of the aggregating buckets).
10 : // See header file for details and examples.
11 :
12 : #include "base/histogram.h"
13 :
14 : #include <math.h>
15 :
16 : #include <algorithm>
17 : #include <string>
18 :
19 : #include "base/logging.h"
20 : #include "base/pickle.h"
21 : #include "base/string_util.h"
22 : #include "base/logging.h"
23 :
24 : namespace base {
25 :
26 : #define DVLOG(x) CHROMIUM_LOG(ERROR)
27 : #define CHECK_GT DCHECK_GT
28 : #define CHECK_LT DCHECK_LT
29 : typedef ::Lock Lock;
30 : typedef ::AutoLock AutoLock;
31 :
32 : // Static table of checksums for all possible 8 bit bytes.
33 : const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
34 : 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
35 : 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
36 : 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL,
37 : 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL,
38 : 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L,
39 : 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL,
40 : 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
41 : 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L,
42 : 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL,
43 : 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL,
44 : 0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L,
45 : 0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL,
46 : 0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L,
47 : 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L,
48 : 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL,
49 : 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L,
50 : 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
51 : 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL,
52 : 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L,
53 : 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L,
54 : 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L,
55 : 0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
56 : 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL,
57 : 0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L,
58 : 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L,
59 : 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L,
60 : 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
61 : 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L,
62 : 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL,
63 : 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L,
64 : 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L,
65 : 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
66 : 0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L,
67 : 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L,
68 : 0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL,
69 : 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL,
70 : 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
71 : 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL,
72 : 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L,
73 : 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L,
74 : 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL,
75 : 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
76 : 0x2d02ef8dL,
77 : };
78 :
79 : typedef Histogram::Count Count;
80 :
81 : // static
82 : const size_t Histogram::kBucketCount_MAX = 16384u;
83 :
84 1862 : Histogram* Histogram::FactoryGet(const std::string& name,
85 : Sample minimum,
86 : Sample maximum,
87 : size_t bucket_count,
88 : Flags flags) {
89 1862 : Histogram* histogram(NULL);
90 :
91 : // Defensive code.
92 1862 : if (minimum < 1)
93 0 : minimum = 1;
94 1862 : if (maximum > kSampleType_MAX - 1)
95 0 : maximum = kSampleType_MAX - 1;
96 :
97 1862 : if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
98 : // Extra variable is not needed... but this keeps this section basically
99 : // identical to other derived classes in this file (and compiler will
100 : // optimize away the extra variable.
101 : Histogram* tentative_histogram =
102 1862 : new Histogram(name, minimum, maximum, bucket_count);
103 1862 : tentative_histogram->InitializeBucketRange();
104 1862 : tentative_histogram->SetFlags(flags);
105 1862 : histogram =
106 1862 : StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
107 : }
108 :
109 1862 : DCHECK_EQ(HISTOGRAM, histogram->histogram_type());
110 1862 : DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
111 1862 : return histogram;
112 : }
113 :
114 0 : Histogram* Histogram::FactoryTimeGet(const std::string& name,
115 : TimeDelta minimum,
116 : TimeDelta maximum,
117 : size_t bucket_count,
118 : Flags flags) {
119 0 : return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
120 0 : bucket_count, flags);
121 : }
122 :
123 174 : void Histogram::Add(int value) {
124 174 : if (value > kSampleType_MAX - 1)
125 0 : value = kSampleType_MAX - 1;
126 174 : if (value < 0)
127 1 : value = 0;
128 174 : size_t index = BucketIndex(value);
129 174 : DCHECK_GE(value, ranges(index));
130 174 : DCHECK_LT(value, ranges(index + 1));
131 174 : Accumulate(value, 1, index);
132 174 : }
133 :
134 0 : void Histogram::Subtract(int value) {
135 0 : if (value > kSampleType_MAX - 1)
136 0 : value = kSampleType_MAX - 1;
137 0 : if (value < 0)
138 0 : value = 0;
139 0 : size_t index = BucketIndex(value);
140 0 : DCHECK_GE(value, ranges(index));
141 0 : DCHECK_LT(value, ranges(index + 1));
142 0 : Accumulate(value, -1, index);
143 0 : }
144 :
145 0 : void Histogram::AddBoolean(bool value) {
146 0 : DCHECK(false);
147 0 : }
148 :
149 15 : void Histogram::AddSampleSet(const SampleSet& sample) {
150 15 : sample_.Add(sample);
151 15 : }
152 :
153 0 : void Histogram::Clear() {
154 0 : SampleSet ss;
155 0 : ss.Resize(*this);
156 0 : sample_ = ss;
157 0 : }
158 :
159 0 : void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
160 0 : DCHECK(false);
161 0 : }
162 :
163 : // The following methods provide a graphical histogram display.
164 0 : void Histogram::WriteHTMLGraph(std::string* output) const {
165 : // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
166 0 : output->append("<PRE>");
167 0 : WriteAscii(true, "<br>", output);
168 0 : output->append("</PRE>");
169 0 : }
170 :
171 0 : void Histogram::WriteAscii(bool graph_it, const std::string& newline,
172 : std::string* output) const {
173 : // Get local (stack) copies of all effectively volatile class data so that we
174 : // are consistent across our output activities.
175 0 : SampleSet snapshot;
176 0 : SnapshotSample(&snapshot);
177 :
178 0 : Count sample_count = snapshot.TotalCount();
179 :
180 0 : WriteAsciiHeader(snapshot, sample_count, output);
181 0 : output->append(newline);
182 :
183 : // Prepare to normalize graphical rendering of bucket contents.
184 0 : double max_size = 0;
185 0 : if (graph_it)
186 0 : max_size = GetPeakBucketSize(snapshot);
187 :
188 : // Calculate space needed to print bucket range numbers. Leave room to print
189 : // nearly the largest bucket range without sliding over the histogram.
190 0 : size_t largest_non_empty_bucket = bucket_count() - 1;
191 0 : while (0 == snapshot.counts(largest_non_empty_bucket)) {
192 0 : if (0 == largest_non_empty_bucket)
193 0 : break; // All buckets are empty.
194 0 : --largest_non_empty_bucket;
195 : }
196 :
197 : // Calculate largest print width needed for any of our bucket range displays.
198 0 : size_t print_width = 1;
199 0 : for (size_t i = 0; i < bucket_count(); ++i) {
200 0 : if (snapshot.counts(i)) {
201 0 : size_t width = GetAsciiBucketRange(i).size() + 1;
202 0 : if (width > print_width)
203 0 : print_width = width;
204 : }
205 : }
206 :
207 0 : int64_t remaining = sample_count;
208 0 : int64_t past = 0;
209 : // Output the actual histogram graph.
210 0 : for (size_t i = 0; i < bucket_count(); ++i) {
211 0 : Count current = snapshot.counts(i);
212 0 : if (!current && !PrintEmptyBucket(i))
213 0 : continue;
214 0 : remaining -= current;
215 0 : std::string range = GetAsciiBucketRange(i);
216 0 : output->append(range);
217 0 : for (size_t j = 0; range.size() + j < print_width + 1; ++j)
218 0 : output->push_back(' ');
219 0 : if (0 == current &&
220 0 : i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
221 0 : while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
222 0 : ++i;
223 0 : output->append("... ");
224 0 : output->append(newline);
225 0 : continue; // No reason to plot emptiness.
226 : }
227 0 : double current_size = GetBucketSize(current, i);
228 0 : if (graph_it)
229 0 : WriteAsciiBucketGraph(current_size, max_size, output);
230 0 : WriteAsciiBucketContext(past, current, remaining, i, output);
231 0 : output->append(newline);
232 0 : past += current;
233 : }
234 0 : DCHECK_EQ(sample_count, past);
235 0 : }
236 :
237 : //------------------------------------------------------------------------------
238 : // Methods for the validating a sample and a related histogram.
239 : //------------------------------------------------------------------------------
240 :
241 : Histogram::Inconsistencies
242 0 : Histogram::FindCorruption(const SampleSet& snapshot) const
243 : {
244 0 : int inconsistencies = NO_INCONSISTENCIES;
245 0 : Sample previous_range = -1; // Bottom range is always 0.
246 0 : int64_t count = 0;
247 0 : for (size_t index = 0; index < bucket_count(); ++index) {
248 0 : count += snapshot.counts(index);
249 0 : int new_range = ranges(index);
250 0 : if (previous_range >= new_range)
251 0 : inconsistencies |= BUCKET_ORDER_ERROR;
252 0 : previous_range = new_range;
253 : }
254 :
255 0 : if (!HasValidRangeChecksum())
256 0 : inconsistencies |= RANGE_CHECKSUM_ERROR;
257 :
258 0 : int64_t delta64 = snapshot.redundant_count() - count;
259 0 : if (delta64 != 0) {
260 0 : int delta = static_cast<int>(delta64);
261 0 : if (delta != delta64)
262 0 : delta = INT_MAX; // Flag all giant errors as INT_MAX.
263 : // Since snapshots of histograms are taken asynchronously relative to
264 : // sampling (and snapped from different threads), it is pretty likely that
265 : // we'll catch a redundant count that doesn't match the sample count. We
266 : // allow for a certain amount of slop before flagging this as an
267 : // inconsistency. Even with an inconsistency, we'll snapshot it again (for
268 : // UMA in about a half hour, so we'll eventually get the data, if it was
269 : // not the result of a corruption. If histograms show that 1 is "too tight"
270 : // then we may try to use 2 or 3 for this slop value.
271 0 : const int kCommonRaceBasedCountMismatch = 1;
272 0 : if (delta > 0) {
273 0 : UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
274 0 : if (delta > kCommonRaceBasedCountMismatch)
275 0 : inconsistencies |= COUNT_HIGH_ERROR;
276 : } else {
277 0 : DCHECK_GT(0, delta);
278 0 : UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta);
279 0 : if (-delta > kCommonRaceBasedCountMismatch)
280 0 : inconsistencies |= COUNT_LOW_ERROR;
281 : }
282 : }
283 0 : return static_cast<Inconsistencies>(inconsistencies);
284 : }
285 :
286 1865 : Histogram::ClassType Histogram::histogram_type() const {
287 1865 : return HISTOGRAM;
288 : }
289 :
290 980971 : Histogram::Sample Histogram::ranges(size_t i) const {
291 980971 : return ranges_[i];
292 : }
293 :
294 1434249 : size_t Histogram::bucket_count() const {
295 1434249 : return bucket_count_;
296 : }
297 :
298 15 : void Histogram::SnapshotSample(SampleSet* sample) const {
299 15 : *sample = sample_;
300 15 : }
301 :
302 3429 : bool Histogram::HasConstructorArguments(Sample minimum,
303 : Sample maximum,
304 : size_t bucket_count) {
305 6858 : return ((minimum == declared_min_) && (maximum == declared_max_) &&
306 6858 : (bucket_count == bucket_count_));
307 : }
308 :
309 0 : bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum,
310 : TimeDelta maximum,
311 : size_t bucket_count) {
312 0 : return ((minimum.InMilliseconds() == declared_min_) &&
313 0 : (maximum.InMilliseconds() == declared_max_) &&
314 0 : (bucket_count == bucket_count_));
315 : }
316 :
317 4327 : bool Histogram::HasValidRangeChecksum() const {
318 4327 : return CalculateRangeChecksum() == range_checksum_;
319 : }
320 :
321 0 : size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
322 : {
323 0 : size_t n = 0;
324 0 : n += aMallocSizeOf(this);
325 : // We're not allowed to do deep dives into STL data structures. This
326 : // is as close as we can get to measuring this array.
327 0 : n += aMallocSizeOf(&ranges_[0]);
328 0 : n += sample_.SizeOfExcludingThis(aMallocSizeOf);
329 0 : return n;
330 : }
331 :
332 : size_t
333 0 : Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
334 : {
335 : // We're not allowed to do deep dives into STL data structures. This
336 : // is as close as we can get to measuring this array.
337 0 : return aMallocSizeOf(&counts_[0]);
338 : }
339 :
340 4327 : Histogram::Histogram(const std::string& name, Sample minimum,
341 4327 : Sample maximum, size_t bucket_count)
342 : : sample_(),
343 : histogram_name_(name),
344 : declared_min_(minimum),
345 : declared_max_(maximum),
346 : bucket_count_(bucket_count),
347 : flags_(kNoFlags),
348 : ranges_(bucket_count + 1, 0),
349 : range_checksum_(0),
350 4327 : recording_enabled_(true) {
351 4327 : Initialize();
352 4327 : }
353 :
354 0 : Histogram::Histogram(const std::string& name, TimeDelta minimum,
355 0 : TimeDelta maximum, size_t bucket_count)
356 : : sample_(),
357 : histogram_name_(name),
358 0 : declared_min_(static_cast<int> (minimum.InMilliseconds())),
359 0 : declared_max_(static_cast<int> (maximum.InMilliseconds())),
360 : bucket_count_(bucket_count),
361 : flags_(kNoFlags),
362 : ranges_(bucket_count + 1, 0),
363 : range_checksum_(0),
364 0 : recording_enabled_(true) {
365 0 : Initialize();
366 0 : }
367 :
368 0 : Histogram::~Histogram() {
369 0 : if (StatisticsRecorder::dump_on_exit()) {
370 0 : std::string output;
371 0 : WriteAscii(true, "\n", &output);
372 0 : CHROMIUM_LOG(INFO) << output;
373 : }
374 :
375 : // Just to make sure most derived class did this properly...
376 0 : DCHECK(ValidateBucketRanges());
377 0 : }
378 :
379 : // Calculate what range of values are held in each bucket.
380 : // We have to be careful that we don't pick a ratio between starting points in
381 : // consecutive buckets that is sooo small, that the integer bounds are the same
382 : // (effectively making one bucket get no values). We need to avoid:
383 : // ranges_[i] == ranges_[i + 1]
384 : // To avoid that, we just do a fine-grained bucket width as far as we need to
385 : // until we get a ratio that moves us along at least 2 units at a time. From
386 : // that bucket onward we do use the exponential growth of buckets.
387 1862 : void Histogram::InitializeBucketRange() {
388 1862 : double log_max = log(static_cast<double>(declared_max()));
389 : double log_ratio;
390 : double log_next;
391 1862 : size_t bucket_index = 1;
392 1862 : Sample current = declared_min();
393 1862 : SetBucketRange(bucket_index, current);
394 425552 : while (bucket_count() > ++bucket_index) {
395 : double log_current;
396 211845 : log_current = log(static_cast<double>(current));
397 : // Calculate the count'th root of the range.
398 211845 : log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
399 : // See where the next bucket would start.
400 211845 : log_next = log_current + log_ratio;
401 : int next;
402 211845 : next = static_cast<int>(floor(exp(log_next) + 0.5));
403 211845 : if (next > current)
404 199801 : current = next;
405 : else
406 12044 : ++current; // Just do a narrow bucket, and keep trying.
407 211845 : SetBucketRange(bucket_index, current);
408 : }
409 1862 : ResetRangeChecksum();
410 :
411 1862 : DCHECK_EQ(bucket_count(), bucket_index);
412 1862 : }
413 :
414 0 : bool Histogram::PrintEmptyBucket(size_t index) const {
415 0 : return true;
416 : }
417 :
418 327 : size_t Histogram::BucketIndex(Sample value) const {
419 : // Use simple binary search. This is very general, but there are better
420 : // approaches if we knew that the buckets were linearly distributed.
421 327 : DCHECK_LE(ranges(0), value);
422 327 : DCHECK_GT(ranges(bucket_count()), value);
423 327 : size_t under = 0;
424 327 : size_t over = bucket_count();
425 : size_t mid;
426 :
427 : do {
428 1999 : DCHECK_GE(over, under);
429 1163 : mid = under + (over - under)/2;
430 1163 : if (mid == under)
431 327 : break;
432 836 : if (ranges(mid) <= value)
433 178 : under = mid;
434 : else
435 658 : over = mid;
436 : } while (true);
437 :
438 327 : DCHECK_LE(ranges(mid), value);
439 327 : CHECK_GT(ranges(mid+1), value);
440 327 : return mid;
441 : }
442 :
443 : // Use the actual bucket widths (like a linear histogram) until the widths get
444 : // over some transition value, and then use that transition width. Exponentials
445 : // get so big so fast (and we don't expect to see a lot of entries in the large
446 : // buckets), so we need this to make it possible to see what is going on and
447 : // not have 0-graphical-height buckets.
448 0 : double Histogram::GetBucketSize(Count current, size_t i) const {
449 0 : DCHECK_GT(ranges(i + 1), ranges(i));
450 : static const double kTransitionWidth = 5;
451 0 : double denominator = ranges(i + 1) - ranges(i);
452 0 : if (denominator > kTransitionWidth)
453 0 : denominator = kTransitionWidth; // Stop trying to normalize.
454 0 : return current/denominator;
455 : }
456 :
457 4327 : void Histogram::ResetRangeChecksum() {
458 4327 : range_checksum_ = CalculateRangeChecksum();
459 4327 : }
460 :
461 0 : const std::string Histogram::GetAsciiBucketRange(size_t i) const {
462 0 : std::string result;
463 0 : if (kHexRangePrintingFlag & flags_)
464 0 : StringAppendF(&result, "%#x", ranges(i));
465 : else
466 0 : StringAppendF(&result, "%d", ranges(i));
467 0 : return result;
468 : }
469 :
470 : // Update histogram data with new sample.
471 56 : void Histogram::Accumulate(Sample value, Count count, size_t index) {
472 56 : sample_.Accumulate(value, count, index);
473 56 : }
474 :
475 322046 : void Histogram::SetBucketRange(size_t i, Sample value) {
476 322046 : DCHECK_GT(bucket_count_, i);
477 322046 : ranges_[i] = value;
478 322046 : }
479 :
480 0 : bool Histogram::ValidateBucketRanges() const {
481 : // Standard assertions that all bucket ranges should satisfy.
482 0 : DCHECK_EQ(bucket_count_ + 1, ranges_.size());
483 0 : DCHECK_EQ(0, ranges_[0]);
484 0 : DCHECK_EQ(declared_min(), ranges_[1]);
485 0 : DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]);
486 0 : DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]);
487 0 : return true;
488 : }
489 :
490 8654 : uint32_t Histogram::CalculateRangeChecksum() const {
491 8654 : DCHECK_EQ(ranges_.size(), bucket_count() + 1);
492 8654 : uint32_t checksum = static_cast<uint32_t>(ranges_.size()); // Seed checksum.
493 661400 : for (size_t index = 0; index < bucket_count(); ++index)
494 652746 : checksum = Crc32(checksum, ranges(index));
495 8654 : return checksum;
496 : }
497 :
498 4327 : void Histogram::Initialize() {
499 4327 : sample_.Resize(*this);
500 4327 : if (declared_min_ < 1)
501 0 : declared_min_ = 1;
502 4327 : if (declared_max_ > kSampleType_MAX - 1)
503 0 : declared_max_ = kSampleType_MAX - 1;
504 4327 : DCHECK_LE(declared_min_, declared_max_);
505 4327 : DCHECK_GT(bucket_count_, 1u);
506 4327 : CHECK_LT(bucket_count_, kBucketCount_MAX);
507 4327 : size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
508 4327 : DCHECK_LE(bucket_count_, maximal_bucket_count);
509 4327 : DCHECK_EQ(0, ranges_[0]);
510 4327 : ranges_[bucket_count_] = kSampleType_MAX;
511 4327 : }
512 :
513 : // We generate the CRC-32 using the low order bits to select whether to XOR in
514 : // the reversed polynomial 0xedb88320L. This is nice and simple, and allows us
515 : // to keep the quotient in a uint32_t. Since we're not concerned about the nature
516 : // of corruptions (i.e., we don't care about bit sequencing, since we are
517 : // handling memory changes, which are more grotesque) so we don't bother to
518 : // get the CRC correct for big-endian vs little-ending calculations. All we
519 : // need is a nice hash, that tends to depend on all the bits of the sample, with
520 : // very little chance of changes in one place impacting changes in another
521 : // place.
522 652746 : uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
523 652746 : const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats.
524 : if (kUseRealCrc) {
525 : union {
526 : Histogram::Sample range;
527 : unsigned char bytes[sizeof(Histogram::Sample)];
528 : } converter;
529 652746 : converter.range = range;
530 3263730 : for (size_t i = 0; i < sizeof(converter); ++i)
531 2610984 : sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
532 : } else {
533 : // Use hash techniques provided in ReallyFastHash, except we don't care
534 : // about "avalanching" (which would worsten the hash, and add collisions),
535 : // and we don't care about edge cases since we have an even number of bytes.
536 : union {
537 : Histogram::Sample range;
538 : uint16_t ints[sizeof(Histogram::Sample) / 2];
539 : } converter;
540 : DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter));
541 : converter.range = range;
542 : sum += converter.ints[0];
543 : sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11);
544 : sum += sum >> 11;
545 : }
546 652746 : return sum;
547 : }
548 :
549 : //------------------------------------------------------------------------------
550 : // Private methods
551 :
552 0 : double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
553 0 : double max = 0;
554 0 : for (size_t i = 0; i < bucket_count() ; ++i) {
555 : double current_size
556 0 : = GetBucketSize(snapshot.counts(i), i);
557 0 : if (current_size > max)
558 0 : max = current_size;
559 : }
560 0 : return max;
561 : }
562 :
563 0 : void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
564 : Count sample_count,
565 : std::string* output) const {
566 0 : StringAppendF(output,
567 : "Histogram: %s recorded %d samples",
568 0 : histogram_name().c_str(),
569 0 : sample_count);
570 0 : int64_t snapshot_sum = snapshot.sum();
571 0 : if (0 == sample_count) {
572 0 : DCHECK_EQ(snapshot_sum, 0);
573 : } else {
574 0 : double average = static_cast<float>(snapshot_sum) / sample_count;
575 :
576 0 : StringAppendF(output, ", average = %.1f", average);
577 : }
578 0 : if (flags_ & ~kHexRangePrintingFlag)
579 0 : StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
580 0 : }
581 :
582 0 : void Histogram::WriteAsciiBucketContext(const int64_t past,
583 : const Count current,
584 : const int64_t remaining,
585 : const size_t i,
586 : std::string* output) const {
587 0 : double scaled_sum = (past + current + remaining) / 100.0;
588 0 : WriteAsciiBucketValue(current, scaled_sum, output);
589 0 : if (0 < i) {
590 0 : double percentage = past / scaled_sum;
591 0 : StringAppendF(output, " {%3.1f%%}", percentage);
592 : }
593 0 : }
594 :
595 0 : void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
596 : std::string* output) const {
597 0 : StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
598 0 : }
599 :
600 0 : void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
601 : std::string* output) const {
602 0 : const int k_line_length = 72; // Maximal horizontal width of graph.
603 0 : int x_count = static_cast<int>(k_line_length * (current_size / max_size)
604 0 : + 0.5);
605 0 : int x_remainder = k_line_length - x_count;
606 :
607 0 : while (0 < x_count--)
608 0 : output->append("-");
609 0 : output->append("O");
610 0 : while (0 < x_remainder--)
611 0 : output->append(" ");
612 0 : }
613 :
614 : //------------------------------------------------------------------------------
615 : // Methods for the Histogram::SampleSet class
616 : //------------------------------------------------------------------------------
617 :
618 4342 : Histogram::SampleSet::SampleSet()
619 : : counts_(),
620 : sum_(0),
621 4342 : redundant_count_(0) {
622 4342 : }
623 :
624 15 : Histogram::SampleSet::~SampleSet() {
625 15 : }
626 :
627 4327 : void Histogram::SampleSet::Resize(const Histogram& histogram) {
628 4327 : counts_.resize(histogram.bucket_count(), 0);
629 4327 : }
630 :
631 315 : void Histogram::SampleSet::Accumulate(Sample value, Count count,
632 : size_t index) {
633 315 : DCHECK(count == 1 || count == -1);
634 315 : counts_[index] += count;
635 315 : redundant_count_ += count;
636 315 : sum_ += static_cast<int64_t>(count) * value;
637 315 : DCHECK_GE(counts_[index], 0);
638 315 : DCHECK_GE(sum_, 0);
639 315 : DCHECK_GE(redundant_count_, 0);
640 315 : }
641 :
642 0 : Count Histogram::SampleSet::TotalCount() const {
643 0 : Count total = 0;
644 0 : for (Counts::const_iterator it = counts_.begin();
645 0 : it != counts_.end();
646 : ++it) {
647 0 : total += *it;
648 : }
649 0 : return total;
650 : }
651 :
652 15 : void Histogram::SampleSet::Add(const SampleSet& other) {
653 15 : DCHECK_EQ(counts_.size(), other.counts_.size());
654 15 : sum_ += other.sum_;
655 15 : redundant_count_ += other.redundant_count_;
656 594 : for (size_t index = 0; index < counts_.size(); ++index)
657 579 : counts_[index] += other.counts_[index];
658 15 : }
659 :
660 : //------------------------------------------------------------------------------
661 : // LinearHistogram: This histogram uses a traditional set of evenly spaced
662 : // buckets.
663 : //------------------------------------------------------------------------------
664 :
665 0 : LinearHistogram::~LinearHistogram() {
666 0 : }
667 :
668 1567 : Histogram* LinearHistogram::FactoryGet(const std::string& name,
669 : Sample minimum,
670 : Sample maximum,
671 : size_t bucket_count,
672 : Flags flags) {
673 1567 : Histogram* histogram(NULL);
674 :
675 1567 : if (minimum < 1)
676 0 : minimum = 1;
677 1567 : if (maximum > kSampleType_MAX - 1)
678 0 : maximum = kSampleType_MAX - 1;
679 :
680 1567 : if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
681 : LinearHistogram* tentative_histogram =
682 1038 : new LinearHistogram(name, minimum, maximum, bucket_count);
683 1038 : tentative_histogram->InitializeBucketRange();
684 1038 : tentative_histogram->SetFlags(flags);
685 1038 : histogram =
686 1038 : StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
687 : }
688 :
689 1567 : DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type());
690 1567 : DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count));
691 1567 : return histogram;
692 : }
693 :
694 0 : Histogram* LinearHistogram::FactoryTimeGet(const std::string& name,
695 : TimeDelta minimum,
696 : TimeDelta maximum,
697 : size_t bucket_count,
698 : Flags flags) {
699 0 : return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
700 0 : bucket_count, flags);
701 : }
702 :
703 1569 : Histogram::ClassType LinearHistogram::histogram_type() const {
704 1569 : return LINEAR_HISTOGRAM;
705 : }
706 :
707 259 : void LinearHistogram::Accumulate(Sample value, Count count, size_t index) {
708 259 : sample_.Accumulate(value, count, index);
709 259 : }
710 :
711 0 : void LinearHistogram::SetRangeDescriptions(
712 : const DescriptionPair descriptions[]) {
713 0 : for (int i =0; descriptions[i].description; ++i) {
714 0 : bucket_description_[descriptions[i].sample] = descriptions[i].description;
715 : }
716 0 : }
717 :
718 2465 : LinearHistogram::LinearHistogram(const std::string& name,
719 : Sample minimum,
720 : Sample maximum,
721 2465 : size_t bucket_count)
722 2465 : : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
723 2465 : }
724 :
725 0 : LinearHistogram::LinearHistogram(const std::string& name,
726 : TimeDelta minimum,
727 : TimeDelta maximum,
728 0 : size_t bucket_count)
729 0 : : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
730 : minimum : TimeDelta::FromMilliseconds(1),
731 0 : maximum, bucket_count) {
732 0 : }
733 :
734 2465 : void LinearHistogram::InitializeBucketRange() {
735 2465 : DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here.
736 2465 : double min = declared_min();
737 2465 : double max = declared_max();
738 : size_t i;
739 110804 : for (i = 1; i < bucket_count(); ++i) {
740 216678 : double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
741 216678 : (bucket_count() - 2);
742 108339 : SetBucketRange(i, static_cast<int> (linear_range + 0.5));
743 : }
744 2465 : ResetRangeChecksum();
745 2465 : }
746 :
747 0 : double LinearHistogram::GetBucketSize(Count current, size_t i) const {
748 0 : DCHECK_GT(ranges(i + 1), ranges(i));
749 : // Adjacent buckets with different widths would have "surprisingly" many (few)
750 : // samples in a histogram if we didn't normalize this way.
751 0 : double denominator = ranges(i + 1) - ranges(i);
752 0 : return current/denominator;
753 : }
754 :
755 0 : const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
756 0 : int range = ranges(i);
757 0 : BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
758 0 : if (it == bucket_description_.end())
759 0 : return Histogram::GetAsciiBucketRange(i);
760 0 : return it->second;
761 : }
762 :
763 0 : bool LinearHistogram::PrintEmptyBucket(size_t index) const {
764 0 : return bucket_description_.find(ranges(index)) == bucket_description_.end();
765 : }
766 :
767 :
768 : //------------------------------------------------------------------------------
769 : // This section provides implementation for BooleanHistogram.
770 : //------------------------------------------------------------------------------
771 :
772 930 : Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) {
773 930 : Histogram* histogram(NULL);
774 :
775 930 : if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
776 930 : BooleanHistogram* tentative_histogram = new BooleanHistogram(name);
777 930 : tentative_histogram->InitializeBucketRange();
778 930 : tentative_histogram->SetFlags(flags);
779 930 : histogram =
780 930 : StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
781 : }
782 :
783 930 : DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type());
784 930 : return histogram;
785 : }
786 :
787 934 : Histogram::ClassType BooleanHistogram::histogram_type() const {
788 934 : return BOOLEAN_HISTOGRAM;
789 : }
790 :
791 0 : void BooleanHistogram::AddBoolean(bool value) {
792 0 : Add(value ? 1 : 0);
793 0 : }
794 :
795 1071 : BooleanHistogram::BooleanHistogram(const std::string& name)
796 1071 : : LinearHistogram(name, 1, 2, 3) {
797 1071 : }
798 :
799 : void
800 2 : BooleanHistogram::Accumulate(Sample value, Count count, size_t index)
801 : {
802 : // Callers will have computed index based on the non-booleanified value.
803 : // So we need to adjust the index manually.
804 2 : LinearHistogram::Accumulate(!!value, count, value ? 1 : 0);
805 2 : }
806 :
807 : //------------------------------------------------------------------------------
808 : // FlagHistogram:
809 : //------------------------------------------------------------------------------
810 :
811 : Histogram *
812 141 : FlagHistogram::FactoryGet(const std::string &name, Flags flags)
813 : {
814 141 : Histogram *h(nullptr);
815 :
816 141 : if (!StatisticsRecorder::FindHistogram(name, &h)) {
817 141 : FlagHistogram *fh = new FlagHistogram(name);
818 141 : fh->InitializeBucketRange();
819 141 : fh->SetFlags(flags);
820 141 : size_t zero_index = fh->BucketIndex(0);
821 141 : fh->LinearHistogram::Accumulate(0, 1, zero_index);
822 141 : h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh);
823 : }
824 :
825 141 : return h;
826 : }
827 :
828 141 : FlagHistogram::FlagHistogram(const std::string &name)
829 141 : : BooleanHistogram(name), mSwitched(false) {
830 141 : }
831 :
832 : Histogram::ClassType
833 0 : FlagHistogram::histogram_type() const
834 : {
835 0 : return FLAG_HISTOGRAM;
836 : }
837 :
838 : void
839 0 : FlagHistogram::Accumulate(Sample value, Count count, size_t index)
840 : {
841 0 : if (mSwitched) {
842 0 : return;
843 : }
844 :
845 0 : mSwitched = true;
846 0 : DCHECK_EQ(value, 1);
847 0 : LinearHistogram::Accumulate(value, 1, index);
848 0 : size_t zero_index = BucketIndex(0);
849 0 : LinearHistogram::Accumulate(0, -1, zero_index);
850 : }
851 :
852 : void
853 0 : FlagHistogram::AddSampleSet(const SampleSet& sample) {
854 0 : DCHECK_EQ(bucket_count(), sample.size());
855 : // We can't be sure the SampleSet provided came from another FlagHistogram,
856 : // so we take the following steps:
857 : // - If our flag has already been set do nothing.
858 : // - Set our flag if the following hold:
859 : // - The sum of the counts in the provided SampleSet is 1.
860 : // - The bucket index for that single value is the same as the index where we
861 : // would place our set flag.
862 : // - Otherwise, take no action.
863 :
864 0 : if (mSwitched) {
865 0 : return;
866 : }
867 :
868 0 : if (sample.sum() != 1) {
869 0 : return;
870 : }
871 :
872 0 : size_t one_index = BucketIndex(1);
873 0 : if (sample.counts(one_index) == 1) {
874 0 : Accumulate(1, 1, one_index);
875 : }
876 : }
877 :
878 : void
879 0 : FlagHistogram::Clear() {
880 0 : Histogram::Clear();
881 :
882 0 : mSwitched = false;
883 0 : size_t zero_index = BucketIndex(0);
884 0 : LinearHistogram::Accumulate(0, 1, zero_index);
885 0 : }
886 :
887 : //------------------------------------------------------------------------------
888 : // CountHistogram:
889 : //------------------------------------------------------------------------------
890 :
891 : Histogram *
892 356 : CountHistogram::FactoryGet(const std::string &name, Flags flags)
893 : {
894 356 : Histogram *h(nullptr);
895 :
896 356 : if (!StatisticsRecorder::FindHistogram(name, &h)) {
897 356 : CountHistogram *fh = new CountHistogram(name);
898 356 : fh->InitializeBucketRange();
899 356 : fh->SetFlags(flags);
900 356 : h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh);
901 : }
902 :
903 356 : return h;
904 : }
905 :
906 356 : CountHistogram::CountHistogram(const std::string &name)
907 356 : : LinearHistogram(name, 1, 2, 3) {
908 356 : }
909 :
910 : Histogram::ClassType
911 0 : CountHistogram::histogram_type() const
912 : {
913 0 : return COUNT_HISTOGRAM;
914 : }
915 :
916 : void
917 12 : CountHistogram::Accumulate(Sample value, Count count, size_t index)
918 : {
919 12 : size_t zero_index = BucketIndex(0);
920 12 : LinearHistogram::Accumulate(value, 1, zero_index);
921 12 : }
922 :
923 : void
924 0 : CountHistogram::AddSampleSet(const SampleSet& sample) {
925 0 : DCHECK_EQ(bucket_count(), sample.size());
926 : // We can't be sure the SampleSet provided came from another CountHistogram,
927 : // so we at least check that the unused buckets are empty.
928 :
929 0 : const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) };
930 :
931 0 : if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) {
932 0 : return;
933 : }
934 :
935 0 : if (sample.counts(indices[0]) != 0) {
936 0 : Accumulate(1, sample.counts(indices[0]), indices[0]);
937 : }
938 : }
939 :
940 :
941 : //------------------------------------------------------------------------------
942 : // CustomHistogram:
943 : //------------------------------------------------------------------------------
944 :
945 0 : Histogram* CustomHistogram::FactoryGet(const std::string& name,
946 : const std::vector<Sample>& custom_ranges,
947 : Flags flags) {
948 0 : Histogram* histogram(NULL);
949 :
950 : // Remove the duplicates in the custom ranges array.
951 0 : std::vector<int> ranges = custom_ranges;
952 0 : ranges.push_back(0); // Ensure we have a zero value.
953 0 : std::sort(ranges.begin(), ranges.end());
954 0 : ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
955 0 : if (ranges.size() <= 1) {
956 0 : DCHECK(false);
957 : // Note that we pushed a 0 in above, so for defensive code....
958 0 : ranges.push_back(1); // Put in some data so we can index to [1].
959 : }
960 :
961 0 : DCHECK_LT(ranges.back(), kSampleType_MAX);
962 :
963 0 : if (!StatisticsRecorder::FindHistogram(name, &histogram)) {
964 0 : CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges);
965 0 : tentative_histogram->InitializedCustomBucketRange(ranges);
966 0 : tentative_histogram->SetFlags(flags);
967 0 : histogram =
968 0 : StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
969 : }
970 :
971 0 : DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM);
972 0 : DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(),
973 0 : ranges.size()));
974 0 : return histogram;
975 : }
976 :
977 0 : Histogram::ClassType CustomHistogram::histogram_type() const {
978 0 : return CUSTOM_HISTOGRAM;
979 : }
980 :
981 0 : CustomHistogram::CustomHistogram(const std::string& name,
982 0 : const std::vector<Sample>& custom_ranges)
983 0 : : Histogram(name, custom_ranges[1], custom_ranges.back(),
984 0 : custom_ranges.size()) {
985 0 : DCHECK_GT(custom_ranges.size(), 1u);
986 0 : DCHECK_EQ(custom_ranges[0], 0);
987 0 : }
988 :
989 0 : void CustomHistogram::InitializedCustomBucketRange(
990 : const std::vector<Sample>& custom_ranges) {
991 0 : DCHECK_GT(custom_ranges.size(), 1u);
992 0 : DCHECK_EQ(custom_ranges[0], 0);
993 0 : DCHECK_LE(custom_ranges.size(), bucket_count());
994 0 : for (size_t index = 0; index < custom_ranges.size(); ++index)
995 0 : SetBucketRange(index, custom_ranges[index]);
996 0 : ResetRangeChecksum();
997 0 : }
998 :
999 0 : double CustomHistogram::GetBucketSize(Count current, size_t i) const {
1000 0 : return 1;
1001 : }
1002 :
1003 : //------------------------------------------------------------------------------
1004 : // The next section handles global (central) support for all histograms, as well
1005 : // as startup/teardown of this service.
1006 : //------------------------------------------------------------------------------
1007 :
1008 : // This singleton instance should be started during the single threaded portion
1009 : // of main(), and hence it is not thread safe. It initializes globals to
1010 : // provide support for all future calls.
1011 3 : StatisticsRecorder::StatisticsRecorder() {
1012 3 : DCHECK(!histograms_);
1013 3 : if (lock_ == NULL) {
1014 : // This will leak on purpose. It's the only way to make sure we won't race
1015 : // against the static uninitialization of the module while one of our
1016 : // static methods relying on the lock get called at an inappropriate time
1017 : // during the termination phase. Since it's a static data member, we will
1018 : // leak one per process, which would be similar to the instance allocated
1019 : // during static initialization and released only on process termination.
1020 3 : lock_ = new base::Lock;
1021 : }
1022 6 : base::AutoLock auto_lock(*lock_);
1023 3 : histograms_ = new HistogramMap;
1024 3 : }
1025 :
1026 0 : StatisticsRecorder::~StatisticsRecorder() {
1027 0 : DCHECK(histograms_ && lock_);
1028 :
1029 0 : if (dump_on_exit_) {
1030 0 : std::string output;
1031 0 : WriteGraph("", &output);
1032 0 : CHROMIUM_LOG(INFO) << output;
1033 : }
1034 : // Clean up.
1035 0 : HistogramMap* histograms = NULL;
1036 : {
1037 0 : base::AutoLock auto_lock(*lock_);
1038 0 : histograms = histograms_;
1039 0 : histograms_ = NULL;
1040 0 : for (HistogramMap::iterator it = histograms->begin();
1041 0 : histograms->end() != it;
1042 : ++it) {
1043 : // No other clients permanently hold Histogram references, so we
1044 : // have the only one and it is safe to delete it.
1045 0 : delete it->second;
1046 : }
1047 : }
1048 0 : delete histograms;
1049 : // We don't delete lock_ on purpose to avoid having to properly protect
1050 : // against it going away after we checked for NULL in the static methods.
1051 0 : }
1052 :
1053 : // static
1054 0 : bool StatisticsRecorder::IsActive() {
1055 0 : if (lock_ == NULL)
1056 0 : return false;
1057 0 : base::AutoLock auto_lock(*lock_);
1058 0 : return NULL != histograms_;
1059 : }
1060 :
1061 4327 : Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) {
1062 4327 : DCHECK(histogram->HasValidRangeChecksum());
1063 4327 : if (lock_ == NULL)
1064 0 : return histogram;
1065 8654 : base::AutoLock auto_lock(*lock_);
1066 4327 : if (!histograms_)
1067 0 : return histogram;
1068 8654 : const std::string name = histogram->histogram_name();
1069 4327 : HistogramMap::iterator it = histograms_->find(name);
1070 : // Avoid overwriting a previous registration.
1071 4327 : if (histograms_->end() == it) {
1072 4327 : (*histograms_)[name] = histogram;
1073 : } else {
1074 0 : delete histogram; // We already have one by this name.
1075 0 : histogram = it->second;
1076 : }
1077 4327 : return histogram;
1078 : }
1079 :
1080 : // static
1081 0 : void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
1082 : std::string* output) {
1083 0 : if (!IsActive())
1084 0 : return;
1085 0 : output->append("<html><head><title>About Histograms");
1086 0 : if (!query.empty())
1087 0 : output->append(" - " + query);
1088 : output->append("</title>"
1089 : // We'd like the following no-cache... but it doesn't work.
1090 : // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
1091 0 : "</head><body>");
1092 :
1093 0 : Histograms snapshot;
1094 0 : GetSnapshot(query, &snapshot);
1095 0 : for (Histograms::iterator it = snapshot.begin();
1096 0 : it != snapshot.end();
1097 : ++it) {
1098 0 : (*it)->WriteHTMLGraph(output);
1099 0 : output->append("<br><hr><br>");
1100 : }
1101 0 : output->append("</body></html>");
1102 : }
1103 :
1104 : // static
1105 0 : void StatisticsRecorder::WriteGraph(const std::string& query,
1106 : std::string* output) {
1107 0 : if (!IsActive())
1108 0 : return;
1109 0 : if (query.length())
1110 0 : StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
1111 : else
1112 0 : output->append("Collections of all histograms\n");
1113 :
1114 0 : Histograms snapshot;
1115 0 : GetSnapshot(query, &snapshot);
1116 0 : for (Histograms::iterator it = snapshot.begin();
1117 0 : it != snapshot.end();
1118 : ++it) {
1119 0 : (*it)->WriteAscii(true, "\n", output);
1120 0 : output->append("\n");
1121 : }
1122 : }
1123 :
1124 : // static
1125 0 : void StatisticsRecorder::GetHistograms(Histograms* output) {
1126 0 : if (lock_ == NULL)
1127 0 : return;
1128 0 : base::AutoLock auto_lock(*lock_);
1129 0 : if (!histograms_)
1130 0 : return;
1131 0 : for (HistogramMap::iterator it = histograms_->begin();
1132 0 : histograms_->end() != it;
1133 : ++it) {
1134 0 : DCHECK_EQ(it->first, it->second->histogram_name());
1135 0 : output->push_back(it->second);
1136 : }
1137 : }
1138 :
1139 4856 : bool StatisticsRecorder::FindHistogram(const std::string& name,
1140 : Histogram** histogram) {
1141 4856 : if (lock_ == NULL)
1142 0 : return false;
1143 9712 : base::AutoLock auto_lock(*lock_);
1144 4856 : if (!histograms_)
1145 0 : return false;
1146 4856 : HistogramMap::iterator it = histograms_->find(name);
1147 4856 : if (histograms_->end() == it)
1148 4327 : return false;
1149 529 : *histogram = it->second;
1150 529 : return true;
1151 : }
1152 :
1153 : // private static
1154 0 : void StatisticsRecorder::GetSnapshot(const std::string& query,
1155 : Histograms* snapshot) {
1156 0 : if (lock_ == NULL)
1157 0 : return;
1158 0 : base::AutoLock auto_lock(*lock_);
1159 0 : if (!histograms_)
1160 0 : return;
1161 0 : for (HistogramMap::iterator it = histograms_->begin();
1162 0 : histograms_->end() != it;
1163 : ++it) {
1164 0 : if (it->first.find(query) != std::string::npos)
1165 0 : snapshot->push_back(it->second);
1166 : }
1167 : }
1168 :
1169 : // static
1170 : StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
1171 : // static
1172 : base::Lock* StatisticsRecorder::lock_ = NULL;
1173 : // static
1174 : bool StatisticsRecorder::dump_on_exit_ = false;
1175 :
1176 : } // namespace base
|