• 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 
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.concurrent.TimeUnit;
14 
15 /**
16  * Java API for recording UMA histograms.
17  *
18  * Internally, histograms objects are cached on the Java side by their pointer
19  * values (converted to long). This is safe to do because C++ Histogram objects
20  * are never freed. Caching them on the Java side prevents needing to do costly
21  * Java String to C++ string conversions on the C++ side during lookup.
22  *
23  * Note: the JNI calls are relatively costly - avoid calling these methods in performance-critical
24  * code.
25  */
26 @JNINamespace("base::android")
27 public class RecordHistogram {
28     private static boolean sIsDisabledForTests = false;
29     private static Map<String, Long> sCache =
30             Collections.synchronizedMap(new HashMap<String, Long>());
31 
32     /**
33      * Tests may not have native initialized, so they may need to disable metrics.
34      */
35     @VisibleForTesting
disableForTests()36     public static void disableForTests() {
37         sIsDisabledForTests = true;
38     }
39 
getCachedHistogramKey(String name)40     private static long getCachedHistogramKey(String name) {
41         Long key = sCache.get(name);
42         // Note: If key is null, we don't have it cached. In that case, pass 0
43         // to the native code, which gets converted to a null histogram pointer
44         // which will cause the native code to look up the object on the native
45         // side.
46         return (key == null ? 0 : key);
47     }
48 
49     /**
50      * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two
51      * buckets, corresponding to success (true) and failure (false). This is the Java equivalent of
52      * the UMA_HISTOGRAM_BOOLEAN C++ macro.
53      * @param name name of the histogram
54      * @param sample sample to be recorded, either true or false
55      */
recordBooleanHistogram(String name, boolean sample)56     public static void recordBooleanHistogram(String name, boolean sample) {
57         if (sIsDisabledForTests) return;
58         long key = getCachedHistogramKey(name);
59         long result = nativeRecordBooleanHistogram(name, key, sample);
60         if (result != key) sCache.put(name, result);
61     }
62 
63     /**
64      * Records a sample in an enumerated histogram of the given name and boundary. Note that
65      * |boundary| identifies the histogram - it should be the same at every invocation. This is the
66      * Java equivalent of the UMA_HISTOGRAM_ENUMERATION C++ macro.
67      * @param name name of the histogram
68      * @param sample sample to be recorded, at least 0 and at most |boundary| - 1
69      * @param boundary upper bound for legal sample values - all sample values have to be strictly
70      *        lower than |boundary|
71      */
recordEnumeratedHistogram(String name, int sample, int boundary)72     public static void recordEnumeratedHistogram(String name, int sample, int boundary) {
73         if (sIsDisabledForTests) return;
74         long key = getCachedHistogramKey(name);
75         long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary);
76         if (result != key) sCache.put(name, result);
77     }
78 
79     /**
80      * Records a sample in a count histogram. This is the Java equivalent of the
81      * UMA_HISTOGRAM_COUNTS C++ macro.
82      * @param name name of the histogram
83      * @param sample sample to be recorded, at least 1 and at most 999999
84      */
recordCountHistogram(String name, int sample)85     public static void recordCountHistogram(String name, int sample) {
86         recordCustomCountHistogram(name, sample, 1, 1000000, 50);
87     }
88 
89     /**
90      * Records a sample in a count histogram. This is the Java equivalent of the
91      * UMA_HISTOGRAM_COUNTS_100 C++ macro.
92      * @param name name of the histogram
93      * @param sample sample to be recorded, at least 1 and at most 99
94      */
recordCount100Histogram(String name, int sample)95     public static void recordCount100Histogram(String name, int sample) {
96         recordCustomCountHistogram(name, sample, 1, 100, 50);
97     }
98 
99     /**
100      * Records a sample in a count histogram. This is the Java equivalent of the
101      * UMA_HISTOGRAM_COUNTS_1000 C++ macro.
102      * @param name name of the histogram
103      * @param sample sample to be recorded, at least 1 and at most 999
104      */
recordCount1000Histogram(String name, int sample)105     public static void recordCount1000Histogram(String name, int sample) {
106         recordCustomCountHistogram(name, sample, 1, 1000, 50);
107     }
108 
109     /**
110      * Records a sample in a count histogram. This is the Java equivalent of the
111      * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro.
112      * @param name name of the histogram
113      * @param sample sample to be recorded, at least |min| and at most |max| - 1
114      * @param min lower bound for expected sample values
115      * @param max upper bounds for expected sample values
116      * @param numBuckets the number of buckets
117      */
recordCustomCountHistogram( String name, int sample, int min, int max, int numBuckets)118     public static void recordCustomCountHistogram(
119             String name, int sample, int min, int max, int numBuckets) {
120         if (sIsDisabledForTests) return;
121         long key = getCachedHistogramKey(name);
122         long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets);
123         if (result != key) sCache.put(name, result);
124     }
125 
126     /**
127      * Records a sample in a linear histogram. This is the Java equivalent for using
128      * base::LinearHistogram.
129      * @param name name of the histogram
130      * @param sample sample to be recorded, at least |min| and at most |max| - 1.
131      * @param min lower bound for expected sample values, should be at least 1.
132      * @param max upper bounds for expected sample values
133      * @param numBuckets the number of buckets
134      */
recordLinearCountHistogram( String name, int sample, int min, int max, int numBuckets)135     public static void recordLinearCountHistogram(
136             String name, int sample, int min, int max, int numBuckets) {
137         if (sIsDisabledForTests) return;
138         long key = getCachedHistogramKey(name);
139         long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets);
140         if (result != key) sCache.put(name, result);
141     }
142 
143     /**
144      * Records a sample in a percentage histogram. This is the Java equivalent of the
145      * UMA_HISTOGRAM_PERCENTAGE C++ macro.
146      * @param name name of the histogram
147      * @param sample sample to be recorded, at least 0 and at most 100.
148      */
recordPercentageHistogram(String name, int sample)149     public static void recordPercentageHistogram(String name, int sample) {
150         if (sIsDisabledForTests) return;
151         long key = getCachedHistogramKey(name);
152         long result = nativeRecordEnumeratedHistogram(name, key, sample, 101);
153         if (result != key) sCache.put(name, result);
154     }
155 
156     /**
157     * Records a sparse histogram. This is the Java equivalent of UMA_HISTOGRAM_SPARSE_SLOWLY.
158     * @param name name of the histogram
159     * @param sample sample to be recorded. All values of |sample| are valid, including negative
160     *        values.
161     */
recordSparseSlowlyHistogram(String name, int sample)162     public static void recordSparseSlowlyHistogram(String name, int sample) {
163         if (sIsDisabledForTests) return;
164         long key = getCachedHistogramKey(name);
165         long result = nativeRecordSparseHistogram(name, key, sample);
166         if (result != key) sCache.put(name, result);
167     }
168 
169     /**
170      * Records a sample in a histogram of times. Useful for recording short durations. This is the
171      * Java equivalent of the UMA_HISTOGRAM_TIMES C++ macro.
172      * @param name name of the histogram
173      * @param duration duration to be recorded
174      * @param timeUnit the unit of the duration argument
175      */
recordTimesHistogram(String name, long duration, TimeUnit timeUnit)176     public static void recordTimesHistogram(String name, long duration, TimeUnit timeUnit) {
177         recordCustomTimesHistogramMilliseconds(
178                 name, timeUnit.toMillis(duration), 1, TimeUnit.SECONDS.toMillis(10), 50);
179     }
180 
181     /**
182      * Records a sample in a histogram of times. Useful for recording medium durations. This is the
183      * Java equivalent of the UMA_HISTOGRAM_MEDIUM_TIMES C++ macro.
184      * @param name name of the histogram
185      * @param duration duration to be recorded
186      * @param timeUnit the unit of the duration argument
187      */
recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit)188     public static void recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit) {
189         recordCustomTimesHistogramMilliseconds(
190                 name, timeUnit.toMillis(duration), 10, TimeUnit.MINUTES.toMillis(3), 50);
191     }
192 
193     /**
194      * Records a sample in a histogram of times. Useful for recording long durations. This is the
195      * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES C++ macro.
196      * @param name name of the histogram
197      * @param duration duration to be recorded
198      * @param timeUnit the unit of the duration argument
199      */
recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit)200     public static void recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit) {
201         recordCustomTimesHistogramMilliseconds(
202                 name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 50);
203     }
204 
205     /**
206      * Records a sample in a histogram of times with custom buckets. This is the Java equivalent of
207      * the UMA_HISTOGRAM_CUSTOM_TIMES C++ macro.
208      * @param name name of the histogram
209      * @param duration duration to be recorded
210      * @param min the minimum bucket value
211      * @param max the maximum bucket value
212      * @param timeUnit the unit of the duration, min, and max arguments
213      * @param numBuckets the number of buckets
214      */
recordCustomTimesHistogram( String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets)215     public static void recordCustomTimesHistogram(
216             String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets) {
217         recordCustomTimesHistogramMilliseconds(name, timeUnit.toMillis(duration),
218                 timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets);
219     }
220 
clampToInt(long value)221     private static int clampToInt(long value) {
222         if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE;
223         // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code
224         // do its own handling of negative values in the future.
225         if (value < Integer.MIN_VALUE) return Integer.MIN_VALUE;
226         return (int) value;
227     }
228 
recordCustomTimesHistogramMilliseconds( String name, long duration, long min, long max, int numBuckets)229     private static void recordCustomTimesHistogramMilliseconds(
230             String name, long duration, long min, long max, int numBuckets) {
231         if (sIsDisabledForTests) return;
232         long key = getCachedHistogramKey(name);
233         // Note: Duration, min and max are clamped to int here because that's what's expected by
234         // the native histograms API. Callers of these functions still pass longs because that's
235         // the types returned by TimeUnit and System.currentTimeMillis() APIs, from which these
236         // values come.
237         long result = nativeRecordCustomTimesHistogramMilliseconds(
238                 name, key, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets);
239         if (result != key) sCache.put(name, result);
240     }
241 
242     /**
243      * Returns the number of samples recorded in the given bucket of the given histogram.
244      * @param name name of the histogram to look up
245      * @param sample the bucket containing this sample value will be looked up
246      */
247     @VisibleForTesting
getHistogramValueCountForTesting(String name, int sample)248     public static int getHistogramValueCountForTesting(String name, int sample) {
249         return nativeGetHistogramValueCountForTesting(name, sample);
250     }
251 
252     /**
253      * Initializes the metrics system.
254      */
initialize()255     public static void initialize() {
256         if (sIsDisabledForTests) return;
257         nativeInitialize();
258     }
259 
nativeRecordCustomTimesHistogramMilliseconds( String name, long key, int duration, int min, int max, int numBuckets)260     private static native long nativeRecordCustomTimesHistogramMilliseconds(
261             String name, long key, int duration, int min, int max, int numBuckets);
262 
nativeRecordBooleanHistogram(String name, long key, boolean sample)263     private static native long nativeRecordBooleanHistogram(String name, long key, boolean sample);
nativeRecordEnumeratedHistogram( String name, long key, int sample, int boundary)264     private static native long nativeRecordEnumeratedHistogram(
265             String name, long key, int sample, int boundary);
nativeRecordCustomCountHistogram( String name, long key, int sample, int min, int max, int numBuckets)266     private static native long nativeRecordCustomCountHistogram(
267             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)268     private static native long nativeRecordLinearCountHistogram(
269             String name, long key, int sample, int min, int max, int numBuckets);
nativeRecordSparseHistogram(String name, long key, int sample)270     private static native long nativeRecordSparseHistogram(String name, long key, int sample);
271 
nativeGetHistogramValueCountForTesting(String name, int sample)272     private static native int nativeGetHistogramValueCountForTesting(String name, int sample);
nativeInitialize()273     private static native void nativeInitialize();
274 }
275