1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/android/callback_android.h"
6 #include "base/android/jni_android.h"
7 #include "base/android/jni_array.h"
8 #include "base/android/jni_string.h"
9 #include "base/base_jni/NativeUmaRecorder_jni.h"
10 #include "base/format_macros.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_base.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/metrics/statistics_recorder.h"
15 #include "base/metrics/user_metrics.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/time.h"
24 using HistogramsSnapshot =
25 std::map<std::string, std::unique_ptr<HistogramSamples>>;
27 std::string HistogramConstructionParamsToString(HistogramBase* histogram) {
28 std::string params_str = histogram->histogram_name();
29 switch (histogram->GetHistogramType()) {
31 case LINEAR_HISTOGRAM:
32 case BOOLEAN_HISTOGRAM:
33 case CUSTOM_HISTOGRAM: {
34 Histogram* hist = static_cast<Histogram*>(histogram);
35 params_str += StringPrintf("/%d/%d/%" PRIuS, hist->declared_min(),
36 hist->declared_max(), hist->bucket_count());
39 case SPARSE_HISTOGRAM:
46 // Convert a jlong |histogram_hint| from Java to a HistogramBase* via a cast.
47 // The Java side caches these in a map (see NativeUmaRecorder.java), which is
48 // safe to do since C++ Histogram objects are never freed.
49 static HistogramBase* HistogramFromHint(jlong j_histogram_hint) {
50 return reinterpret_cast<HistogramBase*>(j_histogram_hint);
53 void CheckHistogramArgs(JNIEnv* env,
54 jstring j_histogram_name,
57 size_t expected_bucket_count,
58 HistogramBase* histogram) {
59 std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
60 bool valid_arguments = Histogram::InspectConstructionArguments(
61 histogram_name, &expected_min, &expected_max, &expected_bucket_count);
62 DCHECK(valid_arguments);
63 DCHECK(histogram->HasConstructionArguments(expected_min, expected_max,
64 expected_bucket_count))
65 << histogram_name << "/" << expected_min << "/" << expected_max << "/"
66 << expected_bucket_count << " vs. "
67 << HistogramConstructionParamsToString(histogram);
70 HistogramBase* BooleanHistogram(JNIEnv* env,
71 jstring j_histogram_name,
72 jlong j_histogram_hint) {
73 DCHECK(j_histogram_name);
74 HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
78 std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
79 histogram = BooleanHistogram::FactoryGet(
80 histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
84 HistogramBase* ExponentialHistogram(JNIEnv* env,
85 jstring j_histogram_name,
86 jlong j_histogram_hint,
90 DCHECK(j_histogram_name);
91 int32_t min = static_cast<int32_t>(j_min);
92 int32_t max = static_cast<int32_t>(j_max);
93 size_t num_buckets = static_cast<size_t>(j_num_buckets);
94 HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
96 CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets, histogram);
100 DCHECK_GE(min, 1) << "The min expected sample must be >= 1";
102 std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
103 histogram = Histogram::FactoryGet(histogram_name, min, max, num_buckets,
104 HistogramBase::kUmaTargetedHistogramFlag);
108 HistogramBase* LinearHistogram(JNIEnv* env,
109 jstring j_histogram_name,
110 jlong j_histogram_hint,
113 jint j_num_buckets) {
114 DCHECK(j_histogram_name);
115 int32_t min = static_cast<int32_t>(j_min);
116 int32_t max = static_cast<int32_t>(j_max);
117 size_t num_buckets = static_cast<size_t>(j_num_buckets);
118 HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
120 CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets, histogram);
124 std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
126 LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets,
127 HistogramBase::kUmaTargetedHistogramFlag);
131 HistogramBase* SparseHistogram(JNIEnv* env,
132 jstring j_histogram_name,
133 jlong j_histogram_hint) {
134 DCHECK(j_histogram_name);
135 HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
139 std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
140 histogram = SparseHistogram::FactoryGet(
141 histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
145 struct ActionCallbackWrapper {
146 base::ActionCallback action_callback;
149 static void OnActionRecorded(const JavaRef<jobject>& callback,
150 const std::string& action,
151 TimeTicks action_time) {
152 RunStringCallbackAndroid(callback, action);
157 jlong JNI_NativeUmaRecorder_RecordBooleanHistogram(
159 const JavaParamRef<jstring>& j_histogram_name,
160 jlong j_histogram_hint,
162 bool sample = static_cast<bool>(j_sample);
163 HistogramBase* histogram =
164 BooleanHistogram(env, j_histogram_name, j_histogram_hint);
165 histogram->AddBoolean(sample);
166 return reinterpret_cast<jlong>(histogram);
169 jlong JNI_NativeUmaRecorder_RecordExponentialHistogram(
171 const JavaParamRef<jstring>& j_histogram_name,
172 jlong j_histogram_hint,
176 jint j_num_buckets) {
177 int sample = static_cast<int>(j_sample);
178 HistogramBase* histogram = ExponentialHistogram(
179 env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets);
180 histogram->Add(sample);
181 return reinterpret_cast<jlong>(histogram);
184 jlong JNI_NativeUmaRecorder_RecordLinearHistogram(
186 const JavaParamRef<jstring>& j_histogram_name,
187 jlong j_histogram_hint,
191 jint j_num_buckets) {
192 int sample = static_cast<int>(j_sample);
193 HistogramBase* histogram = LinearHistogram(
194 env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets);
195 histogram->Add(sample);
196 return reinterpret_cast<jlong>(histogram);
199 jlong JNI_NativeUmaRecorder_RecordSparseHistogram(
201 const JavaParamRef<jstring>& j_histogram_name,
202 jlong j_histogram_hint,
204 int sample = static_cast<int>(j_sample);
205 HistogramBase* histogram =
206 SparseHistogram(env, j_histogram_name, j_histogram_hint);
207 histogram->Add(sample);
208 return reinterpret_cast<jlong>(histogram);
211 void JNI_NativeUmaRecorder_RecordUserAction(
213 const JavaParamRef<jstring>& j_user_action_name,
214 jlong j_millis_since_event) {
215 // Time values coming from Java need to be synchronized with TimeTick clock.
216 RecordComputedActionSince(ConvertJavaStringToUTF8(env, j_user_action_name),
217 Milliseconds(j_millis_since_event));
220 // This backs a Java test util for testing histograms -
221 // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
222 // currently can't have test-specific native code packaged in test-specific Java
223 // targets - see http://crbug.com/415945.
224 jint JNI_NativeUmaRecorder_GetHistogramValueCountForTesting(
226 const JavaParamRef<jstring>& histogram_name,
228 jlong snapshot_ptr) {
229 std::string name = android::ConvertJavaStringToUTF8(env, histogram_name);
230 HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
231 if (histogram == nullptr) {
232 // No samples have been recorded for this histogram (yet?).
236 int actual_count = histogram->SnapshotSamples()->GetCount(sample);
238 auto* snapshot = reinterpret_cast<HistogramsSnapshot*>(snapshot_ptr);
239 auto snapshot_data = snapshot->find(name);
240 if (snapshot_data != snapshot->end())
241 actual_count -= snapshot_data->second->GetCount(sample);
247 jint JNI_NativeUmaRecorder_GetHistogramTotalCountForTesting(
249 const JavaParamRef<jstring>& histogram_name,
250 jlong snapshot_ptr) {
251 std::string name = android::ConvertJavaStringToUTF8(env, histogram_name);
252 HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
253 if (histogram == nullptr) {
254 // No samples have been recorded for this histogram.
258 int actual_count = histogram->SnapshotSamples()->TotalCount();
260 auto* snapshot = reinterpret_cast<HistogramsSnapshot*>(snapshot_ptr);
261 auto snapshot_data = snapshot->find(name);
262 if (snapshot_data != snapshot->end())
263 actual_count -= snapshot_data->second->TotalCount();
268 // Returns an array with 3 entries for each bucket, representing (min, max,
270 ScopedJavaLocalRef<jlongArray>
271 JNI_NativeUmaRecorder_GetHistogramSamplesForTesting(
273 const JavaParamRef<jstring>& histogram_name) {
274 std::string name = android::ConvertJavaStringToUTF8(env, histogram_name);
275 HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
276 std::vector<int64_t> buckets;
278 if (histogram == nullptr) {
279 // No samples have been recorded for this histogram.
280 return base::android::ToJavaLongArray(env, buckets);
283 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
284 for (auto sampleCountIterator = samples->Iterator();
285 !sampleCountIterator->Done(); sampleCountIterator->Next()) {
286 HistogramBase::Sample min;
288 HistogramBase::Count count;
289 sampleCountIterator->Get(&min, &max, &count);
290 buckets.push_back(min);
291 buckets.push_back(max);
292 buckets.push_back(count);
295 return base::android::ToJavaLongArray(env, buckets);
298 jlong JNI_NativeUmaRecorder_CreateHistogramSnapshotForTesting(JNIEnv* env) {
299 HistogramsSnapshot* snapshot = new HistogramsSnapshot();
300 for (const auto* const histogram : StatisticsRecorder::GetHistograms()) {
301 (*snapshot)[histogram->histogram_name()] = histogram->SnapshotSamples();
303 return reinterpret_cast<intptr_t>(snapshot);
306 void JNI_NativeUmaRecorder_DestroyHistogramSnapshotForTesting(
308 jlong snapshot_ptr) {
309 delete reinterpret_cast<HistogramsSnapshot*>(snapshot_ptr);
312 static jlong JNI_NativeUmaRecorder_AddActionCallbackForTesting(
314 const JavaParamRef<jobject>& callback) {
315 // Create a wrapper for the ActionCallback, so it can life on the heap until
316 // RemoveActionCallbackForTesting() is called.
317 auto* wrapper = new ActionCallbackWrapper{base::BindRepeating(
318 &OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
319 base::AddActionCallback(wrapper->action_callback);
320 return reinterpret_cast<intptr_t>(wrapper);
323 static void JNI_NativeUmaRecorder_RemoveActionCallbackForTesting(
327 auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
328 base::RemoveActionCallback(wrapper->action_callback);
332 } // namespace android