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