OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 | 8 |
9 // It supports calls to accumulate either time intervals (which are processed | 9 // It supports calls to accumulate either time intervals (which are processed |
10 // as integral number of milliseconds), or arbitrary integral units. | 10 // as integral number of milliseconds), or arbitrary integral units. |
(...skipping 27 matching lines...) Expand all Loading... |
38 // pointer is NOT deleted, and we leak the histograms at process termination. | 38 // pointer is NOT deleted, and we leak the histograms at process termination. |
39 | 39 |
40 #ifndef BASE_METRICS_HISTOGRAM_H_ | 40 #ifndef BASE_METRICS_HISTOGRAM_H_ |
41 #define BASE_METRICS_HISTOGRAM_H_ | 41 #define BASE_METRICS_HISTOGRAM_H_ |
42 #pragma once | 42 #pragma once |
43 | 43 |
44 #include <map> | 44 #include <map> |
45 #include <string> | 45 #include <string> |
46 #include <vector> | 46 #include <vector> |
47 | 47 |
| 48 #include "base/atomicops.h" |
48 #include "base/base_export.h" | 49 #include "base/base_export.h" |
49 #include "base/gtest_prod_util.h" | 50 #include "base/gtest_prod_util.h" |
50 #include "base/logging.h" | 51 #include "base/logging.h" |
51 #include "base/time.h" | 52 #include "base/time.h" |
52 | 53 |
53 class Pickle; | 54 class Pickle; |
54 | 55 |
55 namespace base { | 56 namespace base { |
56 | 57 |
57 class Lock; | 58 class Lock; |
| 59 //------------------------------------------------------------------------------ |
| 60 // Histograms are often put in areas where they are called many many times, and |
| 61 // performance is critical. As a result, they are designed to have a very low |
| 62 // recurring cost of executing (adding additional samples). Toward that end, |
| 63 // the macros declare a static pointer to the histogram in question, and only |
| 64 // take a "slow path" to construct (or find) the histogram on the first run |
| 65 // through the macro. We leak the histograms at shutdown time so that we don't |
| 66 // have to validate using the pointers at any time during the running of the |
| 67 // process. |
| 68 |
| 69 // The following code is generally what a thread-safe static pointer |
| 70 // initializaion looks like for a histogram (after a macro is expanded). This |
| 71 // sample is an expansion (with comments) of the code for |
| 72 // HISTOGRAM_CUSTOM_COUNTS(). |
| 73 |
| 74 /* |
| 75 do { |
| 76 // The pointer's presence indicates the initialization is complete. |
| 77 // Initialization is idempotent, so it can safely be atomically repeated. |
| 78 static base::subtle::AtomicWord atomic_histogram_pointer = 0; |
| 79 |
| 80 // Acquire_Load() ensures that we acquire visibility to the pointed-to data |
| 81 // in the histogrom. |
| 82 base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>( |
| 83 base::subtle::Acquire_Load(&atomic_histogram_pointer))); |
| 84 |
| 85 if (!histogram_pointer) { |
| 86 // This is the slow path, which will construct OR find the matching |
| 87 // histogram. FactoryGet includes locks on a global histogram name map |
| 88 // and is completely thread safe. |
| 89 histogram_pointer = base::Histogram::FactoryGet( |
| 90 name, min, max, bucket_count, base::Histogram::kNoFlags); |
| 91 |
| 92 // Use Release_Store to ensure that the histogram data is made available |
| 93 // globally before we make the pointer visible. |
| 94 // Several threads may perform this store, but the same value will be |
| 95 // stored in all cases (for a given named/spec'ed histogram). |
| 96 // We could do this without any barrier, since FactoryGet entered and |
| 97 // exited a lock after construction, but this barrier makes things clear. |
| 98 base::subtle::Release_Store(&atomic_histogram_pointer, |
| 99 reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); |
| 100 } |
| 101 |
| 102 // Ensure calling contract is upheld, and the name does NOT vary. |
| 103 DCHECK(histogram_pointer->histogram_name() == constant_histogram_name); |
| 104 |
| 105 histogram_pointer->Add(sample); |
| 106 } while (0); |
| 107 */ |
| 108 |
| 109 // The above pattern is repeated in several macros. The only elements that |
| 110 // vary are the invocation of the Add(sample) vs AddTime(sample), and the choice |
| 111 // of which FactoryGet method to use. The different FactoryGet methods have |
| 112 // various argument lists, so the function with its argument list is provided as |
| 113 // a macro argument here. The name is only used in a DCHECK, to assure that |
| 114 // callers don't try to vary the name of the histogram (which would tend to be |
| 115 // ignored by the one-time initialization of the histogtram_pointer). |
| 116 #define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ |
| 117 histogram_add_method_invocation, \ |
| 118 histogram_factory_get_invocation) \ |
| 119 do { \ |
| 120 static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ |
| 121 base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>( \ |
| 122 base::subtle::Acquire_Load(&atomic_histogram_pointer))); \ |
| 123 if (!histogram_pointer) { \ |
| 124 histogram_pointer = histogram_factory_get_invocation; \ |
| 125 base::subtle::Release_Store(&atomic_histogram_pointer, \ |
| 126 reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \ |
| 127 } \ |
| 128 DCHECK(histogram_pointer->histogram_name() == constant_histogram_name); \ |
| 129 histogram_pointer->histogram_add_method_invocation; \ |
| 130 } while (0) |
| 131 |
58 | 132 |
59 //------------------------------------------------------------------------------ | 133 //------------------------------------------------------------------------------ |
60 // Provide easy general purpose histogram in a macro, just like stats counters. | 134 // Provide easy general purpose histogram in a macro, just like stats counters. |
61 // The first four macros use 50 buckets. | 135 // The first four macros use 50 buckets. |
62 | 136 |
63 #define HISTOGRAM_TIMES(name, sample) HISTOGRAM_CUSTOM_TIMES( \ | 137 #define HISTOGRAM_TIMES(name, sample) HISTOGRAM_CUSTOM_TIMES( \ |
64 name, sample, base::TimeDelta::FromMilliseconds(1), \ | 138 name, sample, base::TimeDelta::FromMilliseconds(1), \ |
65 base::TimeDelta::FromSeconds(10), 50) | 139 base::TimeDelta::FromSeconds(10), 50) |
66 | 140 |
67 #define HISTOGRAM_COUNTS(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ | 141 #define HISTOGRAM_COUNTS(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ |
68 name, sample, 1, 1000000, 50) | 142 name, sample, 1, 1000000, 50) |
69 | 143 |
70 #define HISTOGRAM_COUNTS_100(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ | 144 #define HISTOGRAM_COUNTS_100(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ |
71 name, sample, 1, 100, 50) | 145 name, sample, 1, 100, 50) |
72 | 146 |
73 #define HISTOGRAM_COUNTS_10000(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ | 147 #define HISTOGRAM_COUNTS_10000(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ |
74 name, sample, 1, 10000, 50) | 148 name, sample, 1, 10000, 50) |
75 | 149 |
76 #define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \ | 150 #define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ |
77 static base::Histogram* counter(NULL); \ | 151 STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ |
78 if (!counter) \ | 152 base::Histogram::FactoryGet(name, min, max, bucket_count, \ |
79 counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \ | 153 base::Histogram::kNoFlags)) |
80 base::Histogram::kNoFlags); \ | |
81 DCHECK_EQ(name, counter->histogram_name()); \ | |
82 counter->Add(sample); \ | |
83 } while (0) | |
84 | 154 |
85 #define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ | 155 #define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ |
86 HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) | 156 HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) |
87 | 157 |
88 // For folks that need real specific times, use this to select a precise range | 158 // For folks that need real specific times, use this to select a precise range |
89 // of times you want plotted, and the number of buckets you want used. | 159 // of times you want plotted, and the number of buckets you want used. |
90 #define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \ | 160 #define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ |
91 static base::Histogram* counter(NULL); \ | 161 STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \ |
92 if (!counter) \ | 162 base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ |
93 counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ | 163 base::Histogram::kNoFlags)) |
94 base::Histogram::kNoFlags); \ | |
95 DCHECK_EQ(name, counter->histogram_name()); \ | |
96 counter->AddTime(sample); \ | |
97 } while (0) | |
98 | |
99 // DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES. | |
100 #define HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ | |
101 static base::Histogram* counter(NULL); \ | |
102 if (!counter) \ | |
103 counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ | |
104 base::Histogram::kNoFlags); \ | |
105 DCHECK_EQ(name, counter->histogram_name()); \ | |
106 if ((sample) < (max)) counter->AddTime(sample); \ | |
107 } while (0) | |
108 | 164 |
109 // Support histograming of an enumerated value. The samples should always be | 165 // Support histograming of an enumerated value. The samples should always be |
110 // less than boundary_value. | 166 // less than boundary_value. |
111 #define HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \ | 167 #define HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ |
112 static base::Histogram* counter(NULL); \ | 168 STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ |
113 if (!counter) \ | 169 base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ |
114 counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ | 170 boundary_value + 1, base::Histogram::kNoFlags)) |
115 boundary_value + 1, base::Histogram::kNoFlags); \ | |
116 DCHECK_EQ(name, counter->histogram_name()); \ | |
117 counter->Add(sample); \ | |
118 } while (0) | |
119 | 171 |
120 // Support histograming of an enumerated value. Samples should be one of the | 172 // Support histograming of an enumerated value. Samples should be one of the |
121 // std::vector<int> list provided via |custom_ranges|. You can use the helper | 173 // std::vector<int> list provided via |custom_ranges|. You can use the helper |
122 // function |base::CustomHistogram::ArrayToCustomRanges(samples, num_samples)| | 174 // function |base::CustomHistogram::ArrayToCustomRanges(samples, num_samples)| |
123 // to transform a C-style array of valid sample values to a std::vector<int>. | 175 // to transform a C-style array of valid sample values to a std::vector<int>. |
124 #define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \ | 176 #define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ |
125 static base::Histogram* counter(NULL); \ | 177 STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ |
126 if (!counter) \ | 178 base::CustomHistogram::FactoryGet(name, custom_ranges, \ |
127 counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \ | 179 base::Histogram::kNoFlags)) |
128 base::Histogram::kNoFlags); \ | |
129 DCHECK_EQ(name, counter->histogram_name()); \ | |
130 counter->Add(sample); \ | |
131 } while (0) | |
132 | |
133 | 180 |
134 //------------------------------------------------------------------------------ | 181 //------------------------------------------------------------------------------ |
135 // Define Debug vs non-debug flavors of macros. | 182 // Define Debug vs non-debug flavors of macros. |
136 #ifndef NDEBUG | 183 #ifndef NDEBUG |
137 | 184 |
138 #define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample) | 185 #define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample) |
139 #define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample) | 186 #define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample) |
140 #define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\ | 187 #define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\ |
141 name, under_one_hundred) | 188 name, under_one_hundred) |
142 #define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ | 189 #define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 | 226 |
180 #define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ | 227 #define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ |
181 name, sample, base::TimeDelta::FromMilliseconds(10), \ | 228 name, sample, base::TimeDelta::FromMilliseconds(10), \ |
182 base::TimeDelta::FromMinutes(3), 50) | 229 base::TimeDelta::FromMinutes(3), 50) |
183 | 230 |
184 // Use this macro when times can routinely be much longer than 10 seconds. | 231 // Use this macro when times can routinely be much longer than 10 seconds. |
185 #define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ | 232 #define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ |
186 name, sample, base::TimeDelta::FromMilliseconds(1), \ | 233 name, sample, base::TimeDelta::FromMilliseconds(1), \ |
187 base::TimeDelta::FromHours(1), 50) | 234 base::TimeDelta::FromHours(1), 50) |
188 | 235 |
189 #define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \ | 236 #define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ |
190 static base::Histogram* counter(NULL); \ | 237 STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \ |
191 if (!counter) \ | 238 base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ |
192 counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ | 239 base::Histogram::kUmaTargetedHistogramFlag)) |
193 base::Histogram::kUmaTargetedHistogramFlag); \ | |
194 DCHECK_EQ(name, counter->histogram_name()); \ | |
195 counter->AddTime(sample); \ | |
196 } while (0) | |
197 | |
198 // DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES. | |
199 #define UMA_HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ | |
200 static base::Histogram* counter(NULL); \ | |
201 if (!counter) \ | |
202 counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ | |
203 base::Histogram::kUmaTargetedHistogramFlag); \ | |
204 DCHECK_EQ(name, counter->histogram_name()); \ | |
205 if ((sample) < (max)) counter->AddTime(sample); \ | |
206 } while (0) | |
207 | 240 |
208 #define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 241 #define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
209 name, sample, 1, 1000000, 50) | 242 name, sample, 1, 1000000, 50) |
210 | 243 |
211 #define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 244 #define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
212 name, sample, 1, 100, 50) | 245 name, sample, 1, 100, 50) |
213 | 246 |
214 #define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 247 #define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
215 name, sample, 1, 10000, 50) | 248 name, sample, 1, 10000, 50) |
216 | 249 |
217 #define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \ | 250 #define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ |
218 static base::Histogram* counter(NULL); \ | 251 STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ |
219 if (!counter) \ | 252 base::Histogram::FactoryGet(name, min, max, bucket_count, \ |
220 counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \ | 253 base::Histogram::kUmaTargetedHistogramFlag)) |
221 base::Histogram::kUmaTargetedHistogramFlag); \ | |
222 DCHECK_EQ(name, counter->histogram_name()); \ | |
223 counter->Add(sample); \ | |
224 } while (0) | |
225 | 254 |
226 #define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 255 #define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
227 name, sample, 1000, 500000, 50) | 256 name, sample, 1000, 500000, 50) |
228 | 257 |
229 #define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 258 #define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
230 name, sample, 1, 1000, 50) | 259 name, sample, 1, 1000, 50) |
231 | 260 |
232 #define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ | 261 #define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ |
233 UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) | 262 UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) |
234 | 263 |
235 #define UMA_HISTOGRAM_BOOLEAN(name, sample) do { \ | 264 #define UMA_HISTOGRAM_BOOLEAN(name, sample) \ |
236 static base::Histogram* counter(NULL); \ | 265 STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ |
237 if (!counter) \ | 266 base::BooleanHistogram::FactoryGet(name, \ |
238 counter = base::BooleanHistogram::FactoryGet(name, \ | 267 base::Histogram::kUmaTargetedHistogramFlag)) |
239 base::Histogram::kUmaTargetedHistogramFlag); \ | |
240 DCHECK_EQ(name, counter->histogram_name()); \ | |
241 counter->AddBoolean(sample); \ | |
242 } while (0) | |
243 | 268 |
244 #define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \ | 269 #define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ |
245 static base::Histogram* counter(NULL); \ | 270 STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ |
246 if (!counter) \ | 271 base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ |
247 counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ | 272 boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag)) |
248 boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag); \ | |
249 DCHECK_EQ(name, counter->histogram_name()); \ | |
250 counter->Add(sample); \ | |
251 } while (0) | |
252 | 273 |
253 #define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \ | 274 #define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ |
254 static base::Histogram* counter(NULL); \ | 275 STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ |
255 if (!counter) \ | 276 base::CustomHistogram::FactoryGet(name, custom_ranges, \ |
256 counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \ | 277 base::Histogram::kUmaTargetedHistogramFlag)) |
257 base::Histogram::kUmaTargetedHistogramFlag); \ | |
258 DCHECK_EQ(name, counter->histogram_name()); \ | |
259 counter->Add(sample); \ | |
260 } while (0) | |
261 | 278 |
262 //------------------------------------------------------------------------------ | 279 //------------------------------------------------------------------------------ |
263 | 280 |
264 class BooleanHistogram; | 281 class BooleanHistogram; |
265 class CustomHistogram; | 282 class CustomHistogram; |
266 class Histogram; | 283 class Histogram; |
267 class LinearHistogram; | 284 class LinearHistogram; |
268 | 285 |
269 class BASE_EXPORT Histogram { | 286 class BASE_EXPORT Histogram { |
270 public: | 287 public: |
(...skipping 473 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
744 | 761 |
745 // Dump all known histograms to log. | 762 // Dump all known histograms to log. |
746 static bool dump_on_exit_; | 763 static bool dump_on_exit_; |
747 | 764 |
748 DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); | 765 DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); |
749 }; | 766 }; |
750 | 767 |
751 } // namespace base | 768 } // namespace base |
752 | 769 |
753 #endif // BASE_METRICS_HISTOGRAM_H_ | 770 #endif // BASE_METRICS_HISTOGRAM_H_ |
OLD | NEW |