• 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,uint32_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                           uint32_t expected_bucket_count,
57                           HistogramBase* histogram) {
58     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
59     bool valid_arguments = Histogram::InspectConstructionArguments(
60         histogram_name, &expected_min, &expected_max, &expected_bucket_count);
61     DCHECK(valid_arguments);
62     DCHECK(histogram->HasConstructionArguments(expected_min, expected_max,
63                                                expected_bucket_count))
64         << histogram_name << "/" << expected_min << "/" << expected_max << "/"
65         << expected_bucket_count << " vs. "
66         << HistogramConstructionParamsToString(histogram);
67   }
68 
BooleanHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key)69   HistogramBase* BooleanHistogram(JNIEnv* env,
70                                   jstring j_histogram_name,
71                                   jlong j_histogram_key) {
72     DCHECK(j_histogram_name);
73     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
74     if (histogram)
75       return histogram;
76 
77     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
78     histogram = BooleanHistogram::FactoryGet(
79         histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
80     return histogram;
81   }
82 
EnumeratedHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_boundary)83   HistogramBase* EnumeratedHistogram(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       CheckHistogramArgs(env, j_histogram_name, 1, boundary, boundary + 1,
92                          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 
CustomCountHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_min,jint j_max,jint j_num_buckets)103   HistogramBase* CustomCountHistogram(JNIEnv* env,
104                                       jstring j_histogram_name,
105                                       jlong j_histogram_key,
106                                       jint j_min,
107                                       jint j_max,
108                                       jint j_num_buckets) {
109     DCHECK(j_histogram_name);
110     int32_t min = static_cast<int32_t>(j_min);
111     int32_t max = static_cast<int32_t>(j_max);
112     int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
113     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
114     if (histogram) {
115       CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets,
116                          histogram);
117       return histogram;
118     }
119 
120     DCHECK_GE(min, 1) << "The min expected sample must be >= 1";
121 
122     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
123     histogram =
124         Histogram::FactoryGet(histogram_name, min, max, num_buckets,
125                               HistogramBase::kUmaTargetedHistogramFlag);
126     return histogram;
127   }
128 
LinearCountHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_min,jint j_max,jint j_num_buckets)129   HistogramBase* LinearCountHistogram(JNIEnv* env,
130                                       jstring j_histogram_name,
131                                       jlong j_histogram_key,
132                                       jint j_min,
133                                       jint j_max,
134                                       jint j_num_buckets) {
135     DCHECK(j_histogram_name);
136     int32_t min = static_cast<int32_t>(j_min);
137     int32_t max = static_cast<int32_t>(j_max);
138     int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
139     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
140     if (histogram) {
141       CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets,
142                          histogram);
143       return histogram;
144     }
145 
146     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
147     histogram =
148         LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets,
149                                     HistogramBase::kUmaTargetedHistogramFlag);
150     return histogram;
151   }
152 
SparseHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key)153   HistogramBase* SparseHistogram(JNIEnv* env,
154                                  jstring j_histogram_name,
155                                  jlong j_histogram_key) {
156     DCHECK(j_histogram_name);
157     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
158     if (histogram)
159       return histogram;
160 
161     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
162     histogram = SparseHistogram::FactoryGet(
163         histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
164     return histogram;
165   }
166 
CustomTimesHistogram(JNIEnv * env,jstring j_histogram_name,jlong j_histogram_key,jint j_min,jint j_max,jint j_bucket_count)167   HistogramBase* CustomTimesHistogram(JNIEnv* env,
168                                       jstring j_histogram_name,
169                                       jlong j_histogram_key,
170                                       jint j_min,
171                                       jint j_max,
172                                       jint j_bucket_count) {
173     DCHECK(j_histogram_name);
174     HistogramBase* histogram = HistogramFromKey(j_histogram_key);
175     int32_t min = static_cast<int32_t>(j_min);
176     int32_t max = static_cast<int32_t>(j_max);
177     int32_t bucket_count = static_cast<int32_t>(j_bucket_count);
178     if (histogram) {
179       CheckHistogramArgs(env, j_histogram_name, min, max, bucket_count,
180                          histogram);
181       return histogram;
182     }
183 
184     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
185     // This intentionally uses FactoryGet and not FactoryTimeGet. FactoryTimeGet
186     // is just a convenience for constructing the underlying Histogram with
187     // TimeDelta arguments.
188     histogram = Histogram::FactoryGet(histogram_name, min, max, bucket_count,
189                                       HistogramBase::kUmaTargetedHistogramFlag);
190     return histogram;
191   }
192 
193  private:
194   // Convert a jlong |histogram_key| from Java to a HistogramBase* via a cast.
195   // The Java side caches these in a map (see RecordHistogram.java), which is
196   // safe to do since C++ Histogram objects are never freed.
HistogramFromKey(jlong j_histogram_key)197   static HistogramBase* HistogramFromKey(jlong j_histogram_key) {
198     return reinterpret_cast<HistogramBase*>(j_histogram_key);
199   }
200 
201   DISALLOW_COPY_AND_ASSIGN(HistogramCache);
202 };
203 
204 LazyInstance<HistogramCache>::Leaky g_histograms;
205 
206 }  // namespace
207 
RecordBooleanHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jboolean j_sample)208 jlong RecordBooleanHistogram(JNIEnv* env,
209                              const JavaParamRef<jclass>& clazz,
210                              const JavaParamRef<jstring>& j_histogram_name,
211                              jlong j_histogram_key,
212                              jboolean j_sample) {
213   bool sample = static_cast<bool>(j_sample);
214   HistogramBase* histogram = g_histograms.Get().BooleanHistogram(
215       env, j_histogram_name, j_histogram_key);
216   histogram->AddBoolean(sample);
217   return reinterpret_cast<jlong>(histogram);
218 }
219 
RecordEnumeratedHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_sample,jint j_boundary)220 jlong RecordEnumeratedHistogram(JNIEnv* env,
221                                 const JavaParamRef<jclass>& clazz,
222                                 const JavaParamRef<jstring>& j_histogram_name,
223                                 jlong j_histogram_key,
224                                 jint j_sample,
225                                 jint j_boundary) {
226   int sample = static_cast<int>(j_sample);
227 
228   HistogramBase* histogram = g_histograms.Get().EnumeratedHistogram(
229       env, j_histogram_name, j_histogram_key, j_boundary);
230   histogram->Add(sample);
231   return reinterpret_cast<jlong>(histogram);
232 }
233 
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)234 jlong RecordCustomCountHistogram(JNIEnv* env,
235                                  const JavaParamRef<jclass>& clazz,
236                                  const JavaParamRef<jstring>& j_histogram_name,
237                                  jlong j_histogram_key,
238                                  jint j_sample,
239                                  jint j_min,
240                                  jint j_max,
241                                  jint j_num_buckets) {
242   int sample = static_cast<int>(j_sample);
243 
244   HistogramBase* histogram = g_histograms.Get().CustomCountHistogram(
245       env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
246   histogram->Add(sample);
247   return reinterpret_cast<jlong>(histogram);
248 }
249 
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)250 jlong RecordLinearCountHistogram(JNIEnv* env,
251                                  const JavaParamRef<jclass>& clazz,
252                                  const JavaParamRef<jstring>& j_histogram_name,
253                                  jlong j_histogram_key,
254                                  jint j_sample,
255                                  jint j_min,
256                                  jint j_max,
257                                  jint j_num_buckets) {
258   int sample = static_cast<int>(j_sample);
259 
260   HistogramBase* histogram = g_histograms.Get().LinearCountHistogram(
261       env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
262   histogram->Add(sample);
263   return reinterpret_cast<jlong>(histogram);
264 }
265 
RecordSparseHistogram(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & j_histogram_name,jlong j_histogram_key,jint j_sample)266 jlong RecordSparseHistogram(JNIEnv* env,
267                             const JavaParamRef<jclass>& clazz,
268                             const JavaParamRef<jstring>& j_histogram_name,
269                             jlong j_histogram_key,
270                             jint j_sample) {
271   int sample = static_cast<int>(j_sample);
272   HistogramBase* histogram = g_histograms.Get().SparseHistogram(
273       env, j_histogram_name, j_histogram_key);
274   histogram->Add(sample);
275   return reinterpret_cast<jlong>(histogram);
276 }
277 
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)278 jlong RecordCustomTimesHistogramMilliseconds(
279     JNIEnv* env,
280     const JavaParamRef<jclass>& clazz,
281     const JavaParamRef<jstring>& j_histogram_name,
282     jlong j_histogram_key,
283     jint j_duration,
284     jint j_min,
285     jint j_max,
286     jint j_num_buckets) {
287   HistogramBase* histogram = g_histograms.Get().CustomTimesHistogram(
288       env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
289   histogram->AddTime(
290       TimeDelta::FromMilliseconds(static_cast<int64_t>(j_duration)));
291   return reinterpret_cast<jlong>(histogram);
292 }
293 
Initialize(JNIEnv * env,const JavaParamRef<jclass> &)294 void Initialize(JNIEnv* env, const JavaParamRef<jclass>&) {
295   StatisticsRecorder::Initialize();
296 }
297 
298 // This backs a Java test util for testing histograms -
299 // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
300 // currently can't have test-specific native code packaged in test-specific Java
301 // targets - see http://crbug.com/415945.
GetHistogramValueCountForTesting(JNIEnv * env,const JavaParamRef<jclass> & clazz,const JavaParamRef<jstring> & histogram_name,jint sample)302 jint GetHistogramValueCountForTesting(
303     JNIEnv* env,
304     const JavaParamRef<jclass>& clazz,
305     const JavaParamRef<jstring>& histogram_name,
306     jint sample) {
307   HistogramBase* histogram = StatisticsRecorder::FindHistogram(
308       android::ConvertJavaStringToUTF8(env, histogram_name));
309   if (histogram == nullptr) {
310     // No samples have been recorded for this histogram (yet?).
311     return 0;
312   }
313 
314   std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
315   return samples->GetCount(static_cast<int>(sample));
316 }
317 
RegisterRecordHistogram(JNIEnv * env)318 bool RegisterRecordHistogram(JNIEnv* env) {
319   return RegisterNativesImpl(env);
320 }
321 
322 }  // namespace android
323 }  // namespace base
324