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