• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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.library_loader.LibraryLoader;
8 
9 import java.util.ArrayList;
10 import java.util.List;
11 import java.util.concurrent.TimeUnit;
12 
13 /**
14  * Utility classes for recording UMA metrics before the native library
15  * may have been loaded.  Metrics are cached until the library is known
16  * to be loaded, then committed to the MetricsService all at once.
17  */
18 public class CachedMetrics {
19     /**
20      * Base class for cached metric objects. Subclasses are expected to call
21      * addToCache() when some metric state gets recorded that requires a later
22      * commit operation when the native library is loaded.
23      */
24     private abstract static class CachedMetric {
25         private static final List<CachedMetric> sMetrics = new ArrayList<CachedMetric>();
26 
27         protected final String mName;
28         protected boolean mCached;
29 
30         /**
31          * @param name Name of the metric to record.
32          */
CachedMetric(String name)33         protected CachedMetric(String name) {
34             mName = name;
35         }
36 
37         /**
38          * Adds this object to the sMetrics cache, if it hasn't been added already.
39          * Must be called while holding the synchronized(sMetrics) lock.
40          * Note: The synchronization is not done inside this function because subclasses
41          * need to increment their held values under lock to ensure thread-safety.
42          */
addToCache()43         protected final void addToCache() {
44             assert Thread.holdsLock(sMetrics);
45 
46             if (mCached) return;
47             sMetrics.add(this);
48             mCached = true;
49         }
50 
51         /**
52          * Commits the metric. Expects the native library to be loaded.
53          * Must be called while holding the synchronized(sMetrics) lock.
54          */
commitAndClear()55         protected abstract void commitAndClear();
56     }
57 
58     /**
59      * Caches an action that will be recorded after native side is loaded.
60      */
61     public static class ActionEvent extends CachedMetric {
62         private int mCount;
63 
ActionEvent(String actionName)64         public ActionEvent(String actionName) {
65             super(actionName);
66         }
67 
record()68         public void record() {
69             synchronized (CachedMetric.sMetrics) {
70                 if (LibraryLoader.getInstance().isInitialized()) {
71                     recordWithNative();
72                 } else {
73                     mCount++;
74                     addToCache();
75                 }
76             }
77         }
78 
recordWithNative()79         private void recordWithNative() {
80             RecordUserAction.record(mName);
81         }
82 
83         @Override
commitAndClear()84         protected void commitAndClear() {
85             while (mCount > 0) {
86                 recordWithNative();
87                 mCount--;
88             }
89         }
90     }
91 
92     /** Caches a set of integer histogram samples. */
93     public static class SparseHistogramSample extends CachedMetric {
94         private final List<Integer> mSamples = new ArrayList<Integer>();
95 
SparseHistogramSample(String histogramName)96         public SparseHistogramSample(String histogramName) {
97             super(histogramName);
98         }
99 
record(int sample)100         public void record(int sample) {
101             synchronized (CachedMetric.sMetrics) {
102                 if (LibraryLoader.getInstance().isInitialized()) {
103                     recordWithNative(sample);
104                 } else {
105                     mSamples.add(sample);
106                     addToCache();
107                 }
108             }
109         }
110 
recordWithNative(int sample)111         private void recordWithNative(int sample) {
112             RecordHistogram.recordSparseSlowlyHistogram(mName, sample);
113         }
114 
115         @Override
commitAndClear()116         protected void commitAndClear() {
117             for (Integer sample : mSamples) {
118                 recordWithNative(sample);
119             }
120             mSamples.clear();
121         }
122     }
123 
124     /** Caches a set of enumerated histogram samples. */
125     public static class EnumeratedHistogramSample extends CachedMetric {
126         private final List<Integer> mSamples = new ArrayList<Integer>();
127         private final int mMaxValue;
128 
EnumeratedHistogramSample(String histogramName, int maxValue)129         public EnumeratedHistogramSample(String histogramName, int maxValue) {
130             super(histogramName);
131             mMaxValue = maxValue;
132         }
133 
record(int sample)134         public void record(int sample) {
135             synchronized (CachedMetric.sMetrics) {
136                 if (LibraryLoader.getInstance().isInitialized()) {
137                     recordWithNative(sample);
138                 } else {
139                     mSamples.add(sample);
140                     addToCache();
141                 }
142             }
143         }
144 
recordWithNative(int sample)145         private void recordWithNative(int sample) {
146             RecordHistogram.recordEnumeratedHistogram(mName, sample, mMaxValue);
147         }
148 
149         @Override
commitAndClear()150         protected void commitAndClear() {
151             for (Integer sample : mSamples) {
152                 recordWithNative(sample);
153             }
154             mSamples.clear();
155         }
156     }
157 
158     /** Caches a set of times histogram samples. */
159     public static class TimesHistogramSample extends CachedMetric {
160         private final List<Long> mSamples = new ArrayList<Long>();
161         private final TimeUnit mTimeUnit;
162 
TimesHistogramSample(String histogramName, TimeUnit timeUnit)163         public TimesHistogramSample(String histogramName, TimeUnit timeUnit) {
164             super(histogramName);
165             RecordHistogram.assertTimesHistogramSupportsUnit(timeUnit);
166             mTimeUnit = timeUnit;
167         }
168 
record(long sample)169         public void record(long sample) {
170             synchronized (CachedMetric.sMetrics) {
171                 if (LibraryLoader.getInstance().isInitialized()) {
172                     recordWithNative(sample);
173                 } else {
174                     mSamples.add(sample);
175                     addToCache();
176                 }
177             }
178         }
179 
recordWithNative(long sample)180         private void recordWithNative(long sample) {
181             RecordHistogram.recordTimesHistogram(mName, sample, mTimeUnit);
182         }
183 
184         @Override
commitAndClear()185         protected void commitAndClear() {
186             for (Long sample : mSamples) {
187                 recordWithNative(sample);
188             }
189             mSamples.clear();
190         }
191     }
192 
193     /** Caches a set of boolean histogram samples. */
194     public static class BooleanHistogramSample extends CachedMetric {
195         private final List<Boolean> mSamples = new ArrayList<Boolean>();
196 
BooleanHistogramSample(String histogramName)197         public BooleanHistogramSample(String histogramName) {
198             super(histogramName);
199         }
200 
record(boolean sample)201         public void record(boolean sample) {
202             synchronized (CachedMetric.sMetrics) {
203                 if (LibraryLoader.getInstance().isInitialized()) {
204                     recordWithNative(sample);
205                 } else {
206                     mSamples.add(sample);
207                     addToCache();
208                 }
209             }
210         }
211 
recordWithNative(boolean sample)212         private void recordWithNative(boolean sample) {
213             RecordHistogram.recordBooleanHistogram(mName, sample);
214         }
215 
216         @Override
commitAndClear()217         protected void commitAndClear() {
218             for (Boolean sample : mSamples) {
219                 recordWithNative(sample);
220             }
221             mSamples.clear();
222         }
223     }
224 
225     /**
226      * Caches a set of custom count histogram samples.
227      * Corresponds to UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro.
228      */
229     public static class CustomCountHistogramSample extends CachedMetric {
230         private final List<Integer> mSamples = new ArrayList<Integer>();
231         private final int mMin;
232         private final int mMax;
233         private final int mNumBuckets;
234 
CustomCountHistogramSample(String histogramName, int min, int max, int numBuckets)235         public CustomCountHistogramSample(String histogramName, int min, int max, int numBuckets) {
236             super(histogramName);
237             mMin = min;
238             mMax = max;
239             mNumBuckets = numBuckets;
240         }
241 
record(int sample)242         public void record(int sample) {
243             synchronized (CachedMetric.sMetrics) {
244                 if (LibraryLoader.getInstance().isInitialized()) {
245                     recordWithNative(sample);
246                 } else {
247                     mSamples.add(sample);
248                     addToCache();
249                 }
250             }
251         }
252 
recordWithNative(int sample)253         private void recordWithNative(int sample) {
254             RecordHistogram.recordCustomCountHistogram(mName, sample, mMin, mMax, mNumBuckets);
255         }
256 
257         @Override
commitAndClear()258         protected void commitAndClear() {
259             for (Integer sample : mSamples) {
260                 recordWithNative(sample);
261             }
262             mSamples.clear();
263         }
264     }
265 
266     /**
267      * Caches a set of count histogram samples in range [1, 100).
268      * Corresponds to UMA_HISTOGRAM_COUNTS_100 C++ macro.
269      */
270     public static class Count100HistogramSample extends CustomCountHistogramSample {
Count100HistogramSample(String histogramName)271         public Count100HistogramSample(String histogramName) {
272             super(histogramName, 1, 100, 50);
273         }
274     }
275 
276     /**
277      * Caches a set of count histogram samples in range [1, 1000).
278      * Corresponds to UMA_HISTOGRAM_COUNTS_1000 C++ macro.
279      */
280     public static class Count1000HistogramSample extends CustomCountHistogramSample {
Count1000HistogramSample(String histogramName)281         public Count1000HistogramSample(String histogramName) {
282             super(histogramName, 1, 1000, 50);
283         }
284     }
285 
286     /**
287      * Caches a set of count histogram samples in range [1, 1000000).
288      * Corresponds to UMA_HISTOGRAM_COUNTS_1M C++ macro.
289      */
290     public static class Count1MHistogramSample extends CustomCountHistogramSample {
Count1MHistogramSample(String histogramName)291         public Count1MHistogramSample(String histogramName) {
292             super(histogramName, 1, 1000000, 50);
293         }
294     }
295 
296     /**
297      * Calls out to native code to commit any cached histograms and events.
298      * Should be called once the native library has been loaded.
299      */
commitCachedMetrics()300     public static void commitCachedMetrics() {
301         synchronized (CachedMetric.sMetrics) {
302             for (CachedMetric metric : CachedMetric.sMetrics) {
303                 metric.commitAndClear();
304             }
305         }
306     }
307 }
308