1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/histogram.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1286 @@ 1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// Histogram is an object that aggregates statistics, and can summarize them in 1.9 +// various forms, including ASCII graphical, HTML, and numerically (as a 1.10 +// vector of numbers corresponding to each of the aggregating buckets). 1.11 +// See header file for details and examples. 1.12 + 1.13 +#include "base/histogram.h" 1.14 + 1.15 +#include <math.h> 1.16 + 1.17 +#include <algorithm> 1.18 +#include <string> 1.19 + 1.20 +#include "base/logging.h" 1.21 +#include "base/pickle.h" 1.22 +#include "base/string_util.h" 1.23 +#include "base/logging.h" 1.24 + 1.25 +namespace base { 1.26 + 1.27 +#define DVLOG(x) CHROMIUM_LOG(ERROR) 1.28 +#define CHECK_GT DCHECK_GT 1.29 +#define CHECK_LT DCHECK_LT 1.30 +typedef ::Lock Lock; 1.31 +typedef ::AutoLock AutoLock; 1.32 + 1.33 +// Static table of checksums for all possible 8 bit bytes. 1.34 +const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, 1.35 +0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, 1.36 +0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 1.37 +0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 1.38 +0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 1.39 +0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 1.40 +0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 1.41 +0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 1.42 +0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 1.43 +0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 1.44 +0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 1.45 +0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 1.46 +0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL, 1.47 +0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 1.48 +0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 1.49 +0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 1.50 +0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 1.51 +0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 1.52 +0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 1.53 +0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 1.54 +0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 1.55 +0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 1.56 +0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L, 1.57 +0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 1.58 +0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 1.59 +0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 1.60 +0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 1.61 +0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 1.62 +0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 1.63 +0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 1.64 +0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 1.65 +0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 1.66 +0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 1.67 +0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L, 1.68 +0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 1.69 +0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 1.70 +0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 1.71 +0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 1.72 +0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 1.73 +0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 1.74 +0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 1.75 +0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 1.76 +0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 1.77 +0x2d02ef8dL, 1.78 +}; 1.79 + 1.80 +typedef Histogram::Count Count; 1.81 + 1.82 +// static 1.83 +const size_t Histogram::kBucketCount_MAX = 16384u; 1.84 + 1.85 +Histogram* Histogram::FactoryGet(const std::string& name, 1.86 + Sample minimum, 1.87 + Sample maximum, 1.88 + size_t bucket_count, 1.89 + Flags flags) { 1.90 + Histogram* histogram(NULL); 1.91 + 1.92 + // Defensive code. 1.93 + if (minimum < 1) 1.94 + minimum = 1; 1.95 + if (maximum > kSampleType_MAX - 1) 1.96 + maximum = kSampleType_MAX - 1; 1.97 + 1.98 + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { 1.99 + // Extra variable is not needed... but this keeps this section basically 1.100 + // identical to other derived classes in this file (and compiler will 1.101 + // optimize away the extra variable. 1.102 + Histogram* tentative_histogram = 1.103 + new Histogram(name, minimum, maximum, bucket_count); 1.104 + tentative_histogram->InitializeBucketRange(); 1.105 + tentative_histogram->SetFlags(flags); 1.106 + histogram = 1.107 + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 1.108 + } 1.109 + 1.110 + DCHECK_EQ(HISTOGRAM, histogram->histogram_type()); 1.111 + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); 1.112 + return histogram; 1.113 +} 1.114 + 1.115 +Histogram* Histogram::FactoryTimeGet(const std::string& name, 1.116 + TimeDelta minimum, 1.117 + TimeDelta maximum, 1.118 + size_t bucket_count, 1.119 + Flags flags) { 1.120 + return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), 1.121 + bucket_count, flags); 1.122 +} 1.123 + 1.124 +void Histogram::Add(int value) { 1.125 + if (value > kSampleType_MAX - 1) 1.126 + value = kSampleType_MAX - 1; 1.127 + if (value < 0) 1.128 + value = 0; 1.129 + size_t index = BucketIndex(value); 1.130 + DCHECK_GE(value, ranges(index)); 1.131 + DCHECK_LT(value, ranges(index + 1)); 1.132 + Accumulate(value, 1, index); 1.133 +} 1.134 + 1.135 +void Histogram::Subtract(int value) { 1.136 + if (value > kSampleType_MAX - 1) 1.137 + value = kSampleType_MAX - 1; 1.138 + if (value < 0) 1.139 + value = 0; 1.140 + size_t index = BucketIndex(value); 1.141 + DCHECK_GE(value, ranges(index)); 1.142 + DCHECK_LT(value, ranges(index + 1)); 1.143 + Accumulate(value, -1, index); 1.144 +} 1.145 + 1.146 +void Histogram::AddBoolean(bool value) { 1.147 + DCHECK(false); 1.148 +} 1.149 + 1.150 +void Histogram::AddSampleSet(const SampleSet& sample) { 1.151 + sample_.Add(sample); 1.152 +} 1.153 + 1.154 +void Histogram::Clear() { 1.155 + SampleSet ss; 1.156 + ss.Resize(*this); 1.157 + sample_ = ss; 1.158 +} 1.159 + 1.160 +void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { 1.161 + DCHECK(false); 1.162 +} 1.163 + 1.164 +// The following methods provide a graphical histogram display. 1.165 +void Histogram::WriteHTMLGraph(std::string* output) const { 1.166 + // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. 1.167 + output->append("<PRE>"); 1.168 + WriteAscii(true, "<br>", output); 1.169 + output->append("</PRE>"); 1.170 +} 1.171 + 1.172 +void Histogram::WriteAscii(bool graph_it, const std::string& newline, 1.173 + std::string* output) const { 1.174 + // Get local (stack) copies of all effectively volatile class data so that we 1.175 + // are consistent across our output activities. 1.176 + SampleSet snapshot; 1.177 + SnapshotSample(&snapshot); 1.178 + Count sample_count = snapshot.TotalCount(); 1.179 + 1.180 + WriteAsciiHeader(snapshot, sample_count, output); 1.181 + output->append(newline); 1.182 + 1.183 + // Prepare to normalize graphical rendering of bucket contents. 1.184 + double max_size = 0; 1.185 + if (graph_it) 1.186 + max_size = GetPeakBucketSize(snapshot); 1.187 + 1.188 + // Calculate space needed to print bucket range numbers. Leave room to print 1.189 + // nearly the largest bucket range without sliding over the histogram. 1.190 + size_t largest_non_empty_bucket = bucket_count() - 1; 1.191 + while (0 == snapshot.counts(largest_non_empty_bucket)) { 1.192 + if (0 == largest_non_empty_bucket) 1.193 + break; // All buckets are empty. 1.194 + --largest_non_empty_bucket; 1.195 + } 1.196 + 1.197 + // Calculate largest print width needed for any of our bucket range displays. 1.198 + size_t print_width = 1; 1.199 + for (size_t i = 0; i < bucket_count(); ++i) { 1.200 + if (snapshot.counts(i)) { 1.201 + size_t width = GetAsciiBucketRange(i).size() + 1; 1.202 + if (width > print_width) 1.203 + print_width = width; 1.204 + } 1.205 + } 1.206 + 1.207 + int64_t remaining = sample_count; 1.208 + int64_t past = 0; 1.209 + // Output the actual histogram graph. 1.210 + for (size_t i = 0; i < bucket_count(); ++i) { 1.211 + Count current = snapshot.counts(i); 1.212 + if (!current && !PrintEmptyBucket(i)) 1.213 + continue; 1.214 + remaining -= current; 1.215 + std::string range = GetAsciiBucketRange(i); 1.216 + output->append(range); 1.217 + for (size_t j = 0; range.size() + j < print_width + 1; ++j) 1.218 + output->push_back(' '); 1.219 + if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) { 1.220 + while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) 1.221 + ++i; 1.222 + output->append("... "); 1.223 + output->append(newline); 1.224 + continue; // No reason to plot emptiness. 1.225 + } 1.226 + double current_size = GetBucketSize(current, i); 1.227 + if (graph_it) 1.228 + WriteAsciiBucketGraph(current_size, max_size, output); 1.229 + WriteAsciiBucketContext(past, current, remaining, i, output); 1.230 + output->append(newline); 1.231 + past += current; 1.232 + } 1.233 + DCHECK_EQ(sample_count, past); 1.234 +} 1.235 + 1.236 +// static 1.237 +std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, 1.238 + const SampleSet& snapshot) { 1.239 + DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); 1.240 + 1.241 + Pickle pickle; 1.242 + pickle.WriteString(histogram.histogram_name()); 1.243 + pickle.WriteInt(histogram.declared_min()); 1.244 + pickle.WriteInt(histogram.declared_max()); 1.245 + pickle.WriteSize(histogram.bucket_count()); 1.246 + pickle.WriteUInt32(histogram.range_checksum()); 1.247 + pickle.WriteInt(histogram.histogram_type()); 1.248 + pickle.WriteInt(histogram.flags()); 1.249 + 1.250 + snapshot.Serialize(&pickle); 1.251 + return std::string(static_cast<const char*>(pickle.data()), pickle.size()); 1.252 +} 1.253 + 1.254 +// static 1.255 +bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { 1.256 + if (histogram_info.empty()) { 1.257 + return false; 1.258 + } 1.259 + 1.260 + Pickle pickle(histogram_info.data(), 1.261 + static_cast<int>(histogram_info.size())); 1.262 + std::string histogram_name; 1.263 + int declared_min; 1.264 + int declared_max; 1.265 + size_t bucket_count; 1.266 + uint32_t range_checksum; 1.267 + int histogram_type; 1.268 + int pickle_flags; 1.269 + SampleSet sample; 1.270 + 1.271 + void* iter = NULL; 1.272 + if (!pickle.ReadString(&iter, &histogram_name) || 1.273 + !pickle.ReadInt(&iter, &declared_min) || 1.274 + !pickle.ReadInt(&iter, &declared_max) || 1.275 + !pickle.ReadSize(&iter, &bucket_count) || 1.276 + !pickle.ReadUInt32(&iter, &range_checksum) || 1.277 + !pickle.ReadInt(&iter, &histogram_type) || 1.278 + !pickle.ReadInt(&iter, &pickle_flags) || 1.279 + !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { 1.280 + CHROMIUM_LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; 1.281 + return false; 1.282 + } 1.283 + DCHECK(pickle_flags & kIPCSerializationSourceFlag); 1.284 + // Since these fields may have come from an untrusted renderer, do additional 1.285 + // checks above and beyond those in Histogram::Initialize() 1.286 + if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || 1.287 + INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { 1.288 + CHROMIUM_LOG(ERROR) << "Values error decoding Histogram: " << histogram_name; 1.289 + return false; 1.290 + } 1.291 + 1.292 + Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); 1.293 + 1.294 + DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type); 1.295 + 1.296 + Histogram* render_histogram(NULL); 1.297 + 1.298 + if (histogram_type == HISTOGRAM) { 1.299 + render_histogram = Histogram::FactoryGet( 1.300 + histogram_name, declared_min, declared_max, bucket_count, flags); 1.301 + } else if (histogram_type == LINEAR_HISTOGRAM) { 1.302 + render_histogram = LinearHistogram::FactoryGet( 1.303 + histogram_name, declared_min, declared_max, bucket_count, flags); 1.304 + } else if (histogram_type == BOOLEAN_HISTOGRAM) { 1.305 + render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); 1.306 + } else { 1.307 + CHROMIUM_LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " 1.308 + << histogram_type; 1.309 + return false; 1.310 + } 1.311 + 1.312 + DCHECK_EQ(render_histogram->declared_min(), declared_min); 1.313 + DCHECK_EQ(render_histogram->declared_max(), declared_max); 1.314 + DCHECK_EQ(render_histogram->bucket_count(), bucket_count); 1.315 + DCHECK_EQ(render_histogram->range_checksum(), range_checksum); 1.316 + DCHECK_EQ(render_histogram->histogram_type(), histogram_type); 1.317 + 1.318 + if (render_histogram->flags() & kIPCSerializationSourceFlag) { 1.319 + DVLOG(1) << "Single process mode, histogram observed and not copied: " 1.320 + << histogram_name; 1.321 + } else { 1.322 + DCHECK_EQ(flags & render_histogram->flags(), flags); 1.323 + render_histogram->AddSampleSet(sample); 1.324 + } 1.325 + 1.326 + return true; 1.327 +} 1.328 + 1.329 +//------------------------------------------------------------------------------ 1.330 +// Methods for the validating a sample and a related histogram. 1.331 +//------------------------------------------------------------------------------ 1.332 + 1.333 +Histogram::Inconsistencies Histogram::FindCorruption( 1.334 + const SampleSet& snapshot) const { 1.335 + int inconsistencies = NO_INCONSISTENCIES; 1.336 + Sample previous_range = -1; // Bottom range is always 0. 1.337 + int64_t count = 0; 1.338 + for (size_t index = 0; index < bucket_count(); ++index) { 1.339 + count += snapshot.counts(index); 1.340 + int new_range = ranges(index); 1.341 + if (previous_range >= new_range) 1.342 + inconsistencies |= BUCKET_ORDER_ERROR; 1.343 + previous_range = new_range; 1.344 + } 1.345 + 1.346 + if (!HasValidRangeChecksum()) 1.347 + inconsistencies |= RANGE_CHECKSUM_ERROR; 1.348 + 1.349 + int64_t delta64 = snapshot.redundant_count() - count; 1.350 + if (delta64 != 0) { 1.351 + int delta = static_cast<int>(delta64); 1.352 + if (delta != delta64) 1.353 + delta = INT_MAX; // Flag all giant errors as INT_MAX. 1.354 + // Since snapshots of histograms are taken asynchronously relative to 1.355 + // sampling (and snapped from different threads), it is pretty likely that 1.356 + // we'll catch a redundant count that doesn't match the sample count. We 1.357 + // allow for a certain amount of slop before flagging this as an 1.358 + // inconsistency. Even with an inconsistency, we'll snapshot it again (for 1.359 + // UMA in about a half hour, so we'll eventually get the data, if it was 1.360 + // not the result of a corruption. If histograms show that 1 is "too tight" 1.361 + // then we may try to use 2 or 3 for this slop value. 1.362 + const int kCommonRaceBasedCountMismatch = 1; 1.363 + if (delta > 0) { 1.364 + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); 1.365 + if (delta > kCommonRaceBasedCountMismatch) 1.366 + inconsistencies |= COUNT_HIGH_ERROR; 1.367 + } else { 1.368 + DCHECK_GT(0, delta); 1.369 + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); 1.370 + if (-delta > kCommonRaceBasedCountMismatch) 1.371 + inconsistencies |= COUNT_LOW_ERROR; 1.372 + } 1.373 + } 1.374 + return static_cast<Inconsistencies>(inconsistencies); 1.375 +} 1.376 + 1.377 +Histogram::ClassType Histogram::histogram_type() const { 1.378 + return HISTOGRAM; 1.379 +} 1.380 + 1.381 +Histogram::Sample Histogram::ranges(size_t i) const { 1.382 + return ranges_[i]; 1.383 +} 1.384 + 1.385 +size_t Histogram::bucket_count() const { 1.386 + return bucket_count_; 1.387 +} 1.388 + 1.389 +// Do a safe atomic snapshot of sample data. 1.390 +// This implementation assumes we are on a safe single thread. 1.391 +void Histogram::SnapshotSample(SampleSet* sample) const { 1.392 + // Note locking not done in this version!!! 1.393 + *sample = sample_; 1.394 +} 1.395 + 1.396 +bool Histogram::HasConstructorArguments(Sample minimum, 1.397 + Sample maximum, 1.398 + size_t bucket_count) { 1.399 + return ((minimum == declared_min_) && (maximum == declared_max_) && 1.400 + (bucket_count == bucket_count_)); 1.401 +} 1.402 + 1.403 +bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, 1.404 + TimeDelta maximum, 1.405 + size_t bucket_count) { 1.406 + return ((minimum.InMilliseconds() == declared_min_) && 1.407 + (maximum.InMilliseconds() == declared_max_) && 1.408 + (bucket_count == bucket_count_)); 1.409 +} 1.410 + 1.411 +bool Histogram::HasValidRangeChecksum() const { 1.412 + return CalculateRangeChecksum() == range_checksum_; 1.413 +} 1.414 + 1.415 +size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) 1.416 +{ 1.417 + size_t n = 0; 1.418 + n += aMallocSizeOf(this); 1.419 + // We're not allowed to do deep dives into STL data structures. This 1.420 + // is as close as we can get to measuring this array. 1.421 + n += aMallocSizeOf(&ranges_[0]); 1.422 + n += sample_.SizeOfExcludingThis(aMallocSizeOf); 1.423 + return n; 1.424 +} 1.425 + 1.426 +size_t Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) 1.427 +{ 1.428 + // We're not allowed to do deep dives into STL data structures. This 1.429 + // is as close as we can get to measuring this array. 1.430 + return aMallocSizeOf(&counts_[0]); 1.431 +} 1.432 + 1.433 +Histogram::Histogram(const std::string& name, Sample minimum, 1.434 + Sample maximum, size_t bucket_count) 1.435 + : sample_(), 1.436 + histogram_name_(name), 1.437 + declared_min_(minimum), 1.438 + declared_max_(maximum), 1.439 + bucket_count_(bucket_count), 1.440 + flags_(kNoFlags), 1.441 + ranges_(bucket_count + 1, 0), 1.442 + range_checksum_(0) { 1.443 + Initialize(); 1.444 +} 1.445 + 1.446 +Histogram::Histogram(const std::string& name, TimeDelta minimum, 1.447 + TimeDelta maximum, size_t bucket_count) 1.448 + : sample_(), 1.449 + histogram_name_(name), 1.450 + declared_min_(static_cast<int> (minimum.InMilliseconds())), 1.451 + declared_max_(static_cast<int> (maximum.InMilliseconds())), 1.452 + bucket_count_(bucket_count), 1.453 + flags_(kNoFlags), 1.454 + ranges_(bucket_count + 1, 0), 1.455 + range_checksum_(0) { 1.456 + Initialize(); 1.457 +} 1.458 + 1.459 +Histogram::~Histogram() { 1.460 + if (StatisticsRecorder::dump_on_exit()) { 1.461 + std::string output; 1.462 + WriteAscii(true, "\n", &output); 1.463 + CHROMIUM_LOG(INFO) << output; 1.464 + } 1.465 + 1.466 + // Just to make sure most derived class did this properly... 1.467 + DCHECK(ValidateBucketRanges()); 1.468 +} 1.469 + 1.470 +// Calculate what range of values are held in each bucket. 1.471 +// We have to be careful that we don't pick a ratio between starting points in 1.472 +// consecutive buckets that is sooo small, that the integer bounds are the same 1.473 +// (effectively making one bucket get no values). We need to avoid: 1.474 +// ranges_[i] == ranges_[i + 1] 1.475 +// To avoid that, we just do a fine-grained bucket width as far as we need to 1.476 +// until we get a ratio that moves us along at least 2 units at a time. From 1.477 +// that bucket onward we do use the exponential growth of buckets. 1.478 +void Histogram::InitializeBucketRange() { 1.479 + double log_max = log(static_cast<double>(declared_max())); 1.480 + double log_ratio; 1.481 + double log_next; 1.482 + size_t bucket_index = 1; 1.483 + Sample current = declared_min(); 1.484 + SetBucketRange(bucket_index, current); 1.485 + while (bucket_count() > ++bucket_index) { 1.486 + double log_current; 1.487 + log_current = log(static_cast<double>(current)); 1.488 + // Calculate the count'th root of the range. 1.489 + log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); 1.490 + // See where the next bucket would start. 1.491 + log_next = log_current + log_ratio; 1.492 + int next; 1.493 + next = static_cast<int>(floor(exp(log_next) + 0.5)); 1.494 + if (next > current) 1.495 + current = next; 1.496 + else 1.497 + ++current; // Just do a narrow bucket, and keep trying. 1.498 + SetBucketRange(bucket_index, current); 1.499 + } 1.500 + ResetRangeChecksum(); 1.501 + 1.502 + DCHECK_EQ(bucket_count(), bucket_index); 1.503 +} 1.504 + 1.505 +bool Histogram::PrintEmptyBucket(size_t index) const { 1.506 + return true; 1.507 +} 1.508 + 1.509 +size_t Histogram::BucketIndex(Sample value) const { 1.510 + // Use simple binary search. This is very general, but there are better 1.511 + // approaches if we knew that the buckets were linearly distributed. 1.512 + DCHECK_LE(ranges(0), value); 1.513 + DCHECK_GT(ranges(bucket_count()), value); 1.514 + size_t under = 0; 1.515 + size_t over = bucket_count(); 1.516 + size_t mid; 1.517 + 1.518 + do { 1.519 + DCHECK_GE(over, under); 1.520 + mid = under + (over - under)/2; 1.521 + if (mid == under) 1.522 + break; 1.523 + if (ranges(mid) <= value) 1.524 + under = mid; 1.525 + else 1.526 + over = mid; 1.527 + } while (true); 1.528 + 1.529 + DCHECK_LE(ranges(mid), value); 1.530 + CHECK_GT(ranges(mid+1), value); 1.531 + return mid; 1.532 +} 1.533 + 1.534 +// Use the actual bucket widths (like a linear histogram) until the widths get 1.535 +// over some transition value, and then use that transition width. Exponentials 1.536 +// get so big so fast (and we don't expect to see a lot of entries in the large 1.537 +// buckets), so we need this to make it possible to see what is going on and 1.538 +// not have 0-graphical-height buckets. 1.539 +double Histogram::GetBucketSize(Count current, size_t i) const { 1.540 + DCHECK_GT(ranges(i + 1), ranges(i)); 1.541 + static const double kTransitionWidth = 5; 1.542 + double denominator = ranges(i + 1) - ranges(i); 1.543 + if (denominator > kTransitionWidth) 1.544 + denominator = kTransitionWidth; // Stop trying to normalize. 1.545 + return current/denominator; 1.546 +} 1.547 + 1.548 +void Histogram::ResetRangeChecksum() { 1.549 + range_checksum_ = CalculateRangeChecksum(); 1.550 +} 1.551 + 1.552 +const std::string Histogram::GetAsciiBucketRange(size_t i) const { 1.553 + std::string result; 1.554 + if (kHexRangePrintingFlag & flags_) 1.555 + StringAppendF(&result, "%#x", ranges(i)); 1.556 + else 1.557 + StringAppendF(&result, "%d", ranges(i)); 1.558 + return result; 1.559 +} 1.560 + 1.561 +// Update histogram data with new sample. 1.562 +void Histogram::Accumulate(Sample value, Count count, size_t index) { 1.563 + // Note locking not done in this version!!! 1.564 + sample_.AccumulateWithExponentialStats(value, count, index, 1.565 + flags_ & kExtendedStatisticsFlag); 1.566 +} 1.567 + 1.568 +void Histogram::SetBucketRange(size_t i, Sample value) { 1.569 + DCHECK_GT(bucket_count_, i); 1.570 + ranges_[i] = value; 1.571 +} 1.572 + 1.573 +bool Histogram::ValidateBucketRanges() const { 1.574 + // Standard assertions that all bucket ranges should satisfy. 1.575 + DCHECK_EQ(bucket_count_ + 1, ranges_.size()); 1.576 + DCHECK_EQ(0, ranges_[0]); 1.577 + DCHECK_EQ(declared_min(), ranges_[1]); 1.578 + DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); 1.579 + DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); 1.580 + return true; 1.581 +} 1.582 + 1.583 +uint32_t Histogram::CalculateRangeChecksum() const { 1.584 + DCHECK_EQ(ranges_.size(), bucket_count() + 1); 1.585 + uint32_t checksum = static_cast<uint32_t>(ranges_.size()); // Seed checksum. 1.586 + for (size_t index = 0; index < bucket_count(); ++index) 1.587 + checksum = Crc32(checksum, ranges(index)); 1.588 + return checksum; 1.589 +} 1.590 + 1.591 +void Histogram::Initialize() { 1.592 + sample_.Resize(*this); 1.593 + if (declared_min_ < 1) 1.594 + declared_min_ = 1; 1.595 + if (declared_max_ > kSampleType_MAX - 1) 1.596 + declared_max_ = kSampleType_MAX - 1; 1.597 + DCHECK_LE(declared_min_, declared_max_); 1.598 + DCHECK_GT(bucket_count_, 1u); 1.599 + CHECK_LT(bucket_count_, kBucketCount_MAX); 1.600 + size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; 1.601 + DCHECK_LE(bucket_count_, maximal_bucket_count); 1.602 + DCHECK_EQ(0, ranges_[0]); 1.603 + ranges_[bucket_count_] = kSampleType_MAX; 1.604 +} 1.605 + 1.606 +// We generate the CRC-32 using the low order bits to select whether to XOR in 1.607 +// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us 1.608 +// to keep the quotient in a uint32_t. Since we're not concerned about the nature 1.609 +// of corruptions (i.e., we don't care about bit sequencing, since we are 1.610 +// handling memory changes, which are more grotesque) so we don't bother to 1.611 +// get the CRC correct for big-endian vs little-ending calculations. All we 1.612 +// need is a nice hash, that tends to depend on all the bits of the sample, with 1.613 +// very little chance of changes in one place impacting changes in another 1.614 +// place. 1.615 +uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) { 1.616 + const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats. 1.617 + if (kUseRealCrc) { 1.618 + union { 1.619 + Histogram::Sample range; 1.620 + unsigned char bytes[sizeof(Histogram::Sample)]; 1.621 + } converter; 1.622 + converter.range = range; 1.623 + for (size_t i = 0; i < sizeof(converter); ++i) 1.624 + sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8); 1.625 + } else { 1.626 + // Use hash techniques provided in ReallyFastHash, except we don't care 1.627 + // about "avalanching" (which would worsten the hash, and add collisions), 1.628 + // and we don't care about edge cases since we have an even number of bytes. 1.629 + union { 1.630 + Histogram::Sample range; 1.631 + uint16_t ints[sizeof(Histogram::Sample) / 2]; 1.632 + } converter; 1.633 + DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter)); 1.634 + converter.range = range; 1.635 + sum += converter.ints[0]; 1.636 + sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11); 1.637 + sum += sum >> 11; 1.638 + } 1.639 + return sum; 1.640 +} 1.641 + 1.642 +//------------------------------------------------------------------------------ 1.643 +// Private methods 1.644 + 1.645 +double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { 1.646 + double max = 0; 1.647 + for (size_t i = 0; i < bucket_count() ; ++i) { 1.648 + double current_size = GetBucketSize(snapshot.counts(i), i); 1.649 + if (current_size > max) 1.650 + max = current_size; 1.651 + } 1.652 + return max; 1.653 +} 1.654 + 1.655 +void Histogram::WriteAsciiHeader(const SampleSet& snapshot, 1.656 + Count sample_count, 1.657 + std::string* output) const { 1.658 + StringAppendF(output, 1.659 + "Histogram: %s recorded %d samples", 1.660 + histogram_name().c_str(), 1.661 + sample_count); 1.662 + if (0 == sample_count) { 1.663 + DCHECK_EQ(snapshot.sum(), 0); 1.664 + } else { 1.665 + double average = static_cast<float>(snapshot.sum()) / sample_count; 1.666 + 1.667 + StringAppendF(output, ", average = %.1f", average); 1.668 + } 1.669 + if (flags_ & ~kHexRangePrintingFlag) 1.670 + StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag); 1.671 +} 1.672 + 1.673 +void Histogram::WriteAsciiBucketContext(const int64_t past, 1.674 + const Count current, 1.675 + const int64_t remaining, 1.676 + const size_t i, 1.677 + std::string* output) const { 1.678 + double scaled_sum = (past + current + remaining) / 100.0; 1.679 + WriteAsciiBucketValue(current, scaled_sum, output); 1.680 + if (0 < i) { 1.681 + double percentage = past / scaled_sum; 1.682 + StringAppendF(output, " {%3.1f%%}", percentage); 1.683 + } 1.684 +} 1.685 + 1.686 +void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, 1.687 + std::string* output) const { 1.688 + StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); 1.689 +} 1.690 + 1.691 +void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, 1.692 + std::string* output) const { 1.693 + const int k_line_length = 72; // Maximal horizontal width of graph. 1.694 + int x_count = static_cast<int>(k_line_length * (current_size / max_size) 1.695 + + 0.5); 1.696 + int x_remainder = k_line_length - x_count; 1.697 + 1.698 + while (0 < x_count--) 1.699 + output->append("-"); 1.700 + output->append("O"); 1.701 + while (0 < x_remainder--) 1.702 + output->append(" "); 1.703 +} 1.704 + 1.705 +//------------------------------------------------------------------------------ 1.706 +// Methods for the Histogram::SampleSet class 1.707 +//------------------------------------------------------------------------------ 1.708 + 1.709 +Histogram::SampleSet::SampleSet() 1.710 + : counts_(), 1.711 + sum_(0), 1.712 + sum_squares_(0), 1.713 + log_sum_(0), 1.714 + log_sum_squares_(0), 1.715 + redundant_count_(0) { 1.716 +} 1.717 + 1.718 +Histogram::SampleSet::~SampleSet() { 1.719 +} 1.720 + 1.721 +void Histogram::SampleSet::Resize(const Histogram& histogram) { 1.722 + counts_.resize(histogram.bucket_count(), 0); 1.723 +} 1.724 + 1.725 +void Histogram::SampleSet::CheckSize(const Histogram& histogram) const { 1.726 + DCHECK_EQ(histogram.bucket_count(), counts_.size()); 1.727 +} 1.728 + 1.729 +void Histogram::SampleSet::Accumulate(Sample value, Count count, 1.730 + size_t index) { 1.731 + DCHECK(count == 1 || count == -1); 1.732 + counts_[index] += count; 1.733 + redundant_count_ += count; 1.734 + sum_ += static_cast<int64_t>(count) * value; 1.735 + DCHECK_GE(counts_[index], 0); 1.736 + DCHECK_GE(sum_, 0); 1.737 + DCHECK_GE(redundant_count_, 0); 1.738 +} 1.739 + 1.740 +void Histogram::SampleSet::AccumulateWithLinearStats(Sample value, 1.741 + Count count, 1.742 + size_t index) { 1.743 + Accumulate(value, count, index); 1.744 + sum_squares_ += static_cast<int64_t>(count) * value * value; 1.745 +} 1.746 + 1.747 +void Histogram::SampleSet::AccumulateWithExponentialStats(Sample value, 1.748 + Count count, 1.749 + size_t index, 1.750 + bool computeExtendedStatistics) { 1.751 + Accumulate(value, count, index); 1.752 + if (computeExtendedStatistics) { 1.753 + DCHECK_GE(value, 0); 1.754 + float value_log = logf(static_cast<float>(value) + 1.0f); 1.755 + log_sum_ += count * value_log; 1.756 + log_sum_squares_ += count * value_log * value_log; 1.757 + } 1.758 +} 1.759 + 1.760 +Count Histogram::SampleSet::TotalCount() const { 1.761 + Count total = 0; 1.762 + for (Counts::const_iterator it = counts_.begin(); 1.763 + it != counts_.end(); 1.764 + ++it) { 1.765 + total += *it; 1.766 + } 1.767 + return total; 1.768 +} 1.769 + 1.770 +void Histogram::SampleSet::Add(const SampleSet& other) { 1.771 + DCHECK_EQ(counts_.size(), other.counts_.size()); 1.772 + sum_ += other.sum_; 1.773 + sum_squares_ += other.sum_squares_; 1.774 + log_sum_ += other.log_sum_; 1.775 + log_sum_squares_ += other.log_sum_squares_; 1.776 + redundant_count_ += other.redundant_count_; 1.777 + for (size_t index = 0; index < counts_.size(); ++index) 1.778 + counts_[index] += other.counts_[index]; 1.779 +} 1.780 + 1.781 +void Histogram::SampleSet::Subtract(const SampleSet& other) { 1.782 + DCHECK_EQ(counts_.size(), other.counts_.size()); 1.783 + // Note: Race conditions in snapshotting a sum may lead to (temporary) 1.784 + // negative values when snapshots are later combined (and deltas calculated). 1.785 + // As a result, we don't currently CHCEK() for positive values. 1.786 + sum_ -= other.sum_; 1.787 + sum_squares_ -= other.sum_squares_; 1.788 + log_sum_ -= other.log_sum_; 1.789 + log_sum_squares_ -= other.log_sum_squares_; 1.790 + redundant_count_ -= other.redundant_count_; 1.791 + for (size_t index = 0; index < counts_.size(); ++index) { 1.792 + counts_[index] -= other.counts_[index]; 1.793 + DCHECK_GE(counts_[index], 0); 1.794 + } 1.795 +} 1.796 + 1.797 +bool Histogram::SampleSet::Serialize(Pickle* pickle) const { 1.798 + pickle->WriteInt64(sum_); 1.799 + pickle->WriteInt64(redundant_count_); 1.800 + pickle->WriteSize(counts_.size()); 1.801 + 1.802 + for (size_t index = 0; index < counts_.size(); ++index) { 1.803 + pickle->WriteInt(counts_[index]); 1.804 + } 1.805 + 1.806 + return true; 1.807 +} 1.808 + 1.809 +bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { 1.810 + DCHECK_EQ(counts_.size(), 0u); 1.811 + DCHECK_EQ(sum_, 0); 1.812 + DCHECK_EQ(redundant_count_, 0); 1.813 + 1.814 + size_t counts_size; 1.815 + 1.816 + if (!pickle.ReadInt64(iter, &sum_) || 1.817 + !pickle.ReadInt64(iter, &redundant_count_) || 1.818 + !pickle.ReadSize(iter, &counts_size)) { 1.819 + return false; 1.820 + } 1.821 + 1.822 + if (counts_size == 0) 1.823 + return false; 1.824 + 1.825 + int count = 0; 1.826 + for (size_t index = 0; index < counts_size; ++index) { 1.827 + int i; 1.828 + if (!pickle.ReadInt(iter, &i)) 1.829 + return false; 1.830 + counts_.push_back(i); 1.831 + count += i; 1.832 + } 1.833 + 1.834 + return true; 1.835 +} 1.836 + 1.837 +//------------------------------------------------------------------------------ 1.838 +// LinearHistogram: This histogram uses a traditional set of evenly spaced 1.839 +// buckets. 1.840 +//------------------------------------------------------------------------------ 1.841 + 1.842 +LinearHistogram::~LinearHistogram() { 1.843 +} 1.844 + 1.845 +Histogram* LinearHistogram::FactoryGet(const std::string& name, 1.846 + Sample minimum, 1.847 + Sample maximum, 1.848 + size_t bucket_count, 1.849 + Flags flags) { 1.850 + Histogram* histogram(NULL); 1.851 + 1.852 + if (minimum < 1) 1.853 + minimum = 1; 1.854 + if (maximum > kSampleType_MAX - 1) 1.855 + maximum = kSampleType_MAX - 1; 1.856 + 1.857 + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { 1.858 + LinearHistogram* tentative_histogram = 1.859 + new LinearHistogram(name, minimum, maximum, bucket_count); 1.860 + tentative_histogram->InitializeBucketRange(); 1.861 + tentative_histogram->SetFlags(flags); 1.862 + histogram = 1.863 + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 1.864 + } 1.865 + 1.866 + DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); 1.867 + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); 1.868 + return histogram; 1.869 +} 1.870 + 1.871 +Histogram* LinearHistogram::FactoryTimeGet(const std::string& name, 1.872 + TimeDelta minimum, 1.873 + TimeDelta maximum, 1.874 + size_t bucket_count, 1.875 + Flags flags) { 1.876 + return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), 1.877 + bucket_count, flags); 1.878 +} 1.879 + 1.880 +Histogram::ClassType LinearHistogram::histogram_type() const { 1.881 + return LINEAR_HISTOGRAM; 1.882 +} 1.883 + 1.884 +void LinearHistogram::Accumulate(Sample value, Count count, size_t index) { 1.885 + sample_.AccumulateWithLinearStats(value, count, index); 1.886 +} 1.887 + 1.888 +void LinearHistogram::SetRangeDescriptions( 1.889 + const DescriptionPair descriptions[]) { 1.890 + for (int i =0; descriptions[i].description; ++i) { 1.891 + bucket_description_[descriptions[i].sample] = descriptions[i].description; 1.892 + } 1.893 +} 1.894 + 1.895 +LinearHistogram::LinearHistogram(const std::string& name, 1.896 + Sample minimum, 1.897 + Sample maximum, 1.898 + size_t bucket_count) 1.899 + : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { 1.900 +} 1.901 + 1.902 +LinearHistogram::LinearHistogram(const std::string& name, 1.903 + TimeDelta minimum, 1.904 + TimeDelta maximum, 1.905 + size_t bucket_count) 1.906 + : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? 1.907 + minimum : TimeDelta::FromMilliseconds(1), 1.908 + maximum, bucket_count) { 1.909 +} 1.910 + 1.911 +void LinearHistogram::InitializeBucketRange() { 1.912 + DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. 1.913 + double min = declared_min(); 1.914 + double max = declared_max(); 1.915 + size_t i; 1.916 + for (i = 1; i < bucket_count(); ++i) { 1.917 + double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / 1.918 + (bucket_count() - 2); 1.919 + SetBucketRange(i, static_cast<int> (linear_range + 0.5)); 1.920 + } 1.921 + ResetRangeChecksum(); 1.922 +} 1.923 + 1.924 +double LinearHistogram::GetBucketSize(Count current, size_t i) const { 1.925 + DCHECK_GT(ranges(i + 1), ranges(i)); 1.926 + // Adjacent buckets with different widths would have "surprisingly" many (few) 1.927 + // samples in a histogram if we didn't normalize this way. 1.928 + double denominator = ranges(i + 1) - ranges(i); 1.929 + return current/denominator; 1.930 +} 1.931 + 1.932 +const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { 1.933 + int range = ranges(i); 1.934 + BucketDescriptionMap::const_iterator it = bucket_description_.find(range); 1.935 + if (it == bucket_description_.end()) 1.936 + return Histogram::GetAsciiBucketRange(i); 1.937 + return it->second; 1.938 +} 1.939 + 1.940 +bool LinearHistogram::PrintEmptyBucket(size_t index) const { 1.941 + return bucket_description_.find(ranges(index)) == bucket_description_.end(); 1.942 +} 1.943 + 1.944 + 1.945 +//------------------------------------------------------------------------------ 1.946 +// This section provides implementation for BooleanHistogram. 1.947 +//------------------------------------------------------------------------------ 1.948 + 1.949 +Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) { 1.950 + Histogram* histogram(NULL); 1.951 + 1.952 + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { 1.953 + BooleanHistogram* tentative_histogram = new BooleanHistogram(name); 1.954 + tentative_histogram->InitializeBucketRange(); 1.955 + tentative_histogram->SetFlags(flags); 1.956 + histogram = 1.957 + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 1.958 + } 1.959 + 1.960 + DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); 1.961 + return histogram; 1.962 +} 1.963 + 1.964 +Histogram::ClassType BooleanHistogram::histogram_type() const { 1.965 + return BOOLEAN_HISTOGRAM; 1.966 +} 1.967 + 1.968 +void BooleanHistogram::AddBoolean(bool value) { 1.969 + Add(value ? 1 : 0); 1.970 +} 1.971 + 1.972 +BooleanHistogram::BooleanHistogram(const std::string& name) 1.973 + : LinearHistogram(name, 1, 2, 3) { 1.974 +} 1.975 + 1.976 +void 1.977 +BooleanHistogram::Accumulate(Sample value, Count count, size_t index) 1.978 +{ 1.979 + // Callers will have computed index based on the non-booleanified value. 1.980 + // So we need to adjust the index manually. 1.981 + LinearHistogram::Accumulate(!!value, count, value ? 1 : 0); 1.982 +} 1.983 + 1.984 +//------------------------------------------------------------------------------ 1.985 +// FlagHistogram: 1.986 +//------------------------------------------------------------------------------ 1.987 + 1.988 +Histogram * 1.989 +FlagHistogram::FactoryGet(const std::string &name, Flags flags) 1.990 +{ 1.991 + Histogram *h(nullptr); 1.992 + 1.993 + if (!StatisticsRecorder::FindHistogram(name, &h)) { 1.994 + FlagHistogram *fh = new FlagHistogram(name); 1.995 + fh->InitializeBucketRange(); 1.996 + fh->SetFlags(flags); 1.997 + size_t zero_index = fh->BucketIndex(0); 1.998 + fh->LinearHistogram::Accumulate(0, 1, zero_index); 1.999 + h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh); 1.1000 + } 1.1001 + 1.1002 + return h; 1.1003 +} 1.1004 + 1.1005 +FlagHistogram::FlagHistogram(const std::string &name) 1.1006 + : BooleanHistogram(name), mSwitched(false) { 1.1007 +} 1.1008 + 1.1009 +Histogram::ClassType 1.1010 +FlagHistogram::histogram_type() const 1.1011 +{ 1.1012 + return FLAG_HISTOGRAM; 1.1013 +} 1.1014 + 1.1015 +void 1.1016 +FlagHistogram::Accumulate(Sample value, Count count, size_t index) 1.1017 +{ 1.1018 + if (mSwitched) { 1.1019 + return; 1.1020 + } 1.1021 + 1.1022 + mSwitched = true; 1.1023 + DCHECK_EQ(value, 1); 1.1024 + LinearHistogram::Accumulate(value, 1, index); 1.1025 + size_t zero_index = BucketIndex(0); 1.1026 + LinearHistogram::Accumulate(0, -1, zero_index); 1.1027 +} 1.1028 + 1.1029 +void 1.1030 +FlagHistogram::AddSampleSet(const SampleSet& sample) { 1.1031 + DCHECK_EQ(bucket_count(), sample.size()); 1.1032 + // We can't be sure the SampleSet provided came from another FlagHistogram, 1.1033 + // so we take the following steps: 1.1034 + // - If our flag has already been set do nothing. 1.1035 + // - Set our flag if the following hold: 1.1036 + // - The sum of the counts in the provided SampleSet is 1. 1.1037 + // - The bucket index for that single value is the same as the index where we 1.1038 + // would place our set flag. 1.1039 + // - Otherwise, take no action. 1.1040 + 1.1041 + if (mSwitched) { 1.1042 + return; 1.1043 + } 1.1044 + 1.1045 + if (sample.sum() != 1) { 1.1046 + return; 1.1047 + } 1.1048 + 1.1049 + size_t one_index = BucketIndex(1); 1.1050 + if (sample.counts(one_index) == 1) { 1.1051 + Accumulate(1, 1, one_index); 1.1052 + } 1.1053 +} 1.1054 +//------------------------------------------------------------------------------ 1.1055 +// CustomHistogram: 1.1056 +//------------------------------------------------------------------------------ 1.1057 + 1.1058 +Histogram* CustomHistogram::FactoryGet(const std::string& name, 1.1059 + const std::vector<Sample>& custom_ranges, 1.1060 + Flags flags) { 1.1061 + Histogram* histogram(NULL); 1.1062 + 1.1063 + // Remove the duplicates in the custom ranges array. 1.1064 + std::vector<int> ranges = custom_ranges; 1.1065 + ranges.push_back(0); // Ensure we have a zero value. 1.1066 + std::sort(ranges.begin(), ranges.end()); 1.1067 + ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); 1.1068 + if (ranges.size() <= 1) { 1.1069 + DCHECK(false); 1.1070 + // Note that we pushed a 0 in above, so for defensive code.... 1.1071 + ranges.push_back(1); // Put in some data so we can index to [1]. 1.1072 + } 1.1073 + 1.1074 + DCHECK_LT(ranges.back(), kSampleType_MAX); 1.1075 + 1.1076 + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { 1.1077 + CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges); 1.1078 + tentative_histogram->InitializedCustomBucketRange(ranges); 1.1079 + tentative_histogram->SetFlags(flags); 1.1080 + histogram = 1.1081 + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 1.1082 + } 1.1083 + 1.1084 + DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); 1.1085 + DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(), 1.1086 + ranges.size())); 1.1087 + return histogram; 1.1088 +} 1.1089 + 1.1090 +Histogram::ClassType CustomHistogram::histogram_type() const { 1.1091 + return CUSTOM_HISTOGRAM; 1.1092 +} 1.1093 + 1.1094 +CustomHistogram::CustomHistogram(const std::string& name, 1.1095 + const std::vector<Sample>& custom_ranges) 1.1096 + : Histogram(name, custom_ranges[1], custom_ranges.back(), 1.1097 + custom_ranges.size()) { 1.1098 + DCHECK_GT(custom_ranges.size(), 1u); 1.1099 + DCHECK_EQ(custom_ranges[0], 0); 1.1100 +} 1.1101 + 1.1102 +void CustomHistogram::InitializedCustomBucketRange( 1.1103 + const std::vector<Sample>& custom_ranges) { 1.1104 + DCHECK_GT(custom_ranges.size(), 1u); 1.1105 + DCHECK_EQ(custom_ranges[0], 0); 1.1106 + DCHECK_LE(custom_ranges.size(), bucket_count()); 1.1107 + for (size_t index = 0; index < custom_ranges.size(); ++index) 1.1108 + SetBucketRange(index, custom_ranges[index]); 1.1109 + ResetRangeChecksum(); 1.1110 +} 1.1111 + 1.1112 +double CustomHistogram::GetBucketSize(Count current, size_t i) const { 1.1113 + return 1; 1.1114 +} 1.1115 + 1.1116 +//------------------------------------------------------------------------------ 1.1117 +// The next section handles global (central) support for all histograms, as well 1.1118 +// as startup/teardown of this service. 1.1119 +//------------------------------------------------------------------------------ 1.1120 + 1.1121 +// This singleton instance should be started during the single threaded portion 1.1122 +// of main(), and hence it is not thread safe. It initializes globals to 1.1123 +// provide support for all future calls. 1.1124 +StatisticsRecorder::StatisticsRecorder() { 1.1125 + DCHECK(!histograms_); 1.1126 + if (lock_ == NULL) { 1.1127 + // This will leak on purpose. It's the only way to make sure we won't race 1.1128 + // against the static uninitialization of the module while one of our 1.1129 + // static methods relying on the lock get called at an inappropriate time 1.1130 + // during the termination phase. Since it's a static data member, we will 1.1131 + // leak one per process, which would be similar to the instance allocated 1.1132 + // during static initialization and released only on process termination. 1.1133 + lock_ = new base::Lock; 1.1134 + } 1.1135 + base::AutoLock auto_lock(*lock_); 1.1136 + histograms_ = new HistogramMap; 1.1137 +} 1.1138 + 1.1139 +StatisticsRecorder::~StatisticsRecorder() { 1.1140 + DCHECK(histograms_ && lock_); 1.1141 + 1.1142 + if (dump_on_exit_) { 1.1143 + std::string output; 1.1144 + WriteGraph("", &output); 1.1145 + CHROMIUM_LOG(INFO) << output; 1.1146 + } 1.1147 + // Clean up. 1.1148 + HistogramMap* histograms = NULL; 1.1149 + { 1.1150 + base::AutoLock auto_lock(*lock_); 1.1151 + histograms = histograms_; 1.1152 + histograms_ = NULL; 1.1153 + for (HistogramMap::iterator it = histograms->begin(); 1.1154 + histograms->end() != it; 1.1155 + ++it) { 1.1156 + // No other clients permanently hold Histogram references, so we 1.1157 + // have the only one and it is safe to delete it. 1.1158 + delete it->second; 1.1159 + } 1.1160 + } 1.1161 + delete histograms; 1.1162 + // We don't delete lock_ on purpose to avoid having to properly protect 1.1163 + // against it going away after we checked for NULL in the static methods. 1.1164 +} 1.1165 + 1.1166 +// static 1.1167 +bool StatisticsRecorder::IsActive() { 1.1168 + if (lock_ == NULL) 1.1169 + return false; 1.1170 + base::AutoLock auto_lock(*lock_); 1.1171 + return NULL != histograms_; 1.1172 +} 1.1173 + 1.1174 +Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) { 1.1175 + DCHECK(histogram->HasValidRangeChecksum()); 1.1176 + if (lock_ == NULL) 1.1177 + return histogram; 1.1178 + base::AutoLock auto_lock(*lock_); 1.1179 + if (!histograms_) 1.1180 + return histogram; 1.1181 + const std::string name = histogram->histogram_name(); 1.1182 + HistogramMap::iterator it = histograms_->find(name); 1.1183 + // Avoid overwriting a previous registration. 1.1184 + if (histograms_->end() == it) { 1.1185 + (*histograms_)[name] = histogram; 1.1186 + } else { 1.1187 + delete histogram; // We already have one by this name. 1.1188 + histogram = it->second; 1.1189 + } 1.1190 + return histogram; 1.1191 +} 1.1192 + 1.1193 +// static 1.1194 +void StatisticsRecorder::WriteHTMLGraph(const std::string& query, 1.1195 + std::string* output) { 1.1196 + if (!IsActive()) 1.1197 + return; 1.1198 + output->append("<html><head><title>About Histograms"); 1.1199 + if (!query.empty()) 1.1200 + output->append(" - " + query); 1.1201 + output->append("</title>" 1.1202 + // We'd like the following no-cache... but it doesn't work. 1.1203 + // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">" 1.1204 + "</head><body>"); 1.1205 + 1.1206 + Histograms snapshot; 1.1207 + GetSnapshot(query, &snapshot); 1.1208 + for (Histograms::iterator it = snapshot.begin(); 1.1209 + it != snapshot.end(); 1.1210 + ++it) { 1.1211 + (*it)->WriteHTMLGraph(output); 1.1212 + output->append("<br><hr><br>"); 1.1213 + } 1.1214 + output->append("</body></html>"); 1.1215 +} 1.1216 + 1.1217 +// static 1.1218 +void StatisticsRecorder::WriteGraph(const std::string& query, 1.1219 + std::string* output) { 1.1220 + if (!IsActive()) 1.1221 + return; 1.1222 + if (query.length()) 1.1223 + StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); 1.1224 + else 1.1225 + output->append("Collections of all histograms\n"); 1.1226 + 1.1227 + Histograms snapshot; 1.1228 + GetSnapshot(query, &snapshot); 1.1229 + for (Histograms::iterator it = snapshot.begin(); 1.1230 + it != snapshot.end(); 1.1231 + ++it) { 1.1232 + (*it)->WriteAscii(true, "\n", output); 1.1233 + output->append("\n"); 1.1234 + } 1.1235 +} 1.1236 + 1.1237 +// static 1.1238 +void StatisticsRecorder::GetHistograms(Histograms* output) { 1.1239 + if (lock_ == NULL) 1.1240 + return; 1.1241 + base::AutoLock auto_lock(*lock_); 1.1242 + if (!histograms_) 1.1243 + return; 1.1244 + for (HistogramMap::iterator it = histograms_->begin(); 1.1245 + histograms_->end() != it; 1.1246 + ++it) { 1.1247 + DCHECK_EQ(it->first, it->second->histogram_name()); 1.1248 + output->push_back(it->second); 1.1249 + } 1.1250 +} 1.1251 + 1.1252 +bool StatisticsRecorder::FindHistogram(const std::string& name, 1.1253 + Histogram** histogram) { 1.1254 + if (lock_ == NULL) 1.1255 + return false; 1.1256 + base::AutoLock auto_lock(*lock_); 1.1257 + if (!histograms_) 1.1258 + return false; 1.1259 + HistogramMap::iterator it = histograms_->find(name); 1.1260 + if (histograms_->end() == it) 1.1261 + return false; 1.1262 + *histogram = it->second; 1.1263 + return true; 1.1264 +} 1.1265 + 1.1266 +// private static 1.1267 +void StatisticsRecorder::GetSnapshot(const std::string& query, 1.1268 + Histograms* snapshot) { 1.1269 + if (lock_ == NULL) 1.1270 + return; 1.1271 + base::AutoLock auto_lock(*lock_); 1.1272 + if (!histograms_) 1.1273 + return; 1.1274 + for (HistogramMap::iterator it = histograms_->begin(); 1.1275 + histograms_->end() != it; 1.1276 + ++it) { 1.1277 + if (it->first.find(query) != std::string::npos) 1.1278 + snapshot->push_back(it->second); 1.1279 + } 1.1280 +} 1.1281 + 1.1282 +// static 1.1283 +StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; 1.1284 +// static 1.1285 +base::Lock* StatisticsRecorder::lock_ = NULL; 1.1286 +// static 1.1287 +bool StatisticsRecorder::dump_on_exit_ = false; 1.1288 + 1.1289 +} // namespace base