| // Copyright 2016 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.library_loader.LibraryLoader; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Utility classes for recording UMA metrics before the native library |
| * may have been loaded. Metrics are cached until the library is known |
| * to be loaded, then committed to the MetricsService all at once. |
| */ |
| public class CachedMetrics { |
| /** |
| * Base class for cached metric objects. Subclasses are expected to call |
| * addToCache() when some metric state gets recorded that requires a later |
| * commit operation when the native library is loaded. |
| */ |
| private abstract static class CachedMetric { |
| private static final List<CachedMetric> sMetrics = new ArrayList<CachedMetric>(); |
| |
| protected final String mName; |
| protected boolean mCached; |
| |
| /** |
| * @param name Name of the metric to record. |
| */ |
| protected CachedMetric(String name) { |
| mName = name; |
| } |
| |
| /** |
| * Adds this object to the sMetrics cache, if it hasn't been added already. |
| * Must be called while holding the synchronized(sMetrics) lock. |
| * Note: The synchronization is not done inside this function because subclasses |
| * need to increment their held values under lock to ensure thread-safety. |
| */ |
| protected final void addToCache() { |
| assert Thread.holdsLock(sMetrics); |
| |
| if (mCached) return; |
| sMetrics.add(this); |
| mCached = true; |
| } |
| |
| /** |
| * Commits the metric. Expects the native library to be loaded. |
| * Must be called while holding the synchronized(sMetrics) lock. |
| */ |
| protected abstract void commitAndClear(); |
| } |
| |
| /** |
| * Caches an action that will be recorded after native side is loaded. |
| */ |
| public static class ActionEvent extends CachedMetric { |
| private int mCount; |
| |
| public ActionEvent(String actionName) { |
| super(actionName); |
| } |
| |
| public void record() { |
| synchronized (CachedMetric.sMetrics) { |
| if (LibraryLoader.getInstance().isInitialized()) { |
| recordWithNative(); |
| } else { |
| mCount++; |
| addToCache(); |
| } |
| } |
| } |
| |
| private void recordWithNative() { |
| RecordUserAction.record(mName); |
| } |
| |
| @Override |
| protected void commitAndClear() { |
| while (mCount > 0) { |
| recordWithNative(); |
| mCount--; |
| } |
| } |
| } |
| |
| /** Caches a set of integer histogram samples. */ |
| public static class SparseHistogramSample extends CachedMetric { |
| private final List<Integer> mSamples = new ArrayList<Integer>(); |
| |
| public SparseHistogramSample(String histogramName) { |
| super(histogramName); |
| } |
| |
| public void record(int sample) { |
| synchronized (CachedMetric.sMetrics) { |
| if (LibraryLoader.getInstance().isInitialized()) { |
| recordWithNative(sample); |
| } else { |
| mSamples.add(sample); |
| addToCache(); |
| } |
| } |
| } |
| |
| private void recordWithNative(int sample) { |
| RecordHistogram.recordSparseSlowlyHistogram(mName, sample); |
| } |
| |
| @Override |
| protected void commitAndClear() { |
| for (Integer sample : mSamples) { |
| recordWithNative(sample); |
| } |
| mSamples.clear(); |
| } |
| } |
| |
| /** Caches a set of enumerated histogram samples. */ |
| public static class EnumeratedHistogramSample extends CachedMetric { |
| private final List<Integer> mSamples = new ArrayList<Integer>(); |
| private final int mMaxValue; |
| |
| public EnumeratedHistogramSample(String histogramName, int maxValue) { |
| super(histogramName); |
| mMaxValue = maxValue; |
| } |
| |
| public void record(int sample) { |
| synchronized (CachedMetric.sMetrics) { |
| if (LibraryLoader.getInstance().isInitialized()) { |
| recordWithNative(sample); |
| } else { |
| mSamples.add(sample); |
| addToCache(); |
| } |
| } |
| } |
| |
| private void recordWithNative(int sample) { |
| RecordHistogram.recordEnumeratedHistogram(mName, sample, mMaxValue); |
| } |
| |
| @Override |
| protected void commitAndClear() { |
| for (Integer sample : mSamples) { |
| recordWithNative(sample); |
| } |
| mSamples.clear(); |
| } |
| } |
| |
| /** Caches a set of times histogram samples. */ |
| public static class TimesHistogramSample extends CachedMetric { |
| private final List<Long> mSamples = new ArrayList<Long>(); |
| private final TimeUnit mTimeUnit; |
| |
| public TimesHistogramSample(String histogramName, TimeUnit timeUnit) { |
| super(histogramName); |
| RecordHistogram.assertTimesHistogramSupportsUnit(timeUnit); |
| mTimeUnit = timeUnit; |
| } |
| |
| public void record(long sample) { |
| synchronized (CachedMetric.sMetrics) { |
| if (LibraryLoader.getInstance().isInitialized()) { |
| recordWithNative(sample); |
| } else { |
| mSamples.add(sample); |
| addToCache(); |
| } |
| } |
| } |
| |
| private void recordWithNative(long sample) { |
| RecordHistogram.recordTimesHistogram(mName, sample, mTimeUnit); |
| } |
| |
| @Override |
| protected void commitAndClear() { |
| for (Long sample : mSamples) { |
| recordWithNative(sample); |
| } |
| mSamples.clear(); |
| } |
| } |
| |
| /** Caches a set of boolean histogram samples. */ |
| public static class BooleanHistogramSample extends CachedMetric { |
| private final List<Boolean> mSamples = new ArrayList<Boolean>(); |
| |
| public BooleanHistogramSample(String histogramName) { |
| super(histogramName); |
| } |
| |
| public void record(boolean sample) { |
| synchronized (CachedMetric.sMetrics) { |
| if (LibraryLoader.getInstance().isInitialized()) { |
| recordWithNative(sample); |
| } else { |
| mSamples.add(sample); |
| addToCache(); |
| } |
| } |
| } |
| |
| private void recordWithNative(boolean sample) { |
| RecordHistogram.recordBooleanHistogram(mName, sample); |
| } |
| |
| @Override |
| protected void commitAndClear() { |
| for (Boolean sample : mSamples) { |
| recordWithNative(sample); |
| } |
| mSamples.clear(); |
| } |
| } |
| |
| /** |
| * Caches a set of custom count histogram samples. |
| * Corresponds to UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro. |
| */ |
| public static class CustomCountHistogramSample extends CachedMetric { |
| private final List<Integer> mSamples = new ArrayList<Integer>(); |
| private final int mMin; |
| private final int mMax; |
| private final int mNumBuckets; |
| |
| public CustomCountHistogramSample(String histogramName, int min, int max, int numBuckets) { |
| super(histogramName); |
| mMin = min; |
| mMax = max; |
| mNumBuckets = numBuckets; |
| } |
| |
| public void record(int sample) { |
| synchronized (CachedMetric.sMetrics) { |
| if (LibraryLoader.getInstance().isInitialized()) { |
| recordWithNative(sample); |
| } else { |
| mSamples.add(sample); |
| addToCache(); |
| } |
| } |
| } |
| |
| private void recordWithNative(int sample) { |
| RecordHistogram.recordCustomCountHistogram(mName, sample, mMin, mMax, mNumBuckets); |
| } |
| |
| @Override |
| protected void commitAndClear() { |
| for (Integer sample : mSamples) { |
| recordWithNative(sample); |
| } |
| mSamples.clear(); |
| } |
| } |
| |
| /** |
| * Caches a set of count histogram samples in range [1, 100). |
| * Corresponds to UMA_HISTOGRAM_COUNTS_100 C++ macro. |
| */ |
| public static class Count100HistogramSample extends CustomCountHistogramSample { |
| public Count100HistogramSample(String histogramName) { |
| super(histogramName, 1, 100, 50); |
| } |
| } |
| |
| /** |
| * Caches a set of count histogram samples in range [1, 1000). |
| * Corresponds to UMA_HISTOGRAM_COUNTS_1000 C++ macro. |
| */ |
| public static class Count1000HistogramSample extends CustomCountHistogramSample { |
| public Count1000HistogramSample(String histogramName) { |
| super(histogramName, 1, 1000, 50); |
| } |
| } |
| |
| /** |
| * Caches a set of count histogram samples in range [1, 1000000). |
| * Corresponds to UMA_HISTOGRAM_COUNTS_1M C++ macro. |
| */ |
| public static class Count1MHistogramSample extends CustomCountHistogramSample { |
| public Count1MHistogramSample(String histogramName) { |
| super(histogramName, 1, 1000000, 50); |
| } |
| } |
| |
| /** |
| * Calls out to native code to commit any cached histograms and events. |
| * Should be called once the native library has been loaded. |
| */ |
| public static void commitCachedMetrics() { |
| synchronized (CachedMetric.sMetrics) { |
| for (CachedMetric metric : CachedMetric.sMetrics) { |
| metric.commitAndClear(); |
| } |
| } |
| } |
| } |