| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.base.metrics; |
| |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.base.annotations.JNINamespace; |
| import org.chromium.base.annotations.MainDex; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Java API for recording UMA histograms. |
| * |
| * Internally, histograms objects are cached on the Java side by their pointer |
| * values (converted to long). This is safe to do because C++ Histogram objects |
| * are never freed. Caching them on the Java side prevents needing to do costly |
| * Java String to C++ string conversions on the C++ side during lookup. |
| * |
| * Note: the JNI calls are relatively costly - avoid calling these methods in performance-critical |
| * code. |
| */ |
| @JNINamespace("base::android") |
| @MainDex |
| public class RecordHistogram { |
| private static Throwable sDisabledBy; |
| private static Map<String, Long> sCache = |
| Collections.synchronizedMap(new HashMap<String, Long>()); |
| |
| /** |
| * Tests may not have native initialized, so they may need to disable metrics. The value should |
| * be reset after the test done, to avoid carrying over state to unrelated tests. |
| * |
| * In JUnit tests this can be done automatically using |
| * {@link org.chromium.chrome.browser.DisableHistogramsRule} |
| */ |
| @VisibleForTesting |
| public static void setDisabledForTests(boolean disabled) { |
| if (disabled && sDisabledBy != null) { |
| throw new IllegalStateException("Histograms are already disabled.", sDisabledBy); |
| } |
| sDisabledBy = disabled ? new Throwable() : null; |
| } |
| |
| private static long getCachedHistogramKey(String name) { |
| Long key = sCache.get(name); |
| // Note: If key is null, we don't have it cached. In that case, pass 0 |
| // to the native code, which gets converted to a null histogram pointer |
| // which will cause the native code to look up the object on the native |
| // side. |
| return (key == null ? 0 : key); |
| } |
| |
| /** |
| * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two |
| * buckets, corresponding to success (true) and failure (false). This is the Java equivalent of |
| * the UMA_HISTOGRAM_BOOLEAN C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, either true or false |
| */ |
| public static void recordBooleanHistogram(String name, boolean sample) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| long result = nativeRecordBooleanHistogram(name, key, sample); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Records a sample in an enumerated histogram of the given name and boundary. Note that |
| * |boundary| identifies the histogram - it should be the same at every invocation. This is the |
| * Java equivalent of the UMA_HISTOGRAM_ENUMERATION C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least 0 and at most |boundary| - 1 |
| * @param boundary upper bound for legal sample values - all sample values have to be strictly |
| * lower than |boundary| |
| */ |
| public static void recordEnumeratedHistogram(String name, int sample, int boundary) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Records a sample in a count histogram. This is the Java equivalent of the |
| * UMA_HISTOGRAM_COUNTS_1M C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least 1 and at most 999999 |
| */ |
| public static void recordCountHistogram(String name, int sample) { |
| recordCustomCountHistogram(name, sample, 1, 1000000, 50); |
| } |
| |
| /** |
| * Records a sample in a count histogram. This is the Java equivalent of the |
| * UMA_HISTOGRAM_COUNTS_100 C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least 1 and at most 99 |
| */ |
| public static void recordCount100Histogram(String name, int sample) { |
| recordCustomCountHistogram(name, sample, 1, 100, 50); |
| } |
| |
| /** |
| * Records a sample in a count histogram. This is the Java equivalent of the |
| * UMA_HISTOGRAM_COUNTS_1000 C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least 1 and at most 999 |
| */ |
| public static void recordCount1000Histogram(String name, int sample) { |
| recordCustomCountHistogram(name, sample, 1, 1000, 50); |
| } |
| |
| /** |
| * Records a sample in a count histogram. This is the Java equivalent of the |
| * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least |min| and at most |max| - 1 |
| * @param min lower bound for expected sample values. It must be >= 1 |
| * @param max upper bounds for expected sample values |
| * @param numBuckets the number of buckets |
| */ |
| public static void recordCustomCountHistogram( |
| String name, int sample, int min, int max, int numBuckets) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Records a sample in a linear histogram. This is the Java equivalent for using |
| * base::LinearHistogram. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least |min| and at most |max| - 1. |
| * @param min lower bound for expected sample values, should be at least 1. |
| * @param max upper bounds for expected sample values |
| * @param numBuckets the number of buckets |
| */ |
| public static void recordLinearCountHistogram( |
| String name, int sample, int min, int max, int numBuckets) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Records a sample in a percentage histogram. This is the Java equivalent of the |
| * UMA_HISTOGRAM_PERCENTAGE C++ macro. |
| * @param name name of the histogram |
| * @param sample sample to be recorded, at least 0 and at most 100. |
| */ |
| public static void recordPercentageHistogram(String name, int sample) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| long result = nativeRecordEnumeratedHistogram(name, key, sample, 101); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Records a sparse histogram. This is the Java equivalent of UmaHistogramSparse. |
| * @param name name of the histogram |
| * @param sample sample to be recorded. All values of |sample| are valid, including negative |
| * values. |
| */ |
| public static void recordSparseSlowlyHistogram(String name, int sample) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| long result = nativeRecordSparseHistogram(name, key, sample); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Records a sample in a histogram of times. Useful for recording short durations. This is the |
| * Java equivalent of the UMA_HISTOGRAM_TIMES C++ macro. |
| * Note that histogram samples will always be converted to milliseconds when logged. |
| * @param name name of the histogram |
| * @param duration duration to be recorded |
| * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) |
| */ |
| public static void recordTimesHistogram(String name, long duration, TimeUnit timeUnit) { |
| assertTimesHistogramSupportsUnit(timeUnit); |
| recordCustomTimesHistogramMilliseconds( |
| name, timeUnit.toMillis(duration), 1, TimeUnit.SECONDS.toMillis(10), 50); |
| } |
| |
| /** |
| * Records a sample in a histogram of times. Useful for recording medium durations. This is the |
| * Java equivalent of the UMA_HISTOGRAM_MEDIUM_TIMES C++ macro. |
| * Note that histogram samples will always be converted to milliseconds when logged. |
| * @param name name of the histogram |
| * @param duration duration to be recorded |
| * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) |
| */ |
| public static void recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit) { |
| assertTimesHistogramSupportsUnit(timeUnit); |
| recordCustomTimesHistogramMilliseconds( |
| name, timeUnit.toMillis(duration), 10, TimeUnit.MINUTES.toMillis(3), 50); |
| } |
| |
| /** |
| * Records a sample in a histogram of times. Useful for recording long durations. This is the |
| * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES C++ macro. |
| * Note that histogram samples will always be converted to milliseconds when logged. |
| * @param name name of the histogram |
| * @param duration duration to be recorded |
| * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) |
| */ |
| public static void recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit) { |
| assertTimesHistogramSupportsUnit(timeUnit); |
| recordCustomTimesHistogramMilliseconds( |
| name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 50); |
| } |
| |
| /** |
| * Records a sample in a histogram of times. Useful for recording long durations. This is the |
| * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES_100 C++ macro. |
| * Note that histogram samples will always be converted to milliseconds when logged. |
| * @param name name of the histogram |
| * @param duration duration to be recorded |
| * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) |
| */ |
| public static void recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit) { |
| assertTimesHistogramSupportsUnit(timeUnit); |
| recordCustomTimesHistogramMilliseconds( |
| name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 100); |
| } |
| |
| /** |
| * Records a sample in a histogram of times with custom buckets. This is the Java equivalent of |
| * the UMA_HISTOGRAM_CUSTOM_TIMES C++ macro. |
| * Note that histogram samples will always be converted to milliseconds when logged. |
| * @param name name of the histogram |
| * @param duration duration to be recorded |
| * @param min the minimum bucket value |
| * @param max the maximum bucket value |
| * @param timeUnit the unit of the duration, min, and max arguments (must be >= MILLISECONDS) |
| * @param numBuckets the number of buckets |
| */ |
| public static void recordCustomTimesHistogram( |
| String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets) { |
| assertTimesHistogramSupportsUnit(timeUnit); |
| recordCustomTimesHistogramMilliseconds(name, timeUnit.toMillis(duration), |
| timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets); |
| } |
| |
| /** |
| * Records a sample in a histogram of sizes in KB. This is the Java equivalent of the |
| * UMA_HISTOGRAM_MEMORY_KB C++ macro. |
| * |
| * Good for sizes up to about 500MB. |
| * |
| * @param name name of the histogram. |
| * @param sizeInkB Sample to record in KB. |
| */ |
| public static void recordMemoryKBHistogram(String name, int sizeInKB) { |
| recordCustomCountHistogram(name, sizeInKB, 1000, 500000, 50); |
| } |
| |
| /** |
| * Asserts that the time unit is supported by TimesHistogram. |
| * @param timeUnit the unit, must be >= MILLISECONDS |
| */ |
| /* package */ static void assertTimesHistogramSupportsUnit(TimeUnit timeUnit) { |
| // Use extra variable, or else 'git cl format' produces weird results. |
| boolean supported = timeUnit != TimeUnit.NANOSECONDS && timeUnit != TimeUnit.MICROSECONDS; |
| assert supported : "TimesHistogram doesn't support MICROSECOND and NANOSECONDS time units. " |
| + "Consider using CountHistogram instead."; |
| } |
| |
| private static int clampToInt(long value) { |
| if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE; |
| // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code |
| // do its own handling of negative values in the future. |
| if (value < Integer.MIN_VALUE) return Integer.MIN_VALUE; |
| return (int) value; |
| } |
| |
| private static void recordCustomTimesHistogramMilliseconds( |
| String name, long duration, long min, long max, int numBuckets) { |
| if (sDisabledBy != null) return; |
| long key = getCachedHistogramKey(name); |
| // Note: Duration, min and max are clamped to int here because that's what's expected by |
| // the native histograms API. Callers of these functions still pass longs because that's |
| // the types returned by TimeUnit and System.currentTimeMillis() APIs, from which these |
| // values come. |
| assert max == clampToInt(max); |
| long result = nativeRecordCustomTimesHistogramMilliseconds( |
| name, key, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets); |
| if (result != key) sCache.put(name, result); |
| } |
| |
| /** |
| * Returns the number of samples recorded in the given bucket of the given histogram. |
| * @param name name of the histogram to look up |
| * @param sample the bucket containing this sample value will be looked up |
| */ |
| @VisibleForTesting |
| public static int getHistogramValueCountForTesting(String name, int sample) { |
| return nativeGetHistogramValueCountForTesting(name, sample); |
| } |
| |
| /** |
| * Returns the number of samples recorded for the given histogram. |
| * @param name name of the histogram to look up. |
| */ |
| @VisibleForTesting |
| public static int getHistogramTotalCountForTesting(String name) { |
| return nativeGetHistogramTotalCountForTesting(name); |
| } |
| |
| private static native long nativeRecordCustomTimesHistogramMilliseconds( |
| String name, long key, int duration, int min, int max, int numBuckets); |
| |
| private static native long nativeRecordBooleanHistogram(String name, long key, boolean sample); |
| private static native long nativeRecordEnumeratedHistogram( |
| String name, long key, int sample, int boundary); |
| private static native long nativeRecordCustomCountHistogram( |
| String name, long key, int sample, int min, int max, int numBuckets); |
| private static native long nativeRecordLinearCountHistogram( |
| String name, long key, int sample, int min, int max, int numBuckets); |
| private static native long nativeRecordSparseHistogram(String name, long key, int sample); |
| |
| private static native int nativeGetHistogramValueCountForTesting(String name, int sample); |
| private static native int nativeGetHistogramTotalCountForTesting(String name); |
| } |