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