Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(764)

Side by Side Diff: base/metrics/histogram.cc

Issue 10834011: Refactor of Histogram related code (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/metrics/histogram.h ('k') | base/metrics/histogram_base.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Histogram is an object that aggregates statistics, and can summarize them in 5 // Histogram is an object that aggregates statistics, and can summarize them in
6 // various forms, including ASCII graphical, HTML, and numerically (as a 6 // various forms, including ASCII graphical, HTML, and numerically (as a
7 // vector of numbers corresponding to each of the aggregating buckets). 7 // vector of numbers corresponding to each of the aggregating buckets).
8 // See header file for details and examples. 8 // See header file for details and examples.
9 9
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 11
12 #include <math.h> 12 #include <math.h>
13 13
14 #include <algorithm> 14 #include <algorithm>
15 #include <string> 15 #include <string>
16 16
17 #include "base/logging.h" 17 #include "base/logging.h"
18 #include "base/metrics/statistics_recorder.h" 18 #include "base/metrics/statistics_recorder.h"
19 #include "base/pickle.h" 19 #include "base/pickle.h"
20 #include "base/stringprintf.h" 20 #include "base/stringprintf.h"
21 #include "base/synchronization/lock.h" 21 #include "base/synchronization/lock.h"
22 22
23 using std::string;
24 using std::vector;
25
23 namespace base { 26 namespace base {
24 27
25 // Static table of checksums for all possible 8 bit bytes. 28 typedef HistogramBase::Count Count;
26 const uint32 Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, 29 typedef HistogramBase::Sample Sample;
27 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
28 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
29 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL,
30 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL,
31 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L,
32 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL,
33 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
34 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L,
35 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL,
36 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL,
37 0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L,
38 0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL,
39 0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L,
40 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L,
41 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL,
42 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L,
43 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
44 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL,
45 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L,
46 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L,
47 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L,
48 0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
49 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL,
50 0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L,
51 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L,
52 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L,
53 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
54 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L,
55 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL,
56 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L,
57 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L,
58 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
59 0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L,
60 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L,
61 0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL,
62 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL,
63 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
64 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL,
65 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L,
66 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L,
67 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL,
68 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
69 0x2d02ef8dL,
70 };
71
72 typedef Histogram::Count Count;
73 30
74 // static 31 // static
75 const size_t Histogram::kBucketCount_MAX = 16384u; 32 const size_t Histogram::kBucketCount_MAX = 16384u;
76 33
77 Histogram* Histogram::FactoryGet(const std::string& name, 34 Histogram::SampleSet::SampleSet(size_t size)
35 : counts_(size, 0),
36 sum_(0),
37 redundant_count_(0) {}
38
39 Histogram::SampleSet::SampleSet()
40 : counts_(),
41 sum_(0),
42 redundant_count_(0) {}
43
44 Histogram::SampleSet::~SampleSet() {}
45
46 void Histogram::SampleSet::Resize(size_t size) {
47 counts_.resize(size, 0);
48 }
49
50 void Histogram::SampleSet::Accumulate(Sample value, Count count,
51 size_t index) {
52 DCHECK(count == 1 || count == -1);
53 counts_[index] += count;
54 sum_ += count * value;
55 redundant_count_ += count;
56 DCHECK_GE(counts_[index], 0);
57 DCHECK_GE(sum_, 0);
58 DCHECK_GE(redundant_count_, 0);
59 }
60
61 Count Histogram::SampleSet::TotalCount() const {
62 Count total = 0;
63 for (Counts::const_iterator it = counts_.begin();
64 it != counts_.end();
65 ++it) {
66 total += *it;
67 }
68 return total;
69 }
70
71 void Histogram::SampleSet::Add(const SampleSet& other) {
72 DCHECK_EQ(counts_.size(), other.counts_.size());
73 sum_ += other.sum_;
74 redundant_count_ += other.redundant_count_;
75 for (size_t index = 0; index < counts_.size(); ++index)
76 counts_[index] += other.counts_[index];
77 }
78
79 void Histogram::SampleSet::Subtract(const SampleSet& other) {
80 DCHECK_EQ(counts_.size(), other.counts_.size());
81 // Note: Race conditions in snapshotting a sum may lead to (temporary)
82 // negative values when snapshots are later combined (and deltas calculated).
83 // As a result, we don't currently CHCEK() for positive values.
84 sum_ -= other.sum_;
85 redundant_count_ -= other.redundant_count_;
86 for (size_t index = 0; index < counts_.size(); ++index) {
87 counts_[index] -= other.counts_[index];
88 DCHECK_GE(counts_[index], 0);
89 }
90 }
91
92 bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
93 pickle->WriteInt64(sum_);
94 pickle->WriteInt64(redundant_count_);
95 pickle->WriteUInt64(counts_.size());
96
97 for (size_t index = 0; index < counts_.size(); ++index) {
98 pickle->WriteInt(counts_[index]);
99 }
100
101 return true;
102 }
103
104 bool Histogram::SampleSet::Deserialize(PickleIterator* iter) {
105 DCHECK_EQ(counts_.size(), 0u);
106 DCHECK_EQ(sum_, 0);
107 DCHECK_EQ(redundant_count_, 0);
108
109 uint64 counts_size;
110
111 if (!iter->ReadInt64(&sum_) ||
112 !iter->ReadInt64(&redundant_count_) ||
113 !iter->ReadUInt64(&counts_size)) {
114 return false;
115 }
116
117 if (counts_size == 0)
118 return false;
119
120 int count = 0;
121 for (uint64 index = 0; index < counts_size; ++index) {
122 int i;
123 if (!iter->ReadInt(&i))
124 return false;
125 counts_.push_back(i);
126 count += i;
127 }
128 DCHECK_EQ(count, redundant_count_);
129 return count == redundant_count_;
130 }
131
132 Histogram* Histogram::FactoryGet(const string& name,
78 Sample minimum, 133 Sample minimum,
79 Sample maximum, 134 Sample maximum,
80 size_t bucket_count, 135 size_t bucket_count,
81 Flags flags) { 136 Flags flags) {
82 // Defensive code. 137 CHECK(InspectConstructionArguments(name, &minimum, &maximum, &bucket_count));
83 if (minimum < 1)
84 minimum = 1;
85 if (maximum > kSampleType_MAX - 1)
86 maximum = kSampleType_MAX - 1;
87
88 DCHECK_GT(maximum, minimum);
89 DCHECK_GT((Sample) bucket_count, 2);
90 DCHECK_LE((Sample) bucket_count, maximum - minimum + 2);
91 138
92 Histogram* histogram = StatisticsRecorder::FindHistogram(name); 139 Histogram* histogram = StatisticsRecorder::FindHistogram(name);
93 if (!histogram) { 140 if (!histogram) {
94 // Extra variable is not needed... but this keeps this section basically
95 // identical to other derived classes in this file (and compiler will
96 // optimize away the extra variable.
97 // To avoid racy destruction at shutdown, the following will be leaked. 141 // To avoid racy destruction at shutdown, the following will be leaked.
142 BucketRanges* ranges = new BucketRanges(bucket_count + 1);
143 InitializeBucketRanges(minimum, maximum, bucket_count, ranges);
144 const BucketRanges* registered_ranges =
145 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
146
98 Histogram* tentative_histogram = 147 Histogram* tentative_histogram =
99 new Histogram(name, minimum, maximum, bucket_count); 148 new Histogram(name, minimum, maximum, bucket_count, registered_ranges);
100 tentative_histogram->InitializeBucketRange();
101 tentative_histogram->SetFlags(flags); 149 tentative_histogram->SetFlags(flags);
102 histogram = 150 histogram =
103 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 151 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
104 } 152 }
105 153
106 DCHECK_EQ(HISTOGRAM, histogram->histogram_type()); 154 CHECK_EQ(HISTOGRAM, histogram->histogram_type());
107 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); 155 CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
108 return histogram; 156 return histogram;
109 } 157 }
110 158
111 Histogram* Histogram::FactoryTimeGet(const std::string& name, 159 Histogram* Histogram::FactoryTimeGet(const string& name,
112 TimeDelta minimum, 160 TimeDelta minimum,
113 TimeDelta maximum, 161 TimeDelta maximum,
114 size_t bucket_count, 162 size_t bucket_count,
115 Flags flags) { 163 Flags flags) {
116 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), 164 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
117 bucket_count, flags); 165 bucket_count, flags);
118 } 166 }
119 167
120 TimeTicks Histogram::DebugNow() { 168 TimeTicks Histogram::DebugNow() {
121 #ifndef NDEBUG 169 #ifndef NDEBUG
122 return TimeTicks::Now(); 170 return TimeTicks::Now();
123 #else 171 #else
124 return TimeTicks(); 172 return TimeTicks();
125 #endif 173 #endif
126 } 174 }
127 175
176 // Calculate what range of values are held in each bucket.
177 // We have to be careful that we don't pick a ratio between starting points in
178 // consecutive buckets that is sooo small, that the integer bounds are the same
179 // (effectively making one bucket get no values). We need to avoid:
180 // ranges(i) == ranges(i + 1)
181 // To avoid that, we just do a fine-grained bucket width as far as we need to
182 // until we get a ratio that moves us along at least 2 units at a time. From
183 // that bucket onward we do use the exponential growth of buckets.
184 //
185 // static
186 void Histogram::InitializeBucketRanges(Sample minimum,
187 Sample maximum,
188 size_t bucket_count,
189 BucketRanges* ranges) {
190 DCHECK_EQ(ranges->size(), bucket_count + 1);
191 double log_max = log(static_cast<double>(maximum));
192 double log_ratio;
193 double log_next;
194 size_t bucket_index = 1;
195 Sample current = minimum;
196 ranges->set_range(bucket_index, current);
197 while (bucket_count > ++bucket_index) {
198 double log_current;
199 log_current = log(static_cast<double>(current));
200 // Calculate the count'th root of the range.
201 log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
202 // See where the next bucket would start.
203 log_next = log_current + log_ratio;
204 Sample next;
205 next = static_cast<int>(floor(exp(log_next) + 0.5));
206 if (next > current)
207 current = next;
208 else
209 ++current; // Just do a narrow bucket, and keep trying.
210 ranges->set_range(bucket_index, current);
211 }
212 ranges->set_range(ranges->size() - 1, HistogramBase::kSampleType_MAX);
213 ranges->ResetChecksum();
214 }
215
216 // static
128 void Histogram::Add(int value) { 217 void Histogram::Add(int value) {
129 if (value > kSampleType_MAX - 1) 218 if (value > kSampleType_MAX - 1)
130 value = kSampleType_MAX - 1; 219 value = kSampleType_MAX - 1;
131 if (value < 0) 220 if (value < 0)
132 value = 0; 221 value = 0;
133 size_t index = BucketIndex(value); 222 size_t index = BucketIndex(value);
134 DCHECK_GE(value, ranges(index)); 223 DCHECK_GE(value, ranges(index));
135 DCHECK_LT(value, ranges(index + 1)); 224 DCHECK_LT(value, ranges(index + 1));
136 Accumulate(value, 1, index); 225 Accumulate(value, 1, index);
137 } 226 }
138 227
139 void Histogram::AddBoolean(bool value) { 228 void Histogram::AddBoolean(bool value) {
140 DCHECK(false); 229 DCHECK(false);
141 } 230 }
142 231
143 void Histogram::AddSampleSet(const SampleSet& sample) { 232 void Histogram::AddSampleSet(const SampleSet& sample) {
144 sample_.Add(sample); 233 sample_.Add(sample);
145 } 234 }
146 235
147 void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { 236 void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
148 DCHECK(false); 237 DCHECK(false);
149 } 238 }
150 239
151 // The following methods provide a graphical histogram display. 240 // The following methods provide a graphical histogram display.
152 void Histogram::WriteHTMLGraph(std::string* output) const { 241 void Histogram::WriteHTMLGraph(string* output) const {
153 // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. 242 // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
154 output->append("<PRE>"); 243 output->append("<PRE>");
155 WriteAsciiImpl(true, "<br>", output); 244 WriteAsciiImpl(true, "<br>", output);
156 output->append("</PRE>"); 245 output->append("</PRE>");
157 } 246 }
158 247
159 void Histogram::WriteAscii(std::string* output) const { 248 void Histogram::WriteAscii(string* output) const {
160 WriteAsciiImpl(true, "\n", output); 249 WriteAsciiImpl(true, "\n", output);
161 } 250 }
162 251
163 void Histogram::WriteAsciiImpl(bool graph_it,
164 const std::string& newline,
165 std::string* output) const {
166 // Get local (stack) copies of all effectively volatile class data so that we
167 // are consistent across our output activities.
168 SampleSet snapshot;
169 SnapshotSample(&snapshot);
170 Count sample_count = snapshot.TotalCount();
171
172 WriteAsciiHeader(snapshot, sample_count, output);
173 output->append(newline);
174
175 // Prepare to normalize graphical rendering of bucket contents.
176 double max_size = 0;
177 if (graph_it)
178 max_size = GetPeakBucketSize(snapshot);
179
180 // Calculate space needed to print bucket range numbers. Leave room to print
181 // nearly the largest bucket range without sliding over the histogram.
182 size_t largest_non_empty_bucket = bucket_count() - 1;
183 while (0 == snapshot.counts(largest_non_empty_bucket)) {
184 if (0 == largest_non_empty_bucket)
185 break; // All buckets are empty.
186 --largest_non_empty_bucket;
187 }
188
189 // Calculate largest print width needed for any of our bucket range displays.
190 size_t print_width = 1;
191 for (size_t i = 0; i < bucket_count(); ++i) {
192 if (snapshot.counts(i)) {
193 size_t width = GetAsciiBucketRange(i).size() + 1;
194 if (width > print_width)
195 print_width = width;
196 }
197 }
198
199 int64 remaining = sample_count;
200 int64 past = 0;
201 // Output the actual histogram graph.
202 for (size_t i = 0; i < bucket_count(); ++i) {
203 Count current = snapshot.counts(i);
204 if (!current && !PrintEmptyBucket(i))
205 continue;
206 remaining -= current;
207 std::string range = GetAsciiBucketRange(i);
208 output->append(range);
209 for (size_t j = 0; range.size() + j < print_width + 1; ++j)
210 output->push_back(' ');
211 if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
212 while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
213 ++i;
214 output->append("... ");
215 output->append(newline);
216 continue; // No reason to plot emptiness.
217 }
218 double current_size = GetBucketSize(current, i);
219 if (graph_it)
220 WriteAsciiBucketGraph(current_size, max_size, output);
221 WriteAsciiBucketContext(past, current, remaining, i, output);
222 output->append(newline);
223 past += current;
224 }
225 DCHECK_EQ(sample_count, past);
226 }
227
228 // static 252 // static
229 std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, 253 string Histogram::SerializeHistogramInfo(const Histogram& histogram,
230 const SampleSet& snapshot) { 254 const SampleSet& snapshot) {
231 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); 255 DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type());
256 DCHECK(histogram.bucket_ranges()->HasValidChecksum());
232 257
233 Pickle pickle; 258 Pickle pickle;
234 pickle.WriteString(histogram.histogram_name()); 259 pickle.WriteString(histogram.histogram_name());
235 pickle.WriteInt(histogram.declared_min()); 260 pickle.WriteInt(histogram.declared_min());
236 pickle.WriteInt(histogram.declared_max()); 261 pickle.WriteInt(histogram.declared_max());
237 pickle.WriteUInt64(histogram.bucket_count()); 262 pickle.WriteUInt64(histogram.bucket_count());
238 pickle.WriteUInt32(histogram.range_checksum()); 263 pickle.WriteUInt32(histogram.bucket_ranges()->checksum());
239 pickle.WriteInt(histogram.histogram_type()); 264 pickle.WriteInt(histogram.histogram_type());
240 pickle.WriteInt(histogram.flags()); 265 pickle.WriteInt(histogram.flags());
241 266
242 snapshot.Serialize(&pickle); 267 snapshot.Serialize(&pickle);
243 268
244 histogram.SerializeRanges(&pickle); 269 histogram.SerializeRanges(&pickle);
245 270
246 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); 271 return string(static_cast<const char*>(pickle.data()), pickle.size());
247 } 272 }
248 273
249 // static 274 // static
250 bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { 275 bool Histogram::DeserializeHistogramInfo(const string& histogram_info) {
251 if (histogram_info.empty()) { 276 if (histogram_info.empty()) {
252 return false; 277 return false;
253 } 278 }
254 279
255 Pickle pickle(histogram_info.data(), 280 Pickle pickle(histogram_info.data(),
256 static_cast<int>(histogram_info.size())); 281 static_cast<int>(histogram_info.size()));
257 std::string histogram_name; 282 string histogram_name;
258 int declared_min; 283 int declared_min;
259 int declared_max; 284 int declared_max;
260 uint64 bucket_count; 285 uint64 bucket_count;
261 uint32 range_checksum; 286 uint32 range_checksum;
262 int histogram_type; 287 int histogram_type;
263 int pickle_flags; 288 int pickle_flags;
264 SampleSet sample; 289 SampleSet sample;
265 290
266 PickleIterator iter(pickle); 291 PickleIterator iter(pickle);
267 if (!iter.ReadString(&histogram_name) || 292 if (!iter.ReadString(&histogram_name) ||
(...skipping 25 matching lines...) Expand all
293 318
294 if (histogram_type == HISTOGRAM) { 319 if (histogram_type == HISTOGRAM) {
295 render_histogram = Histogram::FactoryGet( 320 render_histogram = Histogram::FactoryGet(
296 histogram_name, declared_min, declared_max, bucket_count, flags); 321 histogram_name, declared_min, declared_max, bucket_count, flags);
297 } else if (histogram_type == LINEAR_HISTOGRAM) { 322 } else if (histogram_type == LINEAR_HISTOGRAM) {
298 render_histogram = LinearHistogram::FactoryGet( 323 render_histogram = LinearHistogram::FactoryGet(
299 histogram_name, declared_min, declared_max, bucket_count, flags); 324 histogram_name, declared_min, declared_max, bucket_count, flags);
300 } else if (histogram_type == BOOLEAN_HISTOGRAM) { 325 } else if (histogram_type == BOOLEAN_HISTOGRAM) {
301 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); 326 render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags);
302 } else if (histogram_type == CUSTOM_HISTOGRAM) { 327 } else if (histogram_type == CUSTOM_HISTOGRAM) {
303 std::vector<Histogram::Sample> sample_ranges(bucket_count); 328 vector<Sample> sample_ranges(bucket_count);
304 if (!CustomHistogram::DeserializeRanges(&iter, &sample_ranges)) { 329 if (!CustomHistogram::DeserializeRanges(&iter, &sample_ranges)) {
305 DLOG(ERROR) << "Pickle error decoding ranges: " << histogram_name; 330 DLOG(ERROR) << "Pickle error decoding ranges: " << histogram_name;
306 return false; 331 return false;
307 } 332 }
308 render_histogram = 333 render_histogram =
309 CustomHistogram::FactoryGet(histogram_name, sample_ranges, flags); 334 CustomHistogram::FactoryGet(histogram_name, sample_ranges, flags);
310 } else { 335 } else {
311 DLOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " 336 DLOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: "
312 << histogram_type; 337 << histogram_type;
313 return false; 338 return false;
314 } 339 }
315 340
316 DCHECK_EQ(render_histogram->declared_min(), declared_min); 341 DCHECK_EQ(render_histogram->declared_min(), declared_min);
317 DCHECK_EQ(render_histogram->declared_max(), declared_max); 342 DCHECK_EQ(render_histogram->declared_max(), declared_max);
318 DCHECK_EQ(render_histogram->bucket_count(), bucket_count); 343 DCHECK_EQ(render_histogram->bucket_count(), bucket_count);
319 DCHECK_EQ(render_histogram->range_checksum(), range_checksum);
320 DCHECK_EQ(render_histogram->histogram_type(), histogram_type); 344 DCHECK_EQ(render_histogram->histogram_type(), histogram_type);
321 345
346 if (render_histogram->bucket_ranges()->checksum() != range_checksum) {
347 return false;
348 }
349
322 if (render_histogram->flags() & kIPCSerializationSourceFlag) { 350 if (render_histogram->flags() & kIPCSerializationSourceFlag) {
323 DVLOG(1) << "Single process mode, histogram observed and not copied: " 351 DVLOG(1) << "Single process mode, histogram observed and not copied: "
324 << histogram_name; 352 << histogram_name;
325 } else { 353 } else {
326 DCHECK_EQ(flags & render_histogram->flags(), flags); 354 DCHECK_EQ(flags & render_histogram->flags(), flags);
327 render_histogram->AddSampleSet(sample); 355 render_histogram->AddSampleSet(sample);
328 } 356 }
329 357
330 return true; 358 return true;
331 } 359 }
332 360
333 //------------------------------------------------------------------------------
334 // Methods for the validating a sample and a related histogram.
335 //------------------------------------------------------------------------------
336 361
362 // Validate a sample and related histogram.
337 Histogram::Inconsistencies Histogram::FindCorruption( 363 Histogram::Inconsistencies Histogram::FindCorruption(
338 const SampleSet& snapshot) const { 364 const SampleSet& snapshot) const {
339 int inconsistencies = NO_INCONSISTENCIES; 365 int inconsistencies = NO_INCONSISTENCIES;
340 Sample previous_range = -1; // Bottom range is always 0. 366 Sample previous_range = -1; // Bottom range is always 0.
341 int64 count = 0; 367 int64 count = 0;
342 for (size_t index = 0; index < bucket_count(); ++index) { 368 for (size_t index = 0; index < bucket_count(); ++index) {
343 count += snapshot.counts(index); 369 count += snapshot.counts(index);
344 int new_range = ranges(index); 370 int new_range = ranges(index);
345 if (previous_range >= new_range) 371 if (previous_range >= new_range)
346 inconsistencies |= BUCKET_ORDER_ERROR; 372 inconsistencies |= BUCKET_ORDER_ERROR;
347 previous_range = new_range; 373 previous_range = new_range;
348 } 374 }
349 375
350 if (!HasValidRangeChecksum()) 376 if (!bucket_ranges()->HasValidChecksum())
351 inconsistencies |= RANGE_CHECKSUM_ERROR; 377 inconsistencies |= RANGE_CHECKSUM_ERROR;
352 378
353 int64 delta64 = snapshot.redundant_count() - count; 379 int64 delta64 = snapshot.redundant_count() - count;
354 if (delta64 != 0) { 380 if (delta64 != 0) {
355 int delta = static_cast<int>(delta64); 381 int delta = static_cast<int>(delta64);
356 if (delta != delta64) 382 if (delta != delta64)
357 delta = INT_MAX; // Flag all giant errors as INT_MAX. 383 delta = INT_MAX; // Flag all giant errors as INT_MAX.
358 // Since snapshots of histograms are taken asynchronously relative to 384 // Since snapshots of histograms are taken asynchronously relative to
359 // sampling (and snapped from different threads), it is pretty likely that 385 // sampling (and snapped from different threads), it is pretty likely that
360 // we'll catch a redundant count that doesn't match the sample count. We 386 // we'll catch a redundant count that doesn't match the sample count. We
(...skipping 14 matching lines...) Expand all
375 inconsistencies |= COUNT_LOW_ERROR; 401 inconsistencies |= COUNT_LOW_ERROR;
376 } 402 }
377 } 403 }
378 return static_cast<Inconsistencies>(inconsistencies); 404 return static_cast<Inconsistencies>(inconsistencies);
379 } 405 }
380 406
381 Histogram::ClassType Histogram::histogram_type() const { 407 Histogram::ClassType Histogram::histogram_type() const {
382 return HISTOGRAM; 408 return HISTOGRAM;
383 } 409 }
384 410
385 Histogram::Sample Histogram::ranges(size_t i) const { 411 Sample Histogram::ranges(size_t i) const {
386 return bucket_ranges_->range(i); 412 return bucket_ranges_->range(i);
387 } 413 }
388 414
389 size_t Histogram::bucket_count() const { 415 size_t Histogram::bucket_count() const {
390 return bucket_count_; 416 return bucket_count_;
391 } 417 }
392 418
393 // Do a safe atomic snapshot of sample data. 419 // Do a safe atomic snapshot of sample data.
394 // This implementation assumes we are on a safe single thread. 420 // This implementation assumes we are on a safe single thread.
395 void Histogram::SnapshotSample(SampleSet* sample) const { 421 void Histogram::SnapshotSample(SampleSet* sample) const {
396 // Note locking not done in this version!!! 422 // Note locking not done in this version!!!
397 *sample = sample_; 423 *sample = sample_;
398 } 424 }
399 425
400 bool Histogram::HasConstructorArguments(Sample minimum, 426 bool Histogram::HasConstructionArguments(Sample minimum,
401 Sample maximum, 427 Sample maximum,
402 size_t bucket_count) { 428 size_t bucket_count) {
403 return ((minimum == declared_min_) && (maximum == declared_max_) && 429 return ((minimum == declared_min_) && (maximum == declared_max_) &&
404 (bucket_count == bucket_count_)); 430 (bucket_count == bucket_count_));
405 } 431 }
406 432
407 bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, 433 Histogram::Histogram(const string& name,
408 TimeDelta maximum, 434 Sample minimum,
409 size_t bucket_count) { 435 Sample maximum,
410 return ((minimum.InMilliseconds() == declared_min_) && 436 size_t bucket_count,
411 (maximum.InMilliseconds() == declared_max_) && 437 const BucketRanges* ranges)
412 (bucket_count == bucket_count_));
413 }
414
415 bool Histogram::HasValidRangeChecksum() const {
416 return CalculateRangeChecksum() == range_checksum_;
417 }
418
419 Histogram::Histogram(const std::string& name, Sample minimum,
420 Sample maximum, size_t bucket_count)
421 : HistogramBase(name), 438 : HistogramBase(name),
439 bucket_ranges_(ranges),
422 declared_min_(minimum), 440 declared_min_(minimum),
423 declared_max_(maximum), 441 declared_max_(maximum),
424 bucket_count_(bucket_count), 442 bucket_count_(bucket_count),
425 flags_(kNoFlags), 443 flags_(kNoFlags),
426 bucket_ranges_(new BucketRanges(bucket_count + 1)), 444 sample_(bucket_count) {}
427 range_checksum_(0),
428 sample_() {
429 Initialize();
430 }
431
432 Histogram::Histogram(const std::string& name, TimeDelta minimum,
433 TimeDelta maximum, size_t bucket_count)
434 : HistogramBase(name),
435 declared_min_(static_cast<int> (minimum.InMilliseconds())),
436 declared_max_(static_cast<int> (maximum.InMilliseconds())),
437 bucket_count_(bucket_count),
438 flags_(kNoFlags),
439 bucket_ranges_(new BucketRanges(bucket_count + 1)),
440 range_checksum_(0),
441 sample_() {
442 Initialize();
443 }
444 445
445 Histogram::~Histogram() { 446 Histogram::~Histogram() {
446 if (StatisticsRecorder::dump_on_exit()) { 447 if (StatisticsRecorder::dump_on_exit()) {
447 std::string output; 448 string output;
448 WriteAsciiImpl(true, "\n", &output); 449 WriteAsciiImpl(true, "\n", &output);
449 DLOG(INFO) << output; 450 DLOG(INFO) << output;
450 } 451 }
452 }
451 453
452 // Just to make sure most derived class did this properly... 454 // static
453 DCHECK(ValidateBucketRanges()); 455 bool Histogram::InspectConstructionArguments(const string& name,
456 Sample* minimum,
457 Sample* maximum,
458 size_t* bucket_count) {
459 // Defensive code for backward compatibility.
460 if (*minimum < 1) {
461 DLOG(WARNING) << "Histogram: " << name << " Bad minimum: " << *minimum;
Lei Zhang 2012/08/02 04:20:50 Does this have to be DLOG? Can you use a DVLOG ins
Lei Zhang 2012/08/02 04:22:52 Already fixed in r149541. Yay! :)
462 *minimum = 1;
463 }
464 if (*maximum >= kSampleType_MAX) {
465 DLOG(WARNING) << "Histogram: " << name << " Bad maximum: " << *maximum;
466 *maximum = kSampleType_MAX;
467 }
468
469 if (*bucket_count < 3 || *bucket_count >= kBucketCount_MAX)
470 return false;
471 if (*bucket_count > static_cast<size_t>(*maximum - *minimum + 2))
472 return false;
473 return true;
454 } 474 }
455 475
456 bool Histogram::SerializeRanges(Pickle* pickle) const { 476 bool Histogram::SerializeRanges(Pickle* pickle) const {
457 return true; 477 return true;
458 } 478 }
459 479
460 // Calculate what range of values are held in each bucket.
461 // We have to be careful that we don't pick a ratio between starting points in
462 // consecutive buckets that is sooo small, that the integer bounds are the same
463 // (effectively making one bucket get no values). We need to avoid:
464 // ranges(i) == ranges(i + 1)
465 // To avoid that, we just do a fine-grained bucket width as far as we need to
466 // until we get a ratio that moves us along at least 2 units at a time. From
467 // that bucket onward we do use the exponential growth of buckets.
468 void Histogram::InitializeBucketRange() {
469 double log_max = log(static_cast<double>(declared_max()));
470 double log_ratio;
471 double log_next;
472 size_t bucket_index = 1;
473 Sample current = declared_min();
474 SetBucketRange(bucket_index, current);
475 while (bucket_count() > ++bucket_index) {
476 double log_current;
477 log_current = log(static_cast<double>(current));
478 // Calculate the count'th root of the range.
479 log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
480 // See where the next bucket would start.
481 log_next = log_current + log_ratio;
482 int next;
483 next = static_cast<int>(floor(exp(log_next) + 0.5));
484 if (next > current)
485 current = next;
486 else
487 ++current; // Just do a narrow bucket, and keep trying.
488 SetBucketRange(bucket_index, current);
489 }
490 ResetRangeChecksum();
491
492 DCHECK_EQ(bucket_count(), bucket_index);
493 }
494
495 bool Histogram::PrintEmptyBucket(size_t index) const { 480 bool Histogram::PrintEmptyBucket(size_t index) const {
496 return true; 481 return true;
497 } 482 }
498 483
499 size_t Histogram::BucketIndex(Sample value) const { 484 size_t Histogram::BucketIndex(Sample value) const {
500 // Use simple binary search. This is very general, but there are better 485 // Use simple binary search. This is very general, but there are better
501 // approaches if we knew that the buckets were linearly distributed. 486 // approaches if we knew that the buckets were linearly distributed.
502 DCHECK_LE(ranges(0), value); 487 DCHECK_LE(ranges(0), value);
503 DCHECK_GT(ranges(bucket_count()), value); 488 DCHECK_GT(ranges(bucket_count()), value);
504 size_t under = 0; 489 size_t under = 0;
(...skipping 23 matching lines...) Expand all
528 // not have 0-graphical-height buckets. 513 // not have 0-graphical-height buckets.
529 double Histogram::GetBucketSize(Count current, size_t i) const { 514 double Histogram::GetBucketSize(Count current, size_t i) const {
530 DCHECK_GT(ranges(i + 1), ranges(i)); 515 DCHECK_GT(ranges(i + 1), ranges(i));
531 static const double kTransitionWidth = 5; 516 static const double kTransitionWidth = 5;
532 double denominator = ranges(i + 1) - ranges(i); 517 double denominator = ranges(i + 1) - ranges(i);
533 if (denominator > kTransitionWidth) 518 if (denominator > kTransitionWidth)
534 denominator = kTransitionWidth; // Stop trying to normalize. 519 denominator = kTransitionWidth; // Stop trying to normalize.
535 return current/denominator; 520 return current/denominator;
536 } 521 }
537 522
538 void Histogram::ResetRangeChecksum() { 523 const string Histogram::GetAsciiBucketRange(size_t i) const {
539 range_checksum_ = CalculateRangeChecksum(); 524 string result;
540 }
541
542 const std::string Histogram::GetAsciiBucketRange(size_t i) const {
543 std::string result;
544 if (kHexRangePrintingFlag & flags_) 525 if (kHexRangePrintingFlag & flags_)
545 StringAppendF(&result, "%#x", ranges(i)); 526 StringAppendF(&result, "%#x", ranges(i));
546 else 527 else
547 StringAppendF(&result, "%d", ranges(i)); 528 StringAppendF(&result, "%d", ranges(i));
548 return result; 529 return result;
549 } 530 }
550 531
551 // Update histogram data with new sample. 532 // Update histogram data with new sample.
552 void Histogram::Accumulate(Sample value, Count count, size_t index) { 533 void Histogram::Accumulate(Sample value, Count count, size_t index) {
553 // Note locking not done in this version!!! 534 // Note locking not done in this version!!!
554 sample_.Accumulate(value, count, index); 535 sample_.Accumulate(value, count, index);
555 } 536 }
556 537
557 void Histogram::SetBucketRange(size_t i, Sample value) {
558 DCHECK_GT(bucket_count_, i);
559 DCHECK_GE(value, 0);
560 bucket_ranges_->set_range(i, value);
561 }
562
563 bool Histogram::ValidateBucketRanges() const {
564 // Standard assertions that all bucket ranges should satisfy.
565 DCHECK_EQ(bucket_count_ + 1, bucket_ranges_->size());
566 DCHECK_EQ(0, ranges(0));
567 DCHECK_EQ(declared_min(), ranges(1));
568 DCHECK_EQ(declared_max(), ranges(bucket_count_ - 1));
569 DCHECK_EQ(kSampleType_MAX, ranges(bucket_count_));
570 return true;
571 }
572
573 uint32 Histogram::CalculateRangeChecksum() const {
574 DCHECK_EQ(bucket_ranges_->size(), bucket_count() + 1);
575 // Seed checksum.
576 uint32 checksum = static_cast<uint32>(bucket_ranges_->size());
577 for (size_t index = 0; index < bucket_count(); ++index)
578 checksum = Crc32(checksum, ranges(index));
579 return checksum;
580 }
581
582 void Histogram::Initialize() {
583 sample_.Resize(*this);
584 if (declared_min_ < 1)
585 declared_min_ = 1;
586 if (declared_max_ > kSampleType_MAX - 1)
587 declared_max_ = kSampleType_MAX - 1;
588 DCHECK_LE(declared_min_, declared_max_);
589 DCHECK_GT(bucket_count_, 1u);
590 CHECK_LT(bucket_count_, kBucketCount_MAX);
591 size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
592 DCHECK_LE(bucket_count_, maximal_bucket_count);
593 DCHECK_EQ(0, ranges(0));
594 bucket_ranges_->set_range(bucket_count_, kSampleType_MAX);
595 }
596
597 // We generate the CRC-32 using the low order bits to select whether to XOR in
598 // the reversed polynomial 0xedb88320L. This is nice and simple, and allows us
599 // to keep the quotient in a uint32. Since we're not concerned about the nature
600 // of corruptions (i.e., we don't care about bit sequencing, since we are
601 // handling memory changes, which are more grotesque) so we don't bother to
602 // get the CRC correct for big-endian vs little-ending calculations. All we
603 // need is a nice hash, that tends to depend on all the bits of the sample, with
604 // very little chance of changes in one place impacting changes in another
605 // place.
606 uint32 Histogram::Crc32(uint32 sum, Histogram::Sample range) {
607 const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats.
608 if (kUseRealCrc) {
609 union {
610 Histogram::Sample range;
611 unsigned char bytes[sizeof(Histogram::Sample)];
612 } converter;
613 converter.range = range;
614 for (size_t i = 0; i < sizeof(converter); ++i)
615 sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
616 } else {
617 // Use hash techniques provided in ReallyFastHash, except we don't care
618 // about "avalanching" (which would worsten the hash, and add collisions),
619 // and we don't care about edge cases since we have an even number of bytes.
620 union {
621 Histogram::Sample range;
622 uint16 ints[sizeof(Histogram::Sample) / 2];
623 } converter;
624 DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter));
625 converter.range = range;
626 sum += converter.ints[0];
627 sum = (sum << 16) ^ sum ^ (static_cast<uint32>(converter.ints[1]) << 11);
628 sum += sum >> 11;
629 }
630 return sum;
631 }
632
633 //------------------------------------------------------------------------------ 538 //------------------------------------------------------------------------------
634 // Private methods 539 // Private methods
635 540
541 void Histogram::WriteAsciiImpl(bool graph_it,
542 const string& newline,
543 string* output) const {
544 // Get local (stack) copies of all effectively volatile class data so that we
545 // are consistent across our output activities.
546 SampleSet snapshot;
547 SnapshotSample(&snapshot);
548 Count sample_count = snapshot.TotalCount();
549
550 WriteAsciiHeader(snapshot, sample_count, output);
551 output->append(newline);
552
553 // Prepare to normalize graphical rendering of bucket contents.
554 double max_size = 0;
555 if (graph_it)
556 max_size = GetPeakBucketSize(snapshot);
557
558 // Calculate space needed to print bucket range numbers. Leave room to print
559 // nearly the largest bucket range without sliding over the histogram.
560 size_t largest_non_empty_bucket = bucket_count() - 1;
561 while (0 == snapshot.counts(largest_non_empty_bucket)) {
562 if (0 == largest_non_empty_bucket)
563 break; // All buckets are empty.
564 --largest_non_empty_bucket;
565 }
566
567 // Calculate largest print width needed for any of our bucket range displays.
568 size_t print_width = 1;
569 for (size_t i = 0; i < bucket_count(); ++i) {
570 if (snapshot.counts(i)) {
571 size_t width = GetAsciiBucketRange(i).size() + 1;
572 if (width > print_width)
573 print_width = width;
574 }
575 }
576
577 int64 remaining = sample_count;
578 int64 past = 0;
579 // Output the actual histogram graph.
580 for (size_t i = 0; i < bucket_count(); ++i) {
581 Count current = snapshot.counts(i);
582 if (!current && !PrintEmptyBucket(i))
583 continue;
584 remaining -= current;
585 string range = GetAsciiBucketRange(i);
586 output->append(range);
587 for (size_t j = 0; range.size() + j < print_width + 1; ++j)
588 output->push_back(' ');
589 if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
590 while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
591 ++i;
592 output->append("... ");
593 output->append(newline);
594 continue; // No reason to plot emptiness.
595 }
596 double current_size = GetBucketSize(current, i);
597 if (graph_it)
598 WriteAsciiBucketGraph(current_size, max_size, output);
599 WriteAsciiBucketContext(past, current, remaining, i, output);
600 output->append(newline);
601 past += current;
602 }
603 DCHECK_EQ(sample_count, past);
604 }
605
636 double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { 606 double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
637 double max = 0; 607 double max = 0;
638 for (size_t i = 0; i < bucket_count() ; ++i) { 608 for (size_t i = 0; i < bucket_count() ; ++i) {
639 double current_size = GetBucketSize(snapshot.counts(i), i); 609 double current_size = GetBucketSize(snapshot.counts(i), i);
640 if (current_size > max) 610 if (current_size > max)
641 max = current_size; 611 max = current_size;
642 } 612 }
643 return max; 613 return max;
644 } 614 }
645 615
646 void Histogram::WriteAsciiHeader(const SampleSet& snapshot, 616 void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
647 Count sample_count, 617 Count sample_count,
648 std::string* output) const { 618 string* output) const {
649 StringAppendF(output, 619 StringAppendF(output,
650 "Histogram: %s recorded %d samples", 620 "Histogram: %s recorded %d samples",
651 histogram_name().c_str(), 621 histogram_name().c_str(),
652 sample_count); 622 sample_count);
653 if (0 == sample_count) { 623 if (0 == sample_count) {
654 DCHECK_EQ(snapshot.sum(), 0); 624 DCHECK_EQ(snapshot.sum(), 0);
655 } else { 625 } else {
656 double average = static_cast<float>(snapshot.sum()) / sample_count; 626 double average = static_cast<float>(snapshot.sum()) / sample_count;
657 627
658 StringAppendF(output, ", average = %.1f", average); 628 StringAppendF(output, ", average = %.1f", average);
659 } 629 }
660 if (flags_ & ~kHexRangePrintingFlag) 630 if (flags_ & ~kHexRangePrintingFlag)
661 StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag); 631 StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
662 } 632 }
663 633
664 void Histogram::WriteAsciiBucketContext(const int64 past, 634 void Histogram::WriteAsciiBucketContext(const int64 past,
665 const Count current, 635 const Count current,
666 const int64 remaining, 636 const int64 remaining,
667 const size_t i, 637 const size_t i,
668 std::string* output) const { 638 string* output) const {
669 double scaled_sum = (past + current + remaining) / 100.0; 639 double scaled_sum = (past + current + remaining) / 100.0;
670 WriteAsciiBucketValue(current, scaled_sum, output); 640 WriteAsciiBucketValue(current, scaled_sum, output);
671 if (0 < i) { 641 if (0 < i) {
672 double percentage = past / scaled_sum; 642 double percentage = past / scaled_sum;
673 StringAppendF(output, " {%3.1f%%}", percentage); 643 StringAppendF(output, " {%3.1f%%}", percentage);
674 } 644 }
675 } 645 }
676 646
677 void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, 647 void Histogram::WriteAsciiBucketValue(Count current,
678 std::string* output) const { 648 double scaled_sum,
649 string* output) const {
679 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); 650 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
680 } 651 }
681 652
682 void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, 653 void Histogram::WriteAsciiBucketGraph(double current_size,
683 std::string* output) const { 654 double max_size,
655 string* output) const {
684 const int k_line_length = 72; // Maximal horizontal width of graph. 656 const int k_line_length = 72; // Maximal horizontal width of graph.
685 int x_count = static_cast<int>(k_line_length * (current_size / max_size) 657 int x_count = static_cast<int>(k_line_length * (current_size / max_size)
686 + 0.5); 658 + 0.5);
687 int x_remainder = k_line_length - x_count; 659 int x_remainder = k_line_length - x_count;
688 660
689 while (0 < x_count--) 661 while (0 < x_count--)
690 output->append("-"); 662 output->append("-");
691 output->append("O"); 663 output->append("O");
692 while (0 < x_remainder--) 664 while (0 < x_remainder--)
693 output->append(" "); 665 output->append(" ");
694 } 666 }
695 667
696 //------------------------------------------------------------------------------ 668 //------------------------------------------------------------------------------
697 // Methods for the Histogram::SampleSet class
698 //------------------------------------------------------------------------------
699
700 Histogram::SampleSet::SampleSet()
701 : counts_(),
702 sum_(0),
703 redundant_count_(0) {
704 }
705
706 Histogram::SampleSet::~SampleSet() {
707 }
708
709 void Histogram::SampleSet::Resize(const Histogram& histogram) {
710 counts_.resize(histogram.bucket_count(), 0);
711 }
712
713 void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
714 DCHECK_EQ(histogram.bucket_count(), counts_.size());
715 }
716
717
718 void Histogram::SampleSet::Accumulate(Sample value, Count count,
719 size_t index) {
720 DCHECK(count == 1 || count == -1);
721 counts_[index] += count;
722 sum_ += count * value;
723 redundant_count_ += count;
724 DCHECK_GE(counts_[index], 0);
725 DCHECK_GE(sum_, 0);
726 DCHECK_GE(redundant_count_, 0);
727 }
728
729 Count Histogram::SampleSet::TotalCount() const {
730 Count total = 0;
731 for (Counts::const_iterator it = counts_.begin();
732 it != counts_.end();
733 ++it) {
734 total += *it;
735 }
736 return total;
737 }
738
739 void Histogram::SampleSet::Add(const SampleSet& other) {
740 DCHECK_EQ(counts_.size(), other.counts_.size());
741 sum_ += other.sum_;
742 redundant_count_ += other.redundant_count_;
743 for (size_t index = 0; index < counts_.size(); ++index)
744 counts_[index] += other.counts_[index];
745 }
746
747 void Histogram::SampleSet::Subtract(const SampleSet& other) {
748 DCHECK_EQ(counts_.size(), other.counts_.size());
749 // Note: Race conditions in snapshotting a sum may lead to (temporary)
750 // negative values when snapshots are later combined (and deltas calculated).
751 // As a result, we don't currently CHCEK() for positive values.
752 sum_ -= other.sum_;
753 redundant_count_ -= other.redundant_count_;
754 for (size_t index = 0; index < counts_.size(); ++index) {
755 counts_[index] -= other.counts_[index];
756 DCHECK_GE(counts_[index], 0);
757 }
758 }
759
760 bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
761 pickle->WriteInt64(sum_);
762 pickle->WriteInt64(redundant_count_);
763 pickle->WriteUInt64(counts_.size());
764
765 for (size_t index = 0; index < counts_.size(); ++index) {
766 pickle->WriteInt(counts_[index]);
767 }
768
769 return true;
770 }
771
772 bool Histogram::SampleSet::Deserialize(PickleIterator* iter) {
773 DCHECK_EQ(counts_.size(), 0u);
774 DCHECK_EQ(sum_, 0);
775 DCHECK_EQ(redundant_count_, 0);
776
777 uint64 counts_size;
778
779 if (!iter->ReadInt64(&sum_) ||
780 !iter->ReadInt64(&redundant_count_) ||
781 !iter->ReadUInt64(&counts_size)) {
782 return false;
783 }
784
785 if (counts_size == 0)
786 return false;
787
788 int count = 0;
789 for (uint64 index = 0; index < counts_size; ++index) {
790 int i;
791 if (!iter->ReadInt(&i))
792 return false;
793 counts_.push_back(i);
794 count += i;
795 }
796 DCHECK_EQ(count, redundant_count_);
797 return count == redundant_count_;
798 }
799
800 //------------------------------------------------------------------------------
801 // LinearHistogram: This histogram uses a traditional set of evenly spaced 669 // LinearHistogram: This histogram uses a traditional set of evenly spaced
802 // buckets. 670 // buckets.
803 //------------------------------------------------------------------------------ 671 //------------------------------------------------------------------------------
804 672
805 LinearHistogram::~LinearHistogram() { 673 LinearHistogram::~LinearHistogram() {}
806 }
807 674
808 Histogram* LinearHistogram::FactoryGet(const std::string& name, 675 Histogram* LinearHistogram::FactoryGet(const string& name,
809 Sample minimum, 676 Sample minimum,
810 Sample maximum, 677 Sample maximum,
811 size_t bucket_count, 678 size_t bucket_count,
812 Flags flags) { 679 Flags flags) {
813 if (minimum < 1) 680 CHECK(Histogram::InspectConstructionArguments(name, &minimum, &maximum,
814 minimum = 1; 681 &bucket_count));
815 if (maximum > kSampleType_MAX - 1)
816 maximum = kSampleType_MAX - 1;
817
818 DCHECK_GT(maximum, minimum);
819 DCHECK_GT((Sample) bucket_count, 2);
820 DCHECK_LE((Sample) bucket_count, maximum - minimum + 2);
821 682
822 Histogram* histogram = StatisticsRecorder::FindHistogram(name); 683 Histogram* histogram = StatisticsRecorder::FindHistogram(name);
823 if (!histogram) { 684 if (!histogram) {
824 // To avoid racy destruction at shutdown, the following will be leaked. 685 // To avoid racy destruction at shutdown, the following will be leaked.
686 BucketRanges* ranges = new BucketRanges(bucket_count + 1);
687 InitializeBucketRanges(minimum, maximum, bucket_count, ranges);
688 const BucketRanges* registered_ranges =
689 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
690
825 LinearHistogram* tentative_histogram = 691 LinearHistogram* tentative_histogram =
826 new LinearHistogram(name, minimum, maximum, bucket_count); 692 new LinearHistogram(name, minimum, maximum, bucket_count,
827 tentative_histogram->InitializeBucketRange(); 693 registered_ranges);
828 tentative_histogram->SetFlags(flags); 694 tentative_histogram->SetFlags(flags);
829 histogram = 695 histogram =
830 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 696 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
831 } 697 }
832 698
833 DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); 699 CHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type());
834 DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); 700 CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
835 return histogram; 701 return histogram;
836 } 702 }
837 703
838 Histogram* LinearHistogram::FactoryTimeGet(const std::string& name, 704 Histogram* LinearHistogram::FactoryTimeGet(const string& name,
839 TimeDelta minimum, 705 TimeDelta minimum,
840 TimeDelta maximum, 706 TimeDelta maximum,
841 size_t bucket_count, 707 size_t bucket_count,
842 Flags flags) { 708 Flags flags) {
843 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), 709 return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
844 bucket_count, flags); 710 bucket_count, flags);
845 } 711 }
846 712
847 Histogram::ClassType LinearHistogram::histogram_type() const { 713 Histogram::ClassType LinearHistogram::histogram_type() const {
848 return LINEAR_HISTOGRAM; 714 return LINEAR_HISTOGRAM;
849 } 715 }
850 716
851 void LinearHistogram::SetRangeDescriptions( 717 void LinearHistogram::SetRangeDescriptions(
852 const DescriptionPair descriptions[]) { 718 const DescriptionPair descriptions[]) {
853 for (int i =0; descriptions[i].description; ++i) { 719 for (int i =0; descriptions[i].description; ++i) {
854 bucket_description_[descriptions[i].sample] = descriptions[i].description; 720 bucket_description_[descriptions[i].sample] = descriptions[i].description;
855 } 721 }
856 } 722 }
857 723
858 LinearHistogram::LinearHistogram(const std::string& name, 724 LinearHistogram::LinearHistogram(const string& name,
859 Sample minimum, 725 Sample minimum,
860 Sample maximum, 726 Sample maximum,
861 size_t bucket_count) 727 size_t bucket_count,
862 : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { 728 const BucketRanges* ranges)
863 } 729 : Histogram(name, minimum, maximum, bucket_count, ranges) {
864
865 LinearHistogram::LinearHistogram(const std::string& name,
866 TimeDelta minimum,
867 TimeDelta maximum,
868 size_t bucket_count)
869 : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
870 minimum : TimeDelta::FromMilliseconds(1),
871 maximum, bucket_count) {
872 }
873
874 void LinearHistogram::InitializeBucketRange() {
875 DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here.
876 double min = declared_min();
877 double max = declared_max();
878 size_t i;
879 for (i = 1; i < bucket_count(); ++i) {
880 double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
881 (bucket_count() - 2);
882 SetBucketRange(i, static_cast<int> (linear_range + 0.5));
883 }
884 ResetRangeChecksum();
885 } 730 }
886 731
887 double LinearHistogram::GetBucketSize(Count current, size_t i) const { 732 double LinearHistogram::GetBucketSize(Count current, size_t i) const {
888 DCHECK_GT(ranges(i + 1), ranges(i)); 733 DCHECK_GT(ranges(i + 1), ranges(i));
889 // Adjacent buckets with different widths would have "surprisingly" many (few) 734 // Adjacent buckets with different widths would have "surprisingly" many (few)
890 // samples in a histogram if we didn't normalize this way. 735 // samples in a histogram if we didn't normalize this way.
891 double denominator = ranges(i + 1) - ranges(i); 736 double denominator = ranges(i + 1) - ranges(i);
892 return current/denominator; 737 return current/denominator;
893 } 738 }
894 739
895 const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { 740 const string LinearHistogram::GetAsciiBucketRange(size_t i) const {
896 int range = ranges(i); 741 int range = ranges(i);
897 BucketDescriptionMap::const_iterator it = bucket_description_.find(range); 742 BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
898 if (it == bucket_description_.end()) 743 if (it == bucket_description_.end())
899 return Histogram::GetAsciiBucketRange(i); 744 return Histogram::GetAsciiBucketRange(i);
900 return it->second; 745 return it->second;
901 } 746 }
902 747
903 bool LinearHistogram::PrintEmptyBucket(size_t index) const { 748 bool LinearHistogram::PrintEmptyBucket(size_t index) const {
904 return bucket_description_.find(ranges(index)) == bucket_description_.end(); 749 return bucket_description_.find(ranges(index)) == bucket_description_.end();
905 } 750 }
906 751
752 // static
753 void LinearHistogram::InitializeBucketRanges(Sample minimum,
754 Sample maximum,
755 size_t bucket_count,
756 BucketRanges* ranges) {
757 DCHECK_EQ(ranges->size(), bucket_count + 1);
758 double min = minimum;
759 double max = maximum;
760 size_t i;
761 for (i = 1; i < bucket_count; ++i) {
762 double linear_range =
763 (min * (bucket_count -1 - i) + max * (i - 1)) / (bucket_count - 2);
764 ranges->set_range(i, static_cast<Sample>(linear_range + 0.5));
765 }
766 ranges->set_range(ranges->size() - 1, HistogramBase::kSampleType_MAX);
767 ranges->ResetChecksum();
768 }
907 769
908 //------------------------------------------------------------------------------ 770 //------------------------------------------------------------------------------
909 // This section provides implementation for BooleanHistogram. 771 // This section provides implementation for BooleanHistogram.
910 //------------------------------------------------------------------------------ 772 //------------------------------------------------------------------------------
911 773
912 Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) { 774 Histogram* BooleanHistogram::FactoryGet(const string& name, Flags flags) {
913 Histogram* histogram = StatisticsRecorder::FindHistogram(name); 775 Histogram* histogram = StatisticsRecorder::FindHistogram(name);
914 if (!histogram) { 776 if (!histogram) {
915 // To avoid racy destruction at shutdown, the following will be leaked. 777 // To avoid racy destruction at shutdown, the following will be leaked.
916 BooleanHistogram* tentative_histogram = new BooleanHistogram(name); 778 BucketRanges* ranges = new BucketRanges(4);
917 tentative_histogram->InitializeBucketRange(); 779 LinearHistogram::InitializeBucketRanges(1, 2, 3, ranges);
780 const BucketRanges* registered_ranges =
781 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
782
783 BooleanHistogram* tentative_histogram =
784 new BooleanHistogram(name, registered_ranges);
918 tentative_histogram->SetFlags(flags); 785 tentative_histogram->SetFlags(flags);
919 histogram = 786 histogram =
920 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 787 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
921 } 788 }
922 789
923 DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); 790 CHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type());
924 return histogram; 791 return histogram;
925 } 792 }
926 793
927 Histogram::ClassType BooleanHistogram::histogram_type() const { 794 Histogram::ClassType BooleanHistogram::histogram_type() const {
928 return BOOLEAN_HISTOGRAM; 795 return BOOLEAN_HISTOGRAM;
929 } 796 }
930 797
931 void BooleanHistogram::AddBoolean(bool value) { 798 void BooleanHistogram::AddBoolean(bool value) {
932 Add(value ? 1 : 0); 799 Add(value ? 1 : 0);
933 } 800 }
934 801
935 BooleanHistogram::BooleanHistogram(const std::string& name) 802 BooleanHistogram::BooleanHistogram(const string& name,
936 : LinearHistogram(name, 1, 2, 3) { 803 const BucketRanges* ranges)
937 } 804 : LinearHistogram(name, 1, 2, 3, ranges) {}
938 805
939 //------------------------------------------------------------------------------ 806 //------------------------------------------------------------------------------
940 // CustomHistogram: 807 // CustomHistogram:
941 //------------------------------------------------------------------------------ 808 //------------------------------------------------------------------------------
942 809
943 Histogram* CustomHistogram::FactoryGet(const std::string& name, 810 Histogram* CustomHistogram::FactoryGet(const string& name,
944 const std::vector<Sample>& custom_ranges, 811 const vector<Sample>& custom_ranges,
945 Flags flags) { 812 Flags flags) {
946 // Remove the duplicates in the custom ranges array. 813 CHECK(ValidateCustomRanges(custom_ranges));
947 std::vector<int> ranges = custom_ranges;
948 ranges.push_back(0); // Ensure we have a zero value.
949 std::sort(ranges.begin(), ranges.end());
950 ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
951 if (ranges.size() <= 1) {
952 DCHECK(false);
953 // Note that we pushed a 0 in above, so for defensive code....
954 ranges.push_back(1); // Put in some data so we can index to [1].
955 }
956
957 DCHECK_LT(ranges.back(), kSampleType_MAX);
958 814
959 Histogram* histogram = StatisticsRecorder::FindHistogram(name); 815 Histogram* histogram = StatisticsRecorder::FindHistogram(name);
960 if (!histogram) { 816 if (!histogram) {
817 BucketRanges* ranges = CreateBucketRangesFromCustomRanges(custom_ranges);
818 const BucketRanges* registered_ranges =
819 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
820
961 // To avoid racy destruction at shutdown, the following will be leaked. 821 // To avoid racy destruction at shutdown, the following will be leaked.
962 CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges); 822 CustomHistogram* tentative_histogram =
963 tentative_histogram->InitializedCustomBucketRange(ranges); 823 new CustomHistogram(name, registered_ranges);
964 tentative_histogram->SetFlags(flags); 824 tentative_histogram->SetFlags(flags);
825
965 histogram = 826 histogram =
966 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); 827 StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
967 } 828 }
968 829
969 DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); 830 CHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM);
970 DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(),
971 ranges.size()));
972 return histogram; 831 return histogram;
973 } 832 }
974 833
975 Histogram::ClassType CustomHistogram::histogram_type() const { 834 Histogram::ClassType CustomHistogram::histogram_type() const {
976 return CUSTOM_HISTOGRAM; 835 return CUSTOM_HISTOGRAM;
977 } 836 }
978 837
979 // static 838 // static
980 std::vector<Histogram::Sample> CustomHistogram::ArrayToCustomRanges( 839 vector<Sample> CustomHistogram::ArrayToCustomRanges(
981 const Sample* values, size_t num_values) { 840 const Sample* values, size_t num_values) {
982 std::vector<Sample> all_values; 841 vector<Sample> all_values;
983 for (size_t i = 0; i < num_values; ++i) { 842 for (size_t i = 0; i < num_values; ++i) {
984 Sample value = values[i]; 843 Sample value = values[i];
985 all_values.push_back(value); 844 all_values.push_back(value);
986 845
987 // Ensure that a guard bucket is added. If we end up with duplicate 846 // Ensure that a guard bucket is added. If we end up with duplicate
988 // values, FactoryGet will take care of removing them. 847 // values, FactoryGet will take care of removing them.
989 all_values.push_back(value + 1); 848 all_values.push_back(value + 1);
990 } 849 }
991 return all_values; 850 return all_values;
992 } 851 }
993 852
994 CustomHistogram::CustomHistogram(const std::string& name, 853 CustomHistogram::CustomHistogram(const string& name,
995 const std::vector<Sample>& custom_ranges) 854 const BucketRanges* ranges)
996 : Histogram(name, custom_ranges[1], custom_ranges.back(), 855 : Histogram(name,
997 custom_ranges.size()) { 856 ranges->range(1),
998 DCHECK_GT(custom_ranges.size(), 1u); 857 ranges->range(ranges->size() - 2),
999 DCHECK_EQ(custom_ranges[0], 0); 858 ranges->size() - 1,
1000 } 859 ranges) {}
1001 860
1002 bool CustomHistogram::SerializeRanges(Pickle* pickle) const { 861 bool CustomHistogram::SerializeRanges(Pickle* pickle) const {
1003 for (size_t i = 0; i < bucket_ranges()->size(); ++i) { 862 for (size_t i = 0; i < bucket_ranges()->size(); ++i) {
1004 if (!pickle->WriteInt(bucket_ranges()->range(i))) 863 if (!pickle->WriteInt(bucket_ranges()->range(i)))
1005 return false; 864 return false;
1006 } 865 }
1007 return true; 866 return true;
1008 } 867 }
1009 868
1010 // static 869 // static
1011 bool CustomHistogram::DeserializeRanges( 870 bool CustomHistogram::DeserializeRanges(
1012 PickleIterator* iter, std::vector<Histogram::Sample>* ranges) { 871 PickleIterator* iter, vector<Sample>* ranges) {
1013 for (size_t i = 0; i < ranges->size(); ++i) { 872 for (size_t i = 0; i < ranges->size(); ++i) {
1014 if (!iter->ReadInt(&(*ranges)[i])) 873 if (!iter->ReadInt(&(*ranges)[i]))
1015 return false; 874 return false;
1016 } 875 }
1017 return true; 876 return true;
1018 } 877 }
1019 878
1020 void CustomHistogram::InitializedCustomBucketRange(
1021 const std::vector<Sample>& custom_ranges) {
1022 DCHECK_GT(custom_ranges.size(), 1u);
1023 DCHECK_EQ(custom_ranges[0], 0);
1024 DCHECK_LE(custom_ranges.size(), bucket_count());
1025 for (size_t index = 0; index < custom_ranges.size(); ++index)
1026 SetBucketRange(index, custom_ranges[index]);
1027 ResetRangeChecksum();
1028 }
1029
1030 double CustomHistogram::GetBucketSize(Count current, size_t i) const { 879 double CustomHistogram::GetBucketSize(Count current, size_t i) const {
1031 return 1; 880 return 1;
1032 } 881 }
1033 882
883 // static
884 bool CustomHistogram::ValidateCustomRanges(
885 const vector<Sample>& custom_ranges) {
886 if (custom_ranges.size() < 1)
887 return false;
888 for (size_t i = 0; i < custom_ranges.size(); i++) {
889 Sample s = custom_ranges[i];
890 if (s < 0 || s > HistogramBase::kSampleType_MAX - 1)
891 return false;
892 }
893 return true;
894 }
895
896 // static
897 BucketRanges* CustomHistogram::CreateBucketRangesFromCustomRanges(
898 const vector<Sample>& custom_ranges) {
899 // Remove the duplicates in the custom ranges array.
900 vector<int> ranges = custom_ranges;
901 ranges.push_back(0); // Ensure we have a zero value.
902 ranges.push_back(HistogramBase::kSampleType_MAX);
903 std::sort(ranges.begin(), ranges.end());
904 ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
905
906 BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
907 for (size_t i = 0; i < ranges.size(); i++) {
908 bucket_ranges->set_range(i, ranges[i]);
909 }
910 bucket_ranges->ResetChecksum();
911 return bucket_ranges;
912 }
913
1034 } // namespace base 914 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/histogram.h ('k') | base/metrics/histogram_base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698