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 Throwable sDisabledBy; 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. The value should 34 * be reset after the test done, to avoid carrying over state to unrelated tests. 35 * 36 * In JUnit tests this can be done automatically using 37 * {@link org.chromium.chrome.browser.DisableHistogramsRule} 38 */ 39 @VisibleForTesting 40 public static void setDisabledForTests(boolean disabled) { 41 if (disabled && sDisabledBy != null) { 42 throw new IllegalStateException("Histograms are already disabled.", sDisabledBy); 43 } 44 sDisabledBy = disabled ? new Throwable() : null; 45 } 46 47 private static long getCachedHistogramKey(String name) { 48 Long key = sCache.get(name); 49 // Note: If key is null, we don't have it cached. In that case, pass 0 50 // to the native code, which gets converted to a null histogram pointer 51 // which will cause the native code to look up the object on the native 52 // side. 53 return (key == null ? 0 : key); 54 } 55 56 /** 57 * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two 58 * buckets, corresponding to success (true) and failure (false). This is the Java equivalent of 59 * the UMA_HISTOGRAM_BOOLEAN C++ macro. 60 * @param name name of the histogram 61 * @param sample sample to be recorded, either true or false 62 */ 63 public static void recordBooleanHistogram(String name, boolean sample) { 64 if (sDisabledBy != null) return; 65 long key = getCachedHistogramKey(name); 66 long result = nativeRecordBooleanHistogram(name, key, sample); 67 if (result != key) sCache.put(name, result); 68 } 69 70 /** 71 * Records a sample in an enumerated histogram of the given name and boundary. Note that 72 * |boundary| identifies the histogram - it should be the same at every invocation. This is the 73 * Java equivalent of the UMA_HISTOGRAM_ENUMERATION C++ macro. 74 * @param name name of the histogram 75 * @param sample sample to be recorded, at least 0 and at most |boundary| - 1 76 * @param boundary upper bound for legal sample values - all sample values have to be strictly 77 * lower than |boundary| 78 */ 79 public static void recordEnumeratedHistogram(String name, int sample, int boundary) { 80 if (sDisabledBy != null) return; 81 long key = getCachedHistogramKey(name); 82 long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary); 83 if (result != key) sCache.put(name, result); 84 } 85 86 /** 87 * Records a sample in a count histogram. This is the Java equivalent of the 88 * UMA_HISTOGRAM_COUNTS C++ macro. 89 * @param name name of the histogram 90 * @param sample sample to be recorded, at least 1 and at most 999999 91 */ 92 public static void recordCountHistogram(String name, int sample) { 93 recordCustomCountHistogram(name, sample, 1, 1000000, 50); 94 } 95 96 /** 97 * Records a sample in a count histogram. This is the Java equivalent of the 98 * UMA_HISTOGRAM_COUNTS_100 C++ macro. 99 * @param name name of the histogram 100 * @param sample sample to be recorded, at least 1 and at most 99 101 */ 102 public static void recordCount100Histogram(String name, int sample) { 103 recordCustomCountHistogram(name, sample, 1, 100, 50); 104 } 105 106 /** 107 * Records a sample in a count histogram. This is the Java equivalent of the 108 * UMA_HISTOGRAM_COUNTS_1000 C++ macro. 109 * @param name name of the histogram 110 * @param sample sample to be recorded, at least 1 and at most 999 111 */ 112 public static void recordCount1000Histogram(String name, int sample) { 113 recordCustomCountHistogram(name, sample, 1, 1000, 50); 114 } 115 116 /** 117 * Records a sample in a count histogram. This is the Java equivalent of the 118 * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro. 119 * @param name name of the histogram 120 * @param sample sample to be recorded, at least |min| and at most |max| - 1 121 * @param min lower bound for expected sample values. It must be >= 1 122 * @param max upper bounds for expected sample values 123 * @param numBuckets the number of buckets 124 */ 125 public static void recordCustomCountHistogram( 126 String name, int sample, int min, int max, int numBuckets) { 127 if (sDisabledBy != null) return; 128 long key = getCachedHistogramKey(name); 129 long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets); 130 if (result != key) sCache.put(name, result); 131 } 132 133 /** 134 * Records a sample in a linear histogram. This is the Java equivalent for using 135 * base::LinearHistogram. 136 * @param name name of the histogram 137 * @param sample sample to be recorded, at least |min| and at most |max| - 1. 138 * @param min lower bound for expected sample values, should be at least 1. 139 * @param max upper bounds for expected sample values 140 * @param numBuckets the number of buckets 141 */ 142 public static void recordLinearCountHistogram( 143 String name, int sample, int min, int max, int numBuckets) { 144 if (sDisabledBy != null) return; 145 long key = getCachedHistogramKey(name); 146 long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets); 147 if (result != key) sCache.put(name, result); 148 } 149 150 /** 151 * Records a sample in a percentage histogram. This is the Java equivalent of the 152 * UMA_HISTOGRAM_PERCENTAGE C++ macro. 153 * @param name name of the histogram 154 * @param sample sample to be recorded, at least 0 and at most 100. 155 */ 156 public static void recordPercentageHistogram(String name, int sample) { 157 if (sDisabledBy != null) return; 158 long key = getCachedHistogramKey(name); 159 long result = nativeRecordEnumeratedHistogram(name, key, sample, 101); 160 if (result != key) sCache.put(name, result); 161 } 162 163 /** 164 * Records a sparse histogram. This is the Java equivalent of UMA_HISTOGRAM_SPARSE_SLOWLY. 165 * @param name name of the histogram 166 * @param sample sample to be recorded. All values of |sample| are valid, including negative 167 * values. 168 */ 169 public static void recordSparseSlowlyHistogram(String name, int sample) { 170 if (sDisabledBy != null) return; 171 long key = getCachedHistogramKey(name); 172 long result = nativeRecordSparseHistogram(name, key, sample); 173 if (result != key) sCache.put(name, result); 174 } 175 176 /** 177 * Records a sample in a histogram of times. Useful for recording short durations. This is the 178 * Java equivalent of the UMA_HISTOGRAM_TIMES C++ macro. 179 * @param name name of the histogram 180 * @param duration duration to be recorded 181 * @param timeUnit the unit of the duration argument 182 */ 183 public static void recordTimesHistogram(String name, long duration, TimeUnit timeUnit) { 184 recordCustomTimesHistogramMilliseconds( 185 name, timeUnit.toMillis(duration), 1, TimeUnit.SECONDS.toMillis(10), 50); 186 } 187 188 /** 189 * Records a sample in a histogram of times. Useful for recording medium durations. This is the 190 * Java equivalent of the UMA_HISTOGRAM_MEDIUM_TIMES C++ macro. 191 * @param name name of the histogram 192 * @param duration duration to be recorded 193 * @param timeUnit the unit of the duration argument 194 */ 195 public static void recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit) { 196 recordCustomTimesHistogramMilliseconds( 197 name, timeUnit.toMillis(duration), 10, TimeUnit.MINUTES.toMillis(3), 50); 198 } 199 200 /** 201 * Records a sample in a histogram of times. Useful for recording long durations. This is the 202 * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES C++ macro. 203 * @param name name of the histogram 204 * @param duration duration to be recorded 205 * @param timeUnit the unit of the duration argument 206 */ 207 public static void recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit) { 208 recordCustomTimesHistogramMilliseconds( 209 name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 50); 210 } 211 212 /** 213 * Records a sample in a histogram of times with custom buckets. This is the Java equivalent of 214 * the UMA_HISTOGRAM_CUSTOM_TIMES C++ macro. 215 * @param name name of the histogram 216 * @param duration duration to be recorded 217 * @param min the minimum bucket value 218 * @param max the maximum bucket value 219 * @param timeUnit the unit of the duration, min, and max arguments 220 * @param numBuckets the number of buckets 221 */ 222 public static void recordCustomTimesHistogram( 223 String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets) { 224 recordCustomTimesHistogramMilliseconds(name, timeUnit.toMillis(duration), 225 timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets); 226 } 227 228 /** 229 * Records a sample in a histogram of sizes in KB. This is the Java equivalent of the 230 * UMA_HISTOGRAM_MEMORY_KB C++ macro. 231 * 232 * Good for sizes up to about 500MB. 233 * 234 * @param name name of the histogram. 235 * @param sizeInkB Sample to record in KB. 236 */ 237 public static void recordMemoryKBHistogram(String name, int sizeInKB) { 238 recordCustomCountHistogram(name, sizeInKB, 1000, 500000, 50); 239 } 240 241 private static int clampToInt(long value) { 242 if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE; 243 // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code 244 // do its own handling of negative values in the future. 245 if (value < Integer.MIN_VALUE) return Integer.MIN_VALUE; 246 return (int) value; 247 } 248 249 private static void recordCustomTimesHistogramMilliseconds( 250 String name, long duration, long min, long max, int numBuckets) { 251 if (sDisabledBy != null) return; 252 long key = getCachedHistogramKey(name); 253 // Note: Duration, min and max are clamped to int here because that's what's expected by 254 // the native histograms API. Callers of these functions still pass longs because that's 255 // the types returned by TimeUnit and System.currentTimeMillis() APIs, from which these 256 // values come. 257 long result = nativeRecordCustomTimesHistogramMilliseconds( 258 name, key, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets); 259 if (result != key) sCache.put(name, result); 260 } 261 262 /** 263 * Returns the number of samples recorded in the given bucket of the given histogram. 264 * @param name name of the histogram to look up 265 * @param sample the bucket containing this sample value will be looked up 266 */ 267 @VisibleForTesting 268 public static int getHistogramValueCountForTesting(String name, int sample) { 269 return nativeGetHistogramValueCountForTesting(name, sample); 270 } 271 272 /** 273 * Initializes the metrics system. 274 */ 275 public static void initialize() { 276 if (sDisabledBy != null) return; 277 nativeInitialize(); 278 } 279 280 private static native long nativeRecordCustomTimesHistogramMilliseconds( 281 String name, long key, int duration, int min, int max, int numBuckets); 282 283 private static native long nativeRecordBooleanHistogram(String name, long key, boolean sample); 284 private static native long nativeRecordEnumeratedHistogram( 285 String name, long key, int sample, int boundary); 286 private static native long nativeRecordCustomCountHistogram( 287 String name, long key, int sample, int min, int max, int numBuckets); 288 private static native long nativeRecordLinearCountHistogram( 289 String name, long key, int sample, int min, int max, int numBuckets); 290 private static native long nativeRecordSparseHistogram(String name, long key, int sample); 291 292 private static native int nativeGetHistogramValueCountForTesting(String name, int sample); 293 private static native void nativeInitialize(); 294 } 295