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