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: 32 HistogramCache() {} 33 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 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 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 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 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 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 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 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. 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 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 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 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 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 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 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 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. 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 318 bool RegisterRecordHistogram(JNIEnv* env) { 319 return RegisterNativesImpl(env); 320 } 321 322 } // namespace android 323 } // namespace base 324