[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 1 | // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 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/histogram.h" |
| 11 | |
| 12 | #include <math.h> |
| 13 | #include <string> |
| 14 | |
[email protected] | 4dcbc1b | 2010-07-16 20:30:47 | [diff] [blame] | 15 | #include "base/lock.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 16 | #include "base/logging.h" |
[email protected] | 3f38385 | 2009-04-03 18:18:55 | [diff] [blame] | 17 | #include "base/pickle.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 18 | #include "base/string_util.h" |
| 19 | |
[email protected] | e1acf6f | 2008-10-27 20:43:33 | [diff] [blame] | 20 | using base::TimeDelta; |
| 21 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 22 | typedef Histogram::Count Count; |
| 23 | |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 24 | scoped_refptr<Histogram> Histogram::FactoryGet(const std::string& name, |
| 25 | Sample minimum, Sample maximum, size_t bucket_count, Flags flags) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 26 | scoped_refptr<Histogram> histogram(NULL); |
| 27 | |
| 28 | // Defensive code. |
| 29 | if (minimum <= 0) |
| 30 | minimum = 1; |
| 31 | if (maximum >= kSampleType_MAX) |
| 32 | maximum = kSampleType_MAX - 1; |
| 33 | |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 34 | if (!StatisticsRecorder::FindHistogram(name, &histogram)) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 35 | histogram = new Histogram(name, minimum, maximum, bucket_count); |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 36 | StatisticsRecorder::FindHistogram(name, &histogram); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 37 | } |
| 38 | |
| 39 | DCHECK(HISTOGRAM == histogram->histogram_type()); |
| 40 | DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 41 | histogram->SetFlags(flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 42 | return histogram; |
| 43 | } |
| 44 | |
[email protected] | a764bf5e | 2010-06-02 21:31:44 | [diff] [blame] | 45 | scoped_refptr<Histogram> Histogram::FactoryTimeGet(const std::string& name, |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 46 | base::TimeDelta minimum, base::TimeDelta maximum, size_t bucket_count, |
| 47 | Flags flags) { |
| 48 | return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), |
| 49 | bucket_count, flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | Histogram::Histogram(const std::string& name, Sample minimum, |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 53 | Sample maximum, size_t bucket_count) |
[email protected] | e6f02ab | 2009-04-10 22:29:29 | [diff] [blame] | 54 | : histogram_name_(name), |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 55 | declared_min_(minimum), |
| 56 | declared_max_(maximum), |
| 57 | bucket_count_(bucket_count), |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 58 | flags_(kNoFlags), |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 59 | ranges_(bucket_count + 1, 0), |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 60 | sample_() { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 61 | Initialize(); |
| 62 | } |
| 63 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 64 | Histogram::Histogram(const std::string& name, TimeDelta minimum, |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 65 | TimeDelta maximum, size_t bucket_count) |
[email protected] | e6f02ab | 2009-04-10 22:29:29 | [diff] [blame] | 66 | : histogram_name_(name), |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 67 | declared_min_(static_cast<int> (minimum.InMilliseconds())), |
| 68 | declared_max_(static_cast<int> (maximum.InMilliseconds())), |
| 69 | bucket_count_(bucket_count), |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 70 | flags_(kNoFlags), |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 71 | ranges_(bucket_count + 1, 0), |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 72 | sample_() { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 73 | Initialize(); |
| 74 | } |
| 75 | |
| 76 | Histogram::~Histogram() { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 77 | if (StatisticsRecorder::dump_on_exit()) { |
| 78 | std::string output; |
| 79 | WriteAscii(true, "\n", &output); |
| 80 | LOG(INFO) << output; |
| 81 | } |
| 82 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 83 | // Just to make sure most derived class did this properly... |
| 84 | DCHECK(ValidateBucketRanges()); |
| 85 | } |
| 86 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 87 | bool Histogram::PrintEmptyBucket(size_t index) const { |
| 88 | return true; |
| 89 | } |
| 90 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 91 | void Histogram::Add(int value) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 92 | if (value >= kSampleType_MAX) |
| 93 | value = kSampleType_MAX - 1; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 94 | if (value < 0) |
| 95 | value = 0; |
| 96 | size_t index = BucketIndex(value); |
| 97 | DCHECK(value >= ranges(index)); |
| 98 | DCHECK(value < ranges(index + 1)); |
| 99 | Accumulate(value, 1, index); |
| 100 | } |
| 101 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 102 | void Histogram::AddBoolean(bool value) { |
| 103 | DCHECK(false); |
| 104 | } |
| 105 | |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 106 | void Histogram::AddSampleSet(const SampleSet& sample) { |
| 107 | sample_.Add(sample); |
| 108 | } |
| 109 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 110 | void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { |
| 111 | DCHECK(false); |
| 112 | } |
| 113 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 114 | // The following methods provide a graphical histogram display. |
| 115 | void Histogram::WriteHTMLGraph(std::string* output) const { |
| 116 | // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. |
| 117 | output->append("<PRE>"); |
| 118 | WriteAscii(true, "<br>", output); |
| 119 | output->append("</PRE>"); |
| 120 | } |
| 121 | |
| 122 | void Histogram::WriteAscii(bool graph_it, const std::string& newline, |
| 123 | std::string* output) const { |
| 124 | // Get local (stack) copies of all effectively volatile class data so that we |
| 125 | // are consistent across our output activities. |
| 126 | SampleSet snapshot; |
| 127 | SnapshotSample(&snapshot); |
| 128 | Count sample_count = snapshot.TotalCount(); |
| 129 | |
| 130 | WriteAsciiHeader(snapshot, sample_count, output); |
| 131 | output->append(newline); |
| 132 | |
| 133 | // Prepare to normalize graphical rendering of bucket contents. |
| 134 | double max_size = 0; |
| 135 | if (graph_it) |
| 136 | max_size = GetPeakBucketSize(snapshot); |
| 137 | |
| 138 | // Calculate space needed to print bucket range numbers. Leave room to print |
| 139 | // nearly the largest bucket range without sliding over the histogram. |
[email protected] | e2951cf | 2008-09-24 23:51:25 | [diff] [blame] | 140 | size_t largest_non_empty_bucket = bucket_count() - 1; |
| 141 | while (0 == snapshot.counts(largest_non_empty_bucket)) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 142 | if (0 == largest_non_empty_bucket) |
| 143 | break; // All buckets are empty. |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 144 | --largest_non_empty_bucket; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | // Calculate largest print width needed for any of our bucket range displays. |
| 148 | size_t print_width = 1; |
[email protected] | e2951cf | 2008-09-24 23:51:25 | [diff] [blame] | 149 | for (size_t i = 0; i < bucket_count(); ++i) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 150 | if (snapshot.counts(i)) { |
| 151 | size_t width = GetAsciiBucketRange(i).size() + 1; |
| 152 | if (width > print_width) |
| 153 | print_width = width; |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | int64 remaining = sample_count; |
| 158 | int64 past = 0; |
| 159 | // Output the actual histogram graph. |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 160 | for (size_t i = 0; i < bucket_count(); ++i) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 161 | Count current = snapshot.counts(i); |
| 162 | if (!current && !PrintEmptyBucket(i)) |
| 163 | continue; |
| 164 | remaining -= current; |
[email protected] | 34b2b00 | 2009-11-20 06:53:28 | [diff] [blame] | 165 | std::string range = GetAsciiBucketRange(i); |
| 166 | output->append(range); |
| 167 | for (size_t j = 0; range.size() + j < print_width + 1; ++j) |
| 168 | output->push_back(' '); |
[email protected] | e2951cf | 2008-09-24 23:51:25 | [diff] [blame] | 169 | if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) { |
| 170 | while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 171 | ++i; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 172 | output->append("... "); |
| 173 | output->append(newline); |
| 174 | continue; // No reason to plot emptiness. |
| 175 | } |
| 176 | double current_size = GetBucketSize(current, i); |
| 177 | if (graph_it) |
| 178 | WriteAsciiBucketGraph(current_size, max_size, output); |
| 179 | WriteAsciiBucketContext(past, current, remaining, i, output); |
| 180 | output->append(newline); |
| 181 | past += current; |
| 182 | } |
| 183 | DCHECK(past == sample_count); |
| 184 | } |
| 185 | |
| 186 | bool Histogram::ValidateBucketRanges() const { |
| 187 | // Standard assertions that all bucket ranges should satisfy. |
| 188 | DCHECK(ranges_.size() == bucket_count_ + 1); |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 189 | DCHECK_EQ(ranges_[0], 0); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 190 | DCHECK(declared_min() == ranges_[1]); |
| 191 | DCHECK(declared_max() == ranges_[bucket_count_ - 1]); |
| 192 | DCHECK(kSampleType_MAX == ranges_[bucket_count_]); |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | void Histogram::Initialize() { |
| 197 | sample_.Resize(*this); |
| 198 | if (declared_min_ <= 0) |
| 199 | declared_min_ = 1; |
| 200 | if (declared_max_ >= kSampleType_MAX) |
| 201 | declared_max_ = kSampleType_MAX - 1; |
[email protected] | e2951cf | 2008-09-24 23:51:25 | [diff] [blame] | 202 | DCHECK(declared_min_ <= declared_max_); |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 203 | DCHECK_GT(bucket_count_, 1u); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 204 | size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; |
| 205 | DCHECK(bucket_count_ <= maximal_bucket_count); |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 206 | DCHECK_EQ(ranges_[0], 0); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 207 | ranges_[bucket_count_] = kSampleType_MAX; |
| 208 | InitializeBucketRange(); |
| 209 | DCHECK(ValidateBucketRanges()); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 210 | StatisticsRecorder::Register(this); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 211 | } |
| 212 | |
| 213 | // Calculate what range of values are held in each bucket. |
| 214 | // We have to be careful that we don't pick a ratio between starting points in |
| 215 | // consecutive buckets that is sooo small, that the integer bounds are the same |
| 216 | // (effectively making one bucket get no values). We need to avoid: |
| 217 | // (ranges_[i] == ranges_[i + 1] |
| 218 | // To avoid that, we just do a fine-grained bucket width as far as we need to |
| 219 | // until we get a ratio that moves us along at least 2 units at a time. From |
| 220 | // that bucket onward we do use the exponential growth of buckets. |
| 221 | void Histogram::InitializeBucketRange() { |
| 222 | double log_max = log(static_cast<double>(declared_max())); |
| 223 | double log_ratio; |
| 224 | double log_next; |
| 225 | size_t bucket_index = 1; |
| 226 | Sample current = declared_min(); |
| 227 | SetBucketRange(bucket_index, current); |
| 228 | while (bucket_count() > ++bucket_index) { |
| 229 | double log_current; |
| 230 | log_current = log(static_cast<double>(current)); |
| 231 | // Calculate the count'th root of the range. |
| 232 | log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); |
| 233 | // See where the next bucket would start. |
| 234 | log_next = log_current + log_ratio; |
| 235 | int next; |
| 236 | next = static_cast<int>(floor(exp(log_next) + 0.5)); |
| 237 | if (next > current) |
| 238 | current = next; |
| 239 | else |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 240 | ++current; // Just do a narrow bucket, and keep trying. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 241 | SetBucketRange(bucket_index, current); |
| 242 | } |
| 243 | |
| 244 | DCHECK(bucket_count() == bucket_index); |
| 245 | } |
| 246 | |
| 247 | size_t Histogram::BucketIndex(Sample value) const { |
| 248 | // Use simple binary search. This is very general, but there are better |
| 249 | // approaches if we knew that the buckets were linearly distributed. |
| 250 | DCHECK(ranges(0) <= value); |
| 251 | DCHECK(ranges(bucket_count()) > value); |
| 252 | size_t under = 0; |
| 253 | size_t over = bucket_count(); |
| 254 | size_t mid; |
| 255 | |
| 256 | do { |
| 257 | DCHECK(over >= under); |
| 258 | mid = (over + under)/2; |
| 259 | if (mid == under) |
| 260 | break; |
| 261 | if (ranges(mid) <= value) |
| 262 | under = mid; |
| 263 | else |
| 264 | over = mid; |
| 265 | } while (true); |
| 266 | |
| 267 | DCHECK(ranges(mid) <= value && ranges(mid+1) > value); |
| 268 | return mid; |
| 269 | } |
| 270 | |
| 271 | // Use the actual bucket widths (like a linear histogram) until the widths get |
| 272 | // over some transition value, and then use that transition width. Exponentials |
| 273 | // get so big so fast (and we don't expect to see a lot of entries in the large |
| 274 | // buckets), so we need this to make it possible to see what is going on and |
| 275 | // not have 0-graphical-height buckets. |
| 276 | double Histogram::GetBucketSize(Count current, size_t i) const { |
| 277 | DCHECK(ranges(i + 1) > ranges(i)); |
| 278 | static const double kTransitionWidth = 5; |
| 279 | double denominator = ranges(i + 1) - ranges(i); |
| 280 | if (denominator > kTransitionWidth) |
| 281 | denominator = kTransitionWidth; // Stop trying to normalize. |
| 282 | return current/denominator; |
| 283 | } |
| 284 | |
| 285 | //------------------------------------------------------------------------------ |
| 286 | // The following two methods can be overridden to provide a thread safe |
| 287 | // version of this class. The cost of locking is low... but an error in each |
| 288 | // of these methods has minimal impact. For now, I'll leave this unlocked, |
| 289 | // and I don't believe I can loose more than a count or two. |
| 290 | // The vectors are NOT reallocated, so there is no risk of them moving around. |
| 291 | |
| 292 | // Update histogram data with new sample. |
| 293 | void Histogram::Accumulate(Sample value, Count count, size_t index) { |
| 294 | // Note locking not done in this version!!! |
| 295 | sample_.Accumulate(value, count, index); |
| 296 | } |
| 297 | |
| 298 | // Do a safe atomic snapshot of sample data. |
| 299 | // This implementation assumes we are on a safe single thread. |
| 300 | void Histogram::SnapshotSample(SampleSet* sample) const { |
| 301 | // Note locking not done in this version!!! |
| 302 | *sample = sample_; |
| 303 | } |
| 304 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 305 | bool Histogram::HasConstructorArguments(Sample minimum, Sample maximum, |
| 306 | size_t bucket_count) { |
| 307 | return ((minimum == declared_min_) && (maximum == declared_max_) && |
| 308 | (bucket_count == bucket_count_)); |
| 309 | } |
| 310 | |
| 311 | bool Histogram::HasConstructorTimeDeltaArguments(base::TimeDelta minimum, |
| 312 | base::TimeDelta maximum, |
| 313 | size_t bucket_count) { |
| 314 | return ((minimum.InMilliseconds() == declared_min_) && |
| 315 | (maximum.InMilliseconds() == declared_max_) && |
| 316 | (bucket_count == bucket_count_)); |
| 317 | } |
| 318 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 319 | //------------------------------------------------------------------------------ |
| 320 | // Accessor methods |
| 321 | |
| 322 | void Histogram::SetBucketRange(size_t i, Sample value) { |
| 323 | DCHECK(bucket_count_ > i); |
| 324 | ranges_[i] = value; |
| 325 | } |
| 326 | |
| 327 | //------------------------------------------------------------------------------ |
| 328 | // Private methods |
| 329 | |
| 330 | double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { |
| 331 | double max = 0; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 332 | for (size_t i = 0; i < bucket_count() ; ++i) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 333 | double current_size = GetBucketSize(snapshot.counts(i), i); |
| 334 | if (current_size > max) |
| 335 | max = current_size; |
| 336 | } |
| 337 | return max; |
| 338 | } |
| 339 | |
| 340 | void Histogram::WriteAsciiHeader(const SampleSet& snapshot, |
| 341 | Count sample_count, |
| 342 | std::string* output) const { |
| 343 | StringAppendF(output, |
[email protected] | 34b2b00 | 2009-11-20 06:53:28 | [diff] [blame] | 344 | "Histogram: %s recorded %d samples", |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 345 | histogram_name().c_str(), |
| 346 | sample_count); |
| 347 | if (0 == sample_count) { |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 348 | DCHECK_EQ(snapshot.sum(), 0); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 349 | } else { |
| 350 | double average = static_cast<float>(snapshot.sum()) / sample_count; |
| 351 | double variance = static_cast<float>(snapshot.square_sum())/sample_count |
| 352 | - average * average; |
| 353 | double standard_deviation = sqrt(variance); |
| 354 | |
| 355 | StringAppendF(output, |
| 356 | ", average = %.1f, standard deviation = %.1f", |
| 357 | average, standard_deviation); |
| 358 | } |
| 359 | if (flags_ & ~kHexRangePrintingFlag ) |
| 360 | StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag); |
| 361 | } |
| 362 | |
| 363 | void Histogram::WriteAsciiBucketContext(const int64 past, |
| 364 | const Count current, |
| 365 | const int64 remaining, |
| 366 | const size_t i, |
| 367 | std::string* output) const { |
| 368 | double scaled_sum = (past + current + remaining) / 100.0; |
| 369 | WriteAsciiBucketValue(current, scaled_sum, output); |
| 370 | if (0 < i) { |
| 371 | double percentage = past / scaled_sum; |
| 372 | StringAppendF(output, " {%3.1f%%}", percentage); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | const std::string Histogram::GetAsciiBucketRange(size_t i) const { |
| 377 | std::string result; |
| 378 | if (kHexRangePrintingFlag & flags_) |
[email protected] | e2951cf | 2008-09-24 23:51:25 | [diff] [blame] | 379 | StringAppendF(&result, "%#x", ranges(i)); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 380 | else |
[email protected] | e2951cf | 2008-09-24 23:51:25 | [diff] [blame] | 381 | StringAppendF(&result, "%d", ranges(i)); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 382 | return result; |
| 383 | } |
| 384 | |
| 385 | void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, |
| 386 | std::string* output) const { |
| 387 | StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); |
| 388 | } |
| 389 | |
| 390 | void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, |
| 391 | std::string* output) const { |
| 392 | const int k_line_length = 72; // Maximal horizontal width of graph. |
| 393 | int x_count = static_cast<int>(k_line_length * (current_size / max_size) |
| 394 | + 0.5); |
| 395 | int x_remainder = k_line_length - x_count; |
| 396 | |
| 397 | while (0 < x_count--) |
| 398 | output->append("-"); |
| 399 | output->append("O"); |
| 400 | while (0 < x_remainder--) |
| 401 | output->append(" "); |
| 402 | } |
| 403 | |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 404 | // static |
| 405 | std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, |
| 406 | const SampleSet& snapshot) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 407 | DCHECK(histogram.histogram_type() != NOT_VALID_IN_RENDERER); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 408 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 409 | Pickle pickle; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 410 | pickle.WriteString(histogram.histogram_name()); |
| 411 | pickle.WriteInt(histogram.declared_min()); |
| 412 | pickle.WriteInt(histogram.declared_max()); |
| 413 | pickle.WriteSize(histogram.bucket_count()); |
| 414 | pickle.WriteInt(histogram.histogram_type()); |
[email protected] | 1f4fc8e8c | 2010-01-02 00:46:41 | [diff] [blame] | 415 | pickle.WriteInt(histogram.flags()); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 416 | |
| 417 | snapshot.Serialize(&pickle); |
| 418 | return std::string(static_cast<const char*>(pickle.data()), pickle.size()); |
| 419 | } |
| 420 | |
| 421 | // static |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 422 | bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { |
| 423 | if (histogram_info.empty()) { |
| 424 | return false; |
| 425 | } |
| 426 | |
| 427 | Pickle pickle(histogram_info.data(), |
| 428 | static_cast<int>(histogram_info.size())); |
| 429 | void* iter = NULL; |
| 430 | size_t bucket_count; |
| 431 | int declared_min; |
| 432 | int declared_max; |
| 433 | int histogram_type; |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 434 | int pickle_flags; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 435 | std::string histogram_name; |
| 436 | SampleSet sample; |
| 437 | |
| 438 | if (!pickle.ReadString(&iter, &histogram_name) || |
| 439 | !pickle.ReadInt(&iter, &declared_min) || |
| 440 | !pickle.ReadInt(&iter, &declared_max) || |
| 441 | !pickle.ReadSize(&iter, &bucket_count) || |
| 442 | !pickle.ReadInt(&iter, &histogram_type) || |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 443 | !pickle.ReadInt(&iter, &pickle_flags) || |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 444 | !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { |
[email protected] | 86440f5 | 2009-12-31 05:17:23 | [diff] [blame] | 445 | LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 446 | return false; |
| 447 | } |
[email protected] | 1f4fc8e8c | 2010-01-02 00:46:41 | [diff] [blame] | 448 | DCHECK(pickle_flags & kIPCSerializationSourceFlag); |
[email protected] | 86440f5 | 2009-12-31 05:17:23 | [diff] [blame] | 449 | // Since these fields may have come from an untrusted renderer, do additional |
| 450 | // checks above and beyond those in Histogram::Initialize() |
| 451 | if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || |
| 452 | INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { |
| 453 | LOG(ERROR) << "Values error decoding Histogram: " << histogram_name; |
| 454 | return false; |
| 455 | } |
| 456 | |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 457 | Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 458 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 459 | DCHECK(histogram_type != NOT_VALID_IN_RENDERER); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 460 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 461 | scoped_refptr<Histogram> render_histogram(NULL); |
| 462 | |
[email protected] | a764bf5e | 2010-06-02 21:31:44 | [diff] [blame] | 463 | if (histogram_type == HISTOGRAM) { |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 464 | render_histogram = Histogram::FactoryGet( |
| 465 | histogram_name, declared_min, declared_max, bucket_count, flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 466 | } else if (histogram_type == LINEAR_HISTOGRAM) { |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 467 | render_histogram = LinearHistogram::FactoryGet( |
| 468 | histogram_name, declared_min, declared_max, bucket_count, flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 469 | } else if (histogram_type == BOOLEAN_HISTOGRAM) { |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 470 | render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 471 | } else { |
| 472 | LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " << |
| 473 | histogram_type; |
| 474 | return false; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 475 | } |
| 476 | |
| 477 | DCHECK(declared_min == render_histogram->declared_min()); |
| 478 | DCHECK(declared_max == render_histogram->declared_max()); |
| 479 | DCHECK(bucket_count == render_histogram->bucket_count()); |
| 480 | DCHECK(histogram_type == render_histogram->histogram_type()); |
| 481 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 482 | if (render_histogram->flags() & kIPCSerializationSourceFlag) { |
| 483 | DLOG(INFO) << "Single process mode, histogram observed and not copied: " << |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 484 | histogram_name; |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 485 | } else { |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 486 | DCHECK(flags == (flags & render_histogram->flags())); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 487 | render_histogram->AddSampleSet(sample); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 488 | } |
| 489 | |
| 490 | return true; |
| 491 | } |
| 492 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 493 | //------------------------------------------------------------------------------ |
| 494 | // Methods for the Histogram::SampleSet class |
| 495 | //------------------------------------------------------------------------------ |
| 496 | |
| 497 | Histogram::SampleSet::SampleSet() |
| 498 | : counts_(), |
| 499 | sum_(0), |
| 500 | square_sum_(0) { |
| 501 | } |
| 502 | |
| 503 | void Histogram::SampleSet::Resize(const Histogram& histogram) { |
| 504 | counts_.resize(histogram.bucket_count(), 0); |
| 505 | } |
| 506 | |
| 507 | void Histogram::SampleSet::CheckSize(const Histogram& histogram) const { |
| 508 | DCHECK(counts_.size() == histogram.bucket_count()); |
| 509 | } |
| 510 | |
| 511 | |
| 512 | void Histogram::SampleSet::Accumulate(Sample value, Count count, |
| 513 | size_t index) { |
| 514 | DCHECK(count == 1 || count == -1); |
| 515 | counts_[index] += count; |
| 516 | sum_ += count * value; |
| 517 | square_sum_ += (count * value) * static_cast<int64>(value); |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 518 | DCHECK_GE(counts_[index], 0); |
| 519 | DCHECK_GE(sum_, 0); |
| 520 | DCHECK_GE(square_sum_, 0); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 521 | } |
| 522 | |
| 523 | Count Histogram::SampleSet::TotalCount() const { |
| 524 | Count total = 0; |
| 525 | for (Counts::const_iterator it = counts_.begin(); |
| 526 | it != counts_.end(); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 527 | ++it) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 528 | total += *it; |
| 529 | } |
| 530 | return total; |
| 531 | } |
| 532 | |
| 533 | void Histogram::SampleSet::Add(const SampleSet& other) { |
| 534 | DCHECK(counts_.size() == other.counts_.size()); |
| 535 | sum_ += other.sum_; |
| 536 | square_sum_ += other.square_sum_; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 537 | for (size_t index = 0; index < counts_.size(); ++index) |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 538 | counts_[index] += other.counts_[index]; |
| 539 | } |
| 540 | |
| 541 | void Histogram::SampleSet::Subtract(const SampleSet& other) { |
| 542 | DCHECK(counts_.size() == other.counts_.size()); |
| 543 | // Note: Race conditions in snapshotting a sum or square_sum may lead to |
| 544 | // (temporary) negative values when snapshots are later combined (and deltas |
| 545 | // calculated). As a result, we don't currently CHCEK() for positive values. |
| 546 | sum_ -= other.sum_; |
| 547 | square_sum_ -= other.square_sum_; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 548 | for (size_t index = 0; index < counts_.size(); ++index) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 549 | counts_[index] -= other.counts_[index]; |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 550 | DCHECK_GE(counts_[index], 0); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 551 | } |
| 552 | } |
| 553 | |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 554 | bool Histogram::SampleSet::Serialize(Pickle* pickle) const { |
| 555 | pickle->WriteInt64(sum_); |
| 556 | pickle->WriteInt64(square_sum_); |
| 557 | pickle->WriteSize(counts_.size()); |
| 558 | |
| 559 | for (size_t index = 0; index < counts_.size(); ++index) { |
| 560 | pickle->WriteInt(counts_[index]); |
| 561 | } |
| 562 | |
| 563 | return true; |
| 564 | } |
| 565 | |
| 566 | bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 567 | DCHECK_EQ(counts_.size(), 0u); |
| 568 | DCHECK_EQ(sum_, 0); |
| 569 | DCHECK_EQ(square_sum_, 0); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 570 | |
| 571 | size_t counts_size; |
| 572 | |
| 573 | if (!pickle.ReadInt64(iter, &sum_) || |
| 574 | !pickle.ReadInt64(iter, &square_sum_) || |
| 575 | !pickle.ReadSize(iter, &counts_size)) { |
| 576 | return false; |
| 577 | } |
| 578 | |
[email protected] | 86440f5 | 2009-12-31 05:17:23 | [diff] [blame] | 579 | if (counts_size == 0) |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 580 | return false; |
| 581 | |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 582 | for (size_t index = 0; index < counts_size; ++index) { |
[email protected] | 86440f5 | 2009-12-31 05:17:23 | [diff] [blame] | 583 | int i; |
| 584 | if (!pickle.ReadInt(iter, &i)) |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 585 | return false; |
[email protected] | 86440f5 | 2009-12-31 05:17:23 | [diff] [blame] | 586 | counts_.push_back(i); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 587 | } |
| 588 | |
| 589 | return true; |
| 590 | } |
| 591 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 592 | //------------------------------------------------------------------------------ |
| 593 | // LinearHistogram: This histogram uses a traditional set of evenly spaced |
| 594 | // buckets. |
| 595 | //------------------------------------------------------------------------------ |
| 596 | |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 597 | scoped_refptr<Histogram> LinearHistogram::FactoryGet( |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 598 | const std::string& name, Sample minimum, Sample maximum, |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 599 | size_t bucket_count, Flags flags) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 600 | scoped_refptr<Histogram> histogram(NULL); |
| 601 | |
| 602 | if (minimum <= 0) |
| 603 | minimum = 1; |
| 604 | if (maximum >= kSampleType_MAX) |
| 605 | maximum = kSampleType_MAX - 1; |
| 606 | |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 607 | if (!StatisticsRecorder::FindHistogram(name, &histogram)) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 608 | histogram = new LinearHistogram(name, minimum, maximum, bucket_count); |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 609 | StatisticsRecorder::FindHistogram(name, &histogram); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 610 | } |
| 611 | |
| 612 | DCHECK(LINEAR_HISTOGRAM == histogram->histogram_type()); |
| 613 | DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 614 | histogram->SetFlags(flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 615 | return histogram; |
| 616 | } |
| 617 | |
[email protected] | 46f89e14 | 2010-07-19 08:00:42 | [diff] [blame] | 618 | scoped_refptr<Histogram> LinearHistogram::FactoryTimeGet( |
| 619 | const std::string& name, base::TimeDelta minimum, base::TimeDelta maximum, |
| 620 | size_t bucket_count, Flags flags) { |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 621 | return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), |
| 622 | bucket_count, flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 623 | } |
| 624 | |
| 625 | LinearHistogram::LinearHistogram(const std::string& name, Sample minimum, |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 626 | Sample maximum, size_t bucket_count) |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 627 | : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { |
| 628 | InitializeBucketRange(); |
| 629 | DCHECK(ValidateBucketRanges()); |
| 630 | } |
| 631 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 632 | LinearHistogram::LinearHistogram(const std::string& name, |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 633 | TimeDelta minimum, TimeDelta maximum, size_t bucket_count) |
| 634 | : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? |
| 635 | minimum : TimeDelta::FromMilliseconds(1), |
| 636 | maximum, bucket_count) { |
| 637 | // Do a "better" (different) job at init than a base classes did... |
| 638 | InitializeBucketRange(); |
| 639 | DCHECK(ValidateBucketRanges()); |
| 640 | } |
| 641 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 642 | Histogram::ClassType LinearHistogram::histogram_type() const { |
| 643 | return LINEAR_HISTOGRAM; |
| 644 | } |
| 645 | |
[email protected] | 5059e3a | 2009-01-14 16:01:03 | [diff] [blame] | 646 | void LinearHistogram::SetRangeDescriptions( |
| 647 | const DescriptionPair descriptions[]) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 648 | for (int i =0; descriptions[i].description; ++i) { |
| 649 | bucket_description_[descriptions[i].sample] = descriptions[i].description; |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { |
| 654 | int range = ranges(i); |
| 655 | BucketDescriptionMap::const_iterator it = bucket_description_.find(range); |
| 656 | if (it == bucket_description_.end()) |
| 657 | return Histogram::GetAsciiBucketRange(i); |
| 658 | return it->second; |
| 659 | } |
| 660 | |
| 661 | bool LinearHistogram::PrintEmptyBucket(size_t index) const { |
| 662 | return bucket_description_.find(ranges(index)) == bucket_description_.end(); |
| 663 | } |
| 664 | |
| 665 | |
| 666 | void LinearHistogram::InitializeBucketRange() { |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 667 | DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 668 | double min = declared_min(); |
| 669 | double max = declared_max(); |
| 670 | size_t i; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 671 | for (i = 1; i < bucket_count(); ++i) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 672 | double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / |
| 673 | (bucket_count() - 2); |
| 674 | SetBucketRange(i, static_cast<int> (linear_range + 0.5)); |
| 675 | } |
| 676 | } |
| 677 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 678 | double LinearHistogram::GetBucketSize(Count current, size_t i) const { |
| 679 | DCHECK(ranges(i + 1) > ranges(i)); |
| 680 | // Adjacent buckets with different widths would have "surprisingly" many (few) |
| 681 | // samples in a histogram if we didn't normalize this way. |
| 682 | double denominator = ranges(i + 1) - ranges(i); |
| 683 | return current/denominator; |
| 684 | } |
| 685 | |
| 686 | //------------------------------------------------------------------------------ |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 687 | // This section provides implementation for BooleanHistogram. |
| 688 | //------------------------------------------------------------------------------ |
| 689 | |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 690 | scoped_refptr<Histogram> BooleanHistogram::FactoryGet(const std::string& name, |
| 691 | Flags flags) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 692 | scoped_refptr<Histogram> histogram(NULL); |
| 693 | |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 694 | if (!StatisticsRecorder::FindHistogram(name, &histogram)) { |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 695 | histogram = new BooleanHistogram(name); |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 696 | StatisticsRecorder::FindHistogram(name, &histogram); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 697 | } |
| 698 | |
| 699 | DCHECK(BOOLEAN_HISTOGRAM == histogram->histogram_type()); |
[email protected] | 2753b39 | 2009-12-28 06:59:52 | [diff] [blame] | 700 | histogram->SetFlags(flags); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 701 | return histogram; |
| 702 | } |
| 703 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 704 | Histogram::ClassType BooleanHistogram::histogram_type() const { |
| 705 | return BOOLEAN_HISTOGRAM; |
| 706 | } |
| 707 | |
| 708 | void BooleanHistogram::AddBoolean(bool value) { |
| 709 | Add(value ? 1 : 0); |
| 710 | } |
| 711 | |
| 712 | BooleanHistogram::BooleanHistogram(const std::string& name) |
| 713 | : LinearHistogram(name, 1, 2, 3) { |
| 714 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 715 | |
| 716 | //------------------------------------------------------------------------------ |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 717 | // CustomHistogram: |
| 718 | //------------------------------------------------------------------------------ |
| 719 | |
| 720 | scoped_refptr<Histogram> CustomHistogram::FactoryGet( |
| 721 | const std::string& name, const std::vector<int>& custom_ranges, |
| 722 | Flags flags) { |
| 723 | scoped_refptr<Histogram> histogram(NULL); |
| 724 | |
| 725 | // Remove the duplicates in the custom ranges array. |
| 726 | std::vector<int> ranges = custom_ranges; |
| 727 | ranges.push_back(0); // Ensure we have a zero value. |
| 728 | std::sort(ranges.begin(), ranges.end()); |
| 729 | ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); |
| 730 | if (ranges.size() <= 1) { |
| 731 | DCHECK(false); |
| 732 | // Note that we pushed a 0 in above, so for defensive code.... |
| 733 | ranges.push_back(1); // Put in some data so we can index to [1]. |
| 734 | } |
| 735 | |
| 736 | DCHECK_LT(ranges.back(), kSampleType_MAX); |
| 737 | |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 738 | if (!StatisticsRecorder::FindHistogram(name, &histogram)) { |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 739 | histogram = new CustomHistogram(name, ranges); |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 740 | StatisticsRecorder::FindHistogram(name, &histogram); |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 741 | } |
| 742 | |
| 743 | DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); |
| 744 | DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(), |
| 745 | ranges.size())); |
| 746 | histogram->SetFlags(flags); |
| 747 | return histogram; |
| 748 | } |
| 749 | |
[email protected] | 5d91c9e | 2010-07-28 17:25:28 | [diff] [blame^] | 750 | Histogram::ClassType CustomHistogram::histogram_type() const { |
| 751 | return CUSTOM_HISTOGRAM; |
| 752 | } |
| 753 | |
[email protected] | 70cc56e4 | 2010-04-29 22:39:55 | [diff] [blame] | 754 | CustomHistogram::CustomHistogram(const std::string& name, |
| 755 | const std::vector<int>& custom_ranges) |
| 756 | : Histogram(name, custom_ranges[1], custom_ranges.back(), |
| 757 | custom_ranges.size()) { |
| 758 | DCHECK_GT(custom_ranges.size(), 1u); |
| 759 | DCHECK_EQ(custom_ranges[0], 0); |
| 760 | ranges_vector_ = &custom_ranges; |
| 761 | InitializeBucketRange(); |
| 762 | ranges_vector_ = NULL; |
| 763 | DCHECK(ValidateBucketRanges()); |
| 764 | } |
| 765 | |
| 766 | void CustomHistogram::InitializeBucketRange() { |
| 767 | DCHECK(ranges_vector_->size() <= bucket_count()); |
| 768 | for (size_t index = 0; index < ranges_vector_->size(); ++index) { |
| 769 | SetBucketRange(index, (*ranges_vector_)[index]); |
| 770 | } |
| 771 | } |
| 772 | |
| 773 | double CustomHistogram::GetBucketSize(Count current, size_t i) const { |
| 774 | return 1; |
| 775 | } |
| 776 | |
| 777 | //------------------------------------------------------------------------------ |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 778 | // The next section handles global (central) support for all histograms, as well |
| 779 | // as startup/teardown of this service. |
| 780 | //------------------------------------------------------------------------------ |
| 781 | |
| 782 | // This singleton instance should be started during the single threaded portion |
| 783 | // of main(), and hence it is not thread safe. It initializes globals to |
| 784 | // provide support for all future calls. |
| 785 | StatisticsRecorder::StatisticsRecorder() { |
| 786 | DCHECK(!histograms_); |
| 787 | lock_ = new Lock; |
| 788 | histograms_ = new HistogramMap; |
| 789 | } |
| 790 | |
| 791 | StatisticsRecorder::~StatisticsRecorder() { |
| 792 | DCHECK(histograms_); |
| 793 | |
| 794 | if (dump_on_exit_) { |
| 795 | std::string output; |
| 796 | WriteGraph("", &output); |
| 797 | LOG(INFO) << output; |
| 798 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 799 | // Clean up. |
| 800 | delete histograms_; |
| 801 | histograms_ = NULL; |
| 802 | delete lock_; |
| 803 | lock_ = NULL; |
| 804 | } |
| 805 | |
| 806 | // static |
| 807 | bool StatisticsRecorder::WasStarted() { |
| 808 | return NULL != histograms_; |
| 809 | } |
| 810 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 811 | // Note: We can't accept a ref_ptr to |histogram| because we *might* not keep a |
| 812 | // reference, and we are called while in the Histogram constructor. In that |
| 813 | // scenario, a ref_ptr would have incremented the ref count when the histogram |
| 814 | // was passed to us, decremented it when we returned, and the instance would be |
| 815 | // destroyed before assignment (when value was returned by new). |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 816 | // static |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 817 | void StatisticsRecorder::Register(Histogram* histogram) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 818 | if (!histograms_) |
| 819 | return; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 820 | const std::string name = histogram->histogram_name(); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 821 | AutoLock auto_lock(*lock_); |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 822 | DCHECK(histograms_->end() == histograms_->find(name)); |
| 823 | |
| 824 | (*histograms_)[name] = histogram; |
| 825 | return; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 826 | } |
| 827 | |
| 828 | // static |
| 829 | void StatisticsRecorder::WriteHTMLGraph(const std::string& query, |
| 830 | std::string* output) { |
| 831 | if (!histograms_) |
| 832 | return; |
| 833 | output->append("<html><head><title>About Histograms"); |
| 834 | if (!query.empty()) |
| 835 | output->append(" - " + query); |
| 836 | output->append("</title>" |
| 837 | // We'd like the following no-cache... but it doesn't work. |
| 838 | // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">" |
| 839 | "</head><body>"); |
| 840 | |
| 841 | Histograms snapshot; |
| 842 | GetSnapshot(query, &snapshot); |
| 843 | for (Histograms::iterator it = snapshot.begin(); |
| 844 | it != snapshot.end(); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 845 | ++it) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 846 | (*it)->WriteHTMLGraph(output); |
| 847 | output->append("<br><hr><br>"); |
| 848 | } |
| 849 | output->append("</body></html>"); |
| 850 | } |
| 851 | |
| 852 | // static |
| 853 | void StatisticsRecorder::WriteGraph(const std::string& query, |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 854 | std::string* output) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 855 | if (!histograms_) |
| 856 | return; |
| 857 | if (query.length()) |
| 858 | StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); |
| 859 | else |
| 860 | output->append("Collections of all histograms\n"); |
| 861 | |
| 862 | Histograms snapshot; |
| 863 | GetSnapshot(query, &snapshot); |
| 864 | for (Histograms::iterator it = snapshot.begin(); |
| 865 | it != snapshot.end(); |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 866 | ++it) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 867 | (*it)->WriteAscii(true, "\n", output); |
| 868 | output->append("\n"); |
| 869 | } |
| 870 | } |
| 871 | |
| 872 | // static |
| 873 | void StatisticsRecorder::GetHistograms(Histograms* output) { |
| 874 | if (!histograms_) |
| 875 | return; |
| 876 | AutoLock auto_lock(*lock_); |
| 877 | for (HistogramMap::iterator it = histograms_->begin(); |
| 878 | histograms_->end() != it; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 879 | ++it) { |
[email protected] | f2bc0cb5 | 2010-06-25 15:55:15 | [diff] [blame] | 880 | DCHECK(it->second->histogram_name() == it->first); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 881 | output->push_back(it->second); |
| 882 | } |
| 883 | } |
| 884 | |
[email protected] | e8829a19 | 2009-12-06 00:09:37 | [diff] [blame] | 885 | bool StatisticsRecorder::FindHistogram(const std::string& name, |
| 886 | scoped_refptr<Histogram>* histogram) { |
| 887 | if (!histograms_) |
| 888 | return false; |
| 889 | AutoLock auto_lock(*lock_); |
| 890 | HistogramMap::iterator it = histograms_->find(name); |
| 891 | if (histograms_->end() == it) |
| 892 | return false; |
| 893 | *histogram = it->second; |
| 894 | return true; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 895 | } |
| 896 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 897 | // private static |
| 898 | void StatisticsRecorder::GetSnapshot(const std::string& query, |
| 899 | Histograms* snapshot) { |
| 900 | AutoLock auto_lock(*lock_); |
| 901 | for (HistogramMap::iterator it = histograms_->begin(); |
| 902 | histograms_->end() != it; |
[email protected] | 55e57d4 | 2009-02-25 06:10:17 | [diff] [blame] | 903 | ++it) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 904 | if (it->first.find(query) != std::string::npos) |
| 905 | snapshot->push_back(it->second); |
| 906 | } |
| 907 | } |
| 908 | |
| 909 | // static |
| 910 | StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; |
| 911 | // static |
| 912 | Lock* StatisticsRecorder::lock_ = NULL; |
| 913 | // static |
| 914 | bool StatisticsRecorder::dump_on_exit_ = false; |