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