• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <stdint.h>
6 
7 #include <map>
8 #include <string>
9 
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_string.h"
12 #include "base/lazy_instance.h"
13 #include "base/macros.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/sparse_histogram.h"
16 #include "base/metrics/statistics_recorder.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/lock.h"
19 #include "base/time/time.h"
20 #include "jni/RecordHistogram_jni.h"
21 
22 namespace base {
23 namespace android {
24 namespace {
25 
26 // Simple thread-safe wrapper for caching histograms. This avoids
27 // relatively expensive JNI string translation for each recording.
28 class HistogramCache {
29  public:
HistogramCache()30   HistogramCache() {}
31 
HistogramConstructionParamsToString(HistogramBase * histogram)32   std::string HistogramConstructionParamsToString(HistogramBase* histogram) {
33     std::string params_str = histogram->histogram_name();
34     switch (histogram->GetHistogramType()) {
35       case HISTOGRAM:
36       case LINEAR_HISTOGRAM:
37       case BOOLEAN_HISTOGRAM:
38       case CUSTOM_HISTOGRAM: {
39         Histogram* hist = static_cast<Histogram*>(histogram);
40         params_str += StringPrintf("/%d/%d/%d", hist->declared_min(),
41                                    hist->declared_max(), hist->bucket_count());
42         break;
43       }
44       case SPARSE_HISTOGRAM:
45       case DUMMY_HISTOGRAM:
46         break;
47     }
48     return params_str;
49   }
50 
JNI_RecordHistogram_CheckHistogramArgs(JNIEnv * env,jstring j_histogram_name,int32_t expected_min,int32_t expected_max,uint32_t expected_bucket_count,HistogramBase * histogram)51   void JNI_RecordHistogram_CheckHistogramArgs(JNIEnv* env,
52                                               jstring j_histogram_name,
53                                               int32_t expected_min,
54                                               int32_t expected_max,
55                                               uint32_t expected_bucket_count,
56                                               HistogramBase* histogram) {
57     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
58     bool valid_arguments = Histogram::InspectConstructionArguments(
59         histogram_name, &expected_min, &expected_max, &expected_bucket_count);
60     DCHECK(valid_arguments);
61     DCHECK(histogram->HasConstructionArguments(expected_min, expected_max,
62                                                expected_bucket_count))
63         << histogram_name << "/" << expected_min << "/" << expected_max << "/"
64         << expected_bucket_count << " vs. "
65         << HistogramConstructionParamsToString(histogram);
66   }
67 
JNI_RecordHistogram_BooleanHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key)68   HistogramBase* JNI_RecordHistogram_BooleanHistogram(JNIEnv* env,
69                                                       jstring j_histogram_name,
70                                                       jlong j_histogram_key) {
71     DCHECK(j_histogram_name);
72     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
73     if (histogram)
74       return histogram;
75 
76     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
77     histogram = BooleanHistogram::FactoryGet(
78         histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
79     return histogram;
80   }
81 
JNI_RecordHistogram_EnumeratedHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_boundary)82   HistogramBase* JNI_RecordHistogram_EnumeratedHistogram(
83       JNIEnv* env,
84       jstring j_histogram_name,
85       jlong j_histogram_key,
86       jint j_boundary) {
87     DCHECK(j_histogram_name);
88     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
89     int32_t boundary = static_cast<int32_t>(j_boundary);
90     if (histogram) {
91       JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, 1, boundary,
92                                              boundary + 1, histogram);
93       return histogram;
94     }
95 
96     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
97     histogram =
98         LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1,
99                                     HistogramBase::kUmaTargetedHistogramFlag);
100     return histogram;
101   }
102 
JNI_RecordHistogram_CustomCountHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_min,jint j_max,jint j_num_buckets)103   HistogramBase* JNI_RecordHistogram_CustomCountHistogram(
104       JNIEnv* env,
105       jstring j_histogram_name,
106       jlong j_histogram_key,
107       jint j_min,
108       jint j_max,
109       jint j_num_buckets) {
110     DCHECK(j_histogram_name);
111     int32_t min = static_cast<int32_t>(j_min);
112     int32_t max = static_cast<int32_t>(j_max);
113     int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
114     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
115     if (histogram) {
116       JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max,
117                                              num_buckets, histogram);
118       return histogram;
119     }
120 
121     DCHECK_GE(min, 1) << "The min expected sample must be >= 1";
122 
123     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
124     histogram =
125         Histogram::FactoryGet(histogram_name, min, max, num_buckets,
126                               HistogramBase::kUmaTargetedHistogramFlag);
127     return histogram;
128   }
129 
JNI_RecordHistogram_LinearCountHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_min,jint j_max,jint j_num_buckets)130   HistogramBase* JNI_RecordHistogram_LinearCountHistogram(
131       JNIEnv* env,
132       jstring j_histogram_name,
133       jlong j_histogram_key,
134       jint j_min,
135       jint j_max,
136       jint j_num_buckets) {
137     DCHECK(j_histogram_name);
138     int32_t min = static_cast<int32_t>(j_min);
139     int32_t max = static_cast<int32_t>(j_max);
140     int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
141     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
142     if (histogram) {
143       JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max,
144                                              num_buckets, histogram);
145       return histogram;
146     }
147 
148     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
149     histogram =
150         LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets,
151                                     HistogramBase::kUmaTargetedHistogramFlag);
152     return histogram;
153   }
154 
JNI_RecordHistogram_SparseHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key)155   HistogramBase* JNI_RecordHistogram_SparseHistogram(JNIEnv* env,
156                                                      jstring j_histogram_name,
157                                                      jlong j_histogram_key) {
158     DCHECK(j_histogram_name);
159     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
160     if (histogram)
161       return histogram;
162 
163     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
164     histogram = SparseHistogram::FactoryGet(
165         histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
166     return histogram;
167   }
168 
JNI_RecordHistogram_CustomTimesHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_min,jint j_max,jint j_bucket_count)169   HistogramBase* JNI_RecordHistogram_CustomTimesHistogram(
170       JNIEnv* env,
171       jstring j_histogram_name,
172       jlong j_histogram_key,
173       jint j_min,
174       jint j_max,
175       jint j_bucket_count) {
176     DCHECK(j_histogram_name);
177     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
178     int32_t min = static_cast<int32_t>(j_min);
179     int32_t max = static_cast<int32_t>(j_max);
180     int32_t bucket_count = static_cast<int32_t>(j_bucket_count);
181     if (histogram) {
182       JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max,
183                                              bucket_count, histogram);
184       return histogram;
185     }
186 
187     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
188     // This intentionally uses FactoryGet and not FactoryTimeGet. FactoryTimeGet
189     // is just a convenience for constructing the underlying Histogram with
190     // TimeDelta arguments.
191     histogram = Histogram::FactoryGet(histogram_name, min, max, bucket_count,
192                                       HistogramBase::kUmaTargetedHistogramFlag);
193     return histogram;
194   }
195 
196  private:
197   // Convert a jlong |histogram_key| from Java to a HistogramBase* via a cast.
198   // The Java side caches these in a map (see RecordHistogram.java), which is
199   // safe to do since C++ Histogram objects are never freed.
HistogramFromKey(jlong j_histogram_key)200   static HistogramBase* HistogramFromKey(jlong j_histogram_key) {
201     return reinterpret_cast<HistogramBase*>(j_histogram_key);
202   }
203 
204   DISALLOW_COPY_AND_ASSIGN(HistogramCache);
205 };
206 
207 LazyInstance<HistogramCache>::Leaky g_histograms;
208 
209 }  // namespace
210 
JNI_RecordHistogram_RecordBooleanHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jboolean j_sample)211 jlong JNI_RecordHistogram_RecordBooleanHistogram(
212     JNIEnv* env,
213     const JavaParamRef<jclass>& clazz,
214     const JavaParamRef<jstring>& j_histogram_name,
215     jlong j_histogram_key,
216     jboolean j_sample) {
217   bool sample = static_cast<bool>(j_sample);
218   HistogramBase* histogram =
219       g_histograms.Get().JNI_RecordHistogram_BooleanHistogram(
220           env, j_histogram_name, j_histogram_key);
221   histogram->AddBoolean(sample);
222   return reinterpret_cast<jlong>(histogram);
223 }
224 
JNI_RecordHistogram_RecordEnumeratedHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_sample,jint j_boundary)225 jlong JNI_RecordHistogram_RecordEnumeratedHistogram(
226     JNIEnv* env,
227     const JavaParamRef<jclass>& clazz,
228     const JavaParamRef<jstring>& j_histogram_name,
229     jlong j_histogram_key,
230     jint j_sample,
231     jint j_boundary) {
232   int sample = static_cast<int>(j_sample);
233 
234   HistogramBase* histogram =
235       g_histograms.Get().JNI_RecordHistogram_EnumeratedHistogram(
236           env, j_histogram_name, j_histogram_key, j_boundary);
237   histogram->Add(sample);
238   return reinterpret_cast<jlong>(histogram);
239 }
240 
JNI_RecordHistogram_RecordCustomCountHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_sample,jint j_min,jint j_max,jint j_num_buckets)241 jlong JNI_RecordHistogram_RecordCustomCountHistogram(
242     JNIEnv* env,
243     const JavaParamRef<jclass>& clazz,
244     const JavaParamRef<jstring>& j_histogram_name,
245     jlong j_histogram_key,
246     jint j_sample,
247     jint j_min,
248     jint j_max,
249     jint j_num_buckets) {
250   int sample = static_cast<int>(j_sample);
251 
252   HistogramBase* histogram =
253       g_histograms.Get().JNI_RecordHistogram_CustomCountHistogram(
254           env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
255   histogram->Add(sample);
256   return reinterpret_cast<jlong>(histogram);
257 }
258 
JNI_RecordHistogram_RecordLinearCountHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_sample,jint j_min,jint j_max,jint j_num_buckets)259 jlong JNI_RecordHistogram_RecordLinearCountHistogram(
260     JNIEnv* env,
261     const JavaParamRef<jclass>& clazz,
262     const JavaParamRef<jstring>& j_histogram_name,
263     jlong j_histogram_key,
264     jint j_sample,
265     jint j_min,
266     jint j_max,
267     jint j_num_buckets) {
268   int sample = static_cast<int>(j_sample);
269 
270   HistogramBase* histogram =
271       g_histograms.Get().JNI_RecordHistogram_LinearCountHistogram(
272           env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
273   histogram->Add(sample);
274   return reinterpret_cast<jlong>(histogram);
275 }
276 
JNI_RecordHistogram_RecordSparseHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_sample)277 jlong JNI_RecordHistogram_RecordSparseHistogram(
278     JNIEnv* env,
279     const JavaParamRef<jclass>& clazz,
280     const JavaParamRef<jstring>& j_histogram_name,
281     jlong j_histogram_key,
282     jint j_sample) {
283   int sample = static_cast<int>(j_sample);
284   HistogramBase* histogram =
285       g_histograms.Get().JNI_RecordHistogram_SparseHistogram(
286           env, j_histogram_name, j_histogram_key);
287   histogram->Add(sample);
288   return reinterpret_cast<jlong>(histogram);
289 }
290 
JNI_RecordHistogram_RecordCustomTimesHistogramMilliseconds(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_duration,jint j_min,jint j_max,jint j_num_buckets)291 jlong JNI_RecordHistogram_RecordCustomTimesHistogramMilliseconds(
292     JNIEnv* env,
293     const JavaParamRef<jclass>& clazz,
294     const JavaParamRef<jstring>& j_histogram_name,
295     jlong j_histogram_key,
296     jint j_duration,
297     jint j_min,
298     jint j_max,
299     jint j_num_buckets) {
300   HistogramBase* histogram =
301       g_histograms.Get().JNI_RecordHistogram_CustomTimesHistogram(
302           env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
303   histogram->AddTime(
304       TimeDelta::FromMilliseconds(static_cast<int64_t>(j_duration)));
305   return reinterpret_cast<jlong>(histogram);
306 }
307 
308 // This backs a Java test util for testing histograms -
309 // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
310 // currently can't have test-specific native code packaged in test-specific Java
311 // targets - see http://crbug.com/415945.
JNI_RecordHistogram_GetHistogramValueCountForTesting(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & histogram_name,jint sample)312 jint JNI_RecordHistogram_GetHistogramValueCountForTesting(
313     JNIEnv* env,
314     const JavaParamRef<jclass>& clazz,
315     const JavaParamRef<jstring>& histogram_name,
316     jint sample) {
317   HistogramBase* histogram = StatisticsRecorder::FindHistogram(
318       android::ConvertJavaStringToUTF8(env, histogram_name));
319   if (histogram == nullptr) {
320     // No samples have been recorded for this histogram (yet?).
321     return 0;
322   }
323 
324   std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
325   return samples->GetCount(static_cast<int>(sample));
326 }
327 
JNI_RecordHistogram_GetHistogramTotalCountForTesting(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & histogram_name)328 jint JNI_RecordHistogram_GetHistogramTotalCountForTesting(
329     JNIEnv* env,
330     const JavaParamRef<jclass>& clazz,
331     const JavaParamRef<jstring>& histogram_name) {
332   HistogramBase* histogram = StatisticsRecorder::FindHistogram(
333       android::ConvertJavaStringToUTF8(env, histogram_name));
334   if (histogram == nullptr) {
335     // No samples have been recorded for this histogram.
336     return 0;
337   }
338 
339   return histogram->SnapshotSamples()->TotalCount();
340 }
341 
342 }  // namespace android
343 }  // namespace base
344