• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base.metrics;
6 
7 import org.chromium.base.VisibleForTesting;
8 import org.chromium.base.annotations.JNINamespace;
9 import org.chromium.base.annotations.MainDex;
10 
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.Map;
14 import java.util.concurrent.TimeUnit;
15 
16 /**
17  * Java API for recording UMA histograms.
18  *
19  * Internally, histograms objects are cached on the Java side by their pointer
20  * values (converted to long). This is safe to do because C++ Histogram objects
21  * are never freed. Caching them on the Java side prevents needing to do costly
22  * Java String to C++ string conversions on the C++ side during lookup.
23  *
24  * Note: the JNI calls are relatively costly - avoid calling these methods in performance-critical
25  * code.
26  */
27 @JNINamespace("base::android")
28 @MainDex
29 public class RecordHistogram {
30     private static Throwable sDisabledBy;
31     private static Map<String, Long> sCache =
32             Collections.synchronizedMap(new HashMap<String, Long>());
33 
34     /**
35      * Tests may not have native initialized, so they may need to disable metrics. The value should
36      * be reset after the test done, to avoid carrying over state to unrelated tests.
37      *
38      * In JUnit tests this can be done automatically using
39      * {@link org.chromium.chrome.browser.DisableHistogramsRule}
40      */
41     @VisibleForTesting
setDisabledForTests(boolean disabled)42     public static void setDisabledForTests(boolean disabled) {
43         if (disabled && sDisabledBy != null) {
44             throw new IllegalStateException("Histograms are already disabled.", sDisabledBy);
45         }
46         sDisabledBy = disabled ? new Throwable() : null;
47     }
48 
getCachedHistogramKey(String name)49     private static long getCachedHistogramKey(String name) {
50         Long key = sCache.get(name);
51         // Note: If key is null, we don't have it cached. In that case, pass 0
52         // to the native code, which gets converted to a null histogram pointer
53         // which will cause the native code to look up the object on the native
54         // side.
55         return (key == null ? 0 : key);
56     }
57 
58     /**
59      * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two
60      * buckets, corresponding to success (true) and failure (false). This is the Java equivalent of
61      * the UMA_HISTOGRAM_BOOLEAN C++ macro.
62      * @param name name of the histogram
63      * @param sample sample to be recorded, either true or false
64      */
recordBooleanHistogram(String name, boolean sample)65     public static void recordBooleanHistogram(String name, boolean sample) {
66         if (sDisabledBy != null) return;
67         long key = getCachedHistogramKey(name);
68         long result = nativeRecordBooleanHistogram(name, key, sample);
69         if (result != key) sCache.put(name, result);
70     }
71 
72     /**
73      * Records a sample in an enumerated histogram of the given name and boundary. Note that
74      * |boundary| identifies the histogram - it should be the same at every invocation. This is the
75      * Java equivalent of the UMA_HISTOGRAM_ENUMERATION C++ macro.
76      * @param name name of the histogram
77      * @param sample sample to be recorded, at least 0 and at most |boundary| - 1
78      * @param boundary upper bound for legal sample values - all sample values have to be strictly
79      *        lower than |boundary|
80      */
recordEnumeratedHistogram(String name, int sample, int boundary)81     public static void recordEnumeratedHistogram(String name, int sample, int boundary) {
82         if (sDisabledBy != null) return;
83         long key = getCachedHistogramKey(name);
84         long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary);
85         if (result != key) sCache.put(name, result);
86     }
87 
88     /**
89      * Records a sample in a count histogram. This is the Java equivalent of the
90      * UMA_HISTOGRAM_COUNTS C++ macro.
91      * @param name name of the histogram
92      * @param sample sample to be recorded, at least 1 and at most 999999
93      */
recordCountHistogram(String name, int sample)94     public static void recordCountHistogram(String name, int sample) {
95         recordCustomCountHistogram(name, sample, 1, 1000000, 50);
96     }
97 
98     /**
99      * Records a sample in a count histogram. This is the Java equivalent of the
100      * UMA_HISTOGRAM_COUNTS_100 C++ macro.
101      * @param name name of the histogram
102      * @param sample sample to be recorded, at least 1 and at most 99
103      */
recordCount100Histogram(String name, int sample)104     public static void recordCount100Histogram(String name, int sample) {
105         recordCustomCountHistogram(name, sample, 1, 100, 50);
106     }
107 
108     /**
109      * Records a sample in a count histogram. This is the Java equivalent of the
110      * UMA_HISTOGRAM_COUNTS_1000 C++ macro.
111      * @param name name of the histogram
112      * @param sample sample to be recorded, at least 1 and at most 999
113      */
recordCount1000Histogram(String name, int sample)114     public static void recordCount1000Histogram(String name, int sample) {
115         recordCustomCountHistogram(name, sample, 1, 1000, 50);
116     }
117 
118     /**
119      * Records a sample in a count histogram. This is the Java equivalent of the
120      * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro.
121      * @param name name of the histogram
122      * @param sample sample to be recorded, at least |min| and at most |max| - 1
123      * @param min lower bound for expected sample values. It must be >= 1
124      * @param max upper bounds for expected sample values
125      * @param numBuckets the number of buckets
126      */
recordCustomCountHistogram( String name, int sample, int min, int max, int numBuckets)127     public static void recordCustomCountHistogram(
128             String name, int sample, int min, int max, int numBuckets) {
129         if (sDisabledBy != null) return;
130         long key = getCachedHistogramKey(name);
131         long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets);
132         if (result != key) sCache.put(name, result);
133     }
134 
135     /**
136      * Records a sample in a linear histogram. This is the Java equivalent for using
137      * base::LinearHistogram.
138      * @param name name of the histogram
139      * @param sample sample to be recorded, at least |min| and at most |max| - 1.
140      * @param min lower bound for expected sample values, should be at least 1.
141      * @param max upper bounds for expected sample values
142      * @param numBuckets the number of buckets
143      */
recordLinearCountHistogram( String name, int sample, int min, int max, int numBuckets)144     public static void recordLinearCountHistogram(
145             String name, int sample, int min, int max, int numBuckets) {
146         if (sDisabledBy != null) return;
147         long key = getCachedHistogramKey(name);
148         long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets);
149         if (result != key) sCache.put(name, result);
150     }
151 
152     /**
153      * Records a sample in a percentage histogram. This is the Java equivalent of the
154      * UMA_HISTOGRAM_PERCENTAGE C++ macro.
155      * @param name name of the histogram
156      * @param sample sample to be recorded, at least 0 and at most 100.
157      */
recordPercentageHistogram(String name, int sample)158     public static void recordPercentageHistogram(String name, int sample) {
159         if (sDisabledBy != null) return;
160         long key = getCachedHistogramKey(name);
161         long result = nativeRecordEnumeratedHistogram(name, key, sample, 101);
162         if (result != key) sCache.put(name, result);
163     }
164 
165     /**
166      * Records a sparse histogram. This is the Java equivalent of UmaHistogramSparse.
167      * @param name name of the histogram
168      * @param sample sample to be recorded. All values of |sample| are valid, including negative
169      *        values.
170      */
recordSparseSlowlyHistogram(String name, int sample)171     public static void recordSparseSlowlyHistogram(String name, int sample) {
172         if (sDisabledBy != null) return;
173         long key = getCachedHistogramKey(name);
174         long result = nativeRecordSparseHistogram(name, key, sample);
175         if (result != key) sCache.put(name, result);
176     }
177 
178     /**
179      * Records a sample in a histogram of times. Useful for recording short durations. This is the
180      * Java equivalent of the UMA_HISTOGRAM_TIMES C++ macro.
181      * Note that histogram samples will always be converted to milliseconds when logged.
182      * @param name name of the histogram
183      * @param duration duration to be recorded
184      * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
185      */
recordTimesHistogram(String name, long duration, TimeUnit timeUnit)186     public static void recordTimesHistogram(String name, long duration, TimeUnit timeUnit) {
187         assertTimesHistogramSupportsUnit(timeUnit);
188         recordCustomTimesHistogramMilliseconds(
189                 name, timeUnit.toMillis(duration), 1, TimeUnit.SECONDS.toMillis(10), 50);
190     }
191 
192     /**
193      * Records a sample in a histogram of times. Useful for recording medium durations. This is the
194      * Java equivalent of the UMA_HISTOGRAM_MEDIUM_TIMES C++ macro.
195      * Note that histogram samples will always be converted to milliseconds when logged.
196      * @param name name of the histogram
197      * @param duration duration to be recorded
198      * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
199      */
recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit)200     public static void recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit) {
201         assertTimesHistogramSupportsUnit(timeUnit);
202         recordCustomTimesHistogramMilliseconds(
203                 name, timeUnit.toMillis(duration), 10, TimeUnit.MINUTES.toMillis(3), 50);
204     }
205 
206     /**
207      * Records a sample in a histogram of times. Useful for recording long durations. This is the
208      * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES C++ macro.
209      * Note that histogram samples will always be converted to milliseconds when logged.
210      * @param name name of the histogram
211      * @param duration duration to be recorded
212      * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
213      */
recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit)214     public static void recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit) {
215         assertTimesHistogramSupportsUnit(timeUnit);
216         recordCustomTimesHistogramMilliseconds(
217                 name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 50);
218     }
219 
220     /**
221      * Records a sample in a histogram of times. Useful for recording long durations. This is the
222      * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES_100 C++ macro.
223      * Note that histogram samples will always be converted to milliseconds when logged.
224      * @param name name of the histogram
225      * @param duration duration to be recorded
226      * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
227      */
recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit)228     public static void recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit) {
229         assertTimesHistogramSupportsUnit(timeUnit);
230         recordCustomTimesHistogramMilliseconds(
231                 name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 100);
232     }
233 
234     /**
235      * Records a sample in a histogram of times with custom buckets. This is the Java equivalent of
236      * the UMA_HISTOGRAM_CUSTOM_TIMES C++ macro.
237      * Note that histogram samples will always be converted to milliseconds when logged.
238      * @param name name of the histogram
239      * @param duration duration to be recorded
240      * @param min the minimum bucket value
241      * @param max the maximum bucket value
242      * @param timeUnit the unit of the duration, min, and max arguments (must be >= MILLISECONDS)
243      * @param numBuckets the number of buckets
244      */
recordCustomTimesHistogram( String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets)245     public static void recordCustomTimesHistogram(
246             String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets) {
247         assertTimesHistogramSupportsUnit(timeUnit);
248         recordCustomTimesHistogramMilliseconds(name, timeUnit.toMillis(duration),
249                 timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets);
250     }
251 
252     /**
253      * Records a sample in a histogram of sizes in KB. This is the Java equivalent of the
254      * UMA_HISTOGRAM_MEMORY_KB C++ macro.
255      *
256      * Good for sizes up to about 500MB.
257      *
258      * @param name name of the histogram.
259      * @param sizeInkB Sample to record in KB.
260      */
recordMemoryKBHistogram(String name, int sizeInKB)261     public static void recordMemoryKBHistogram(String name, int sizeInKB) {
262         recordCustomCountHistogram(name, sizeInKB, 1000, 500000, 50);
263     }
264 
265     /**
266      * Asserts that the time unit is supported by TimesHistogram.
267      * @param timeUnit the unit, must be >= MILLISECONDS
268      */
assertTimesHistogramSupportsUnit(TimeUnit timeUnit)269     /* package */ static void assertTimesHistogramSupportsUnit(TimeUnit timeUnit) {
270         // Use extra variable, or else 'git cl format' produces weird results.
271         boolean supported = timeUnit != TimeUnit.NANOSECONDS && timeUnit != TimeUnit.MICROSECONDS;
272         assert supported : "TimesHistogram doesn't support MICROSECOND and NANOSECONDS time units. "
273                            + "Consider using CountHistogram instead.";
274     }
275 
clampToInt(long value)276     private static int clampToInt(long value) {
277         if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE;
278         // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code
279         // do its own handling of negative values in the future.
280         if (value < Integer.MIN_VALUE) return Integer.MIN_VALUE;
281         return (int) value;
282     }
283 
recordCustomTimesHistogramMilliseconds( String name, long duration, long min, long max, int numBuckets)284     private static void recordCustomTimesHistogramMilliseconds(
285             String name, long duration, long min, long max, int numBuckets) {
286         if (sDisabledBy != null) return;
287         long key = getCachedHistogramKey(name);
288         // Note: Duration, min and max are clamped to int here because that's what's expected by
289         // the native histograms API. Callers of these functions still pass longs because that's
290         // the types returned by TimeUnit and System.currentTimeMillis() APIs, from which these
291         // values come.
292         assert max == clampToInt(max);
293         long result = nativeRecordCustomTimesHistogramMilliseconds(
294                 name, key, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets);
295         if (result != key) sCache.put(name, result);
296     }
297 
298     /**
299      * Returns the number of samples recorded in the given bucket of the given histogram.
300      * @param name name of the histogram to look up
301      * @param sample the bucket containing this sample value will be looked up
302      */
303     @VisibleForTesting
getHistogramValueCountForTesting(String name, int sample)304     public static int getHistogramValueCountForTesting(String name, int sample) {
305         return nativeGetHistogramValueCountForTesting(name, sample);
306     }
307 
308     /**
309      * Returns the number of samples recorded for the given histogram.
310      * @param name name of the histogram to look up.
311      */
312     @VisibleForTesting
getHistogramTotalCountForTesting(String name)313     public static int getHistogramTotalCountForTesting(String name) {
314         return nativeGetHistogramTotalCountForTesting(name);
315     }
316 
nativeRecordCustomTimesHistogramMilliseconds( String name, long key, int duration, int min, int max, int numBuckets)317     private static native long nativeRecordCustomTimesHistogramMilliseconds(
318             String name, long key, int duration, int min, int max, int numBuckets);
319 
nativeRecordBooleanHistogram(String name, long key, boolean sample)320     private static native long nativeRecordBooleanHistogram(String name, long key, boolean sample);
nativeRecordEnumeratedHistogram( String name, long key, int sample, int boundary)321     private static native long nativeRecordEnumeratedHistogram(
322             String name, long key, int sample, int boundary);
nativeRecordCustomCountHistogram( String name, long key, int sample, int min, int max, int numBuckets)323     private static native long nativeRecordCustomCountHistogram(
324             String name, long key, int sample, int min, int max, int numBuckets);
nativeRecordLinearCountHistogram( String name, long key, int sample, int min, int max, int numBuckets)325     private static native long nativeRecordLinearCountHistogram(
326             String name, long key, int sample, int min, int max, int numBuckets);
nativeRecordSparseHistogram(String name, long key, int sample)327     private static native long nativeRecordSparseHistogram(String name, long key, int sample);
328 
nativeGetHistogramValueCountForTesting(String name, int sample)329     private static native int nativeGetHistogramValueCountForTesting(String name, int sample);
nativeGetHistogramTotalCountForTesting(String name)330     private static native int nativeGetHistogramTotalCountForTesting(String name);
331 }
332