| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/android/record_histogram.h" | 
 |  | 
 | #include <map> | 
 |  | 
 | #include "base/android/jni_android.h" | 
 | #include "base/android/jni_string.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/metrics/histogram.h" | 
 | #include "base/metrics/sparse_histogram.h" | 
 | #include "base/metrics/statistics_recorder.h" | 
 | #include "base/synchronization/lock.h" | 
 | #include "base/time/time.h" | 
 | #include "jni/RecordHistogram_jni.h" | 
 |  | 
 | namespace base { | 
 | namespace android { | 
 | namespace { | 
 |  | 
 | // Simple thread-safe wrapper for caching histograms. This avoids | 
 | // relatively expensive JNI string translation for each recording. | 
 | class HistogramCache { | 
 |  public: | 
 |   HistogramCache() {} | 
 |  | 
 |   HistogramBase* BooleanHistogram(JNIEnv* env, | 
 |                                   jstring j_histogram_name, | 
 |                                   jint j_histogram_key) { | 
 |     DCHECK(j_histogram_name); | 
 |     DCHECK(j_histogram_key); | 
 |     HistogramBase* histogram = FindLocked(j_histogram_key); | 
 |     if (histogram) | 
 |       return histogram; | 
 |  | 
 |     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
 |     histogram = BooleanHistogram::FactoryGet( | 
 |         histogram_name, HistogramBase::kUmaTargetedHistogramFlag); | 
 |     return InsertLocked(j_histogram_key, histogram); | 
 |   } | 
 |  | 
 |   HistogramBase* EnumeratedHistogram(JNIEnv* env, | 
 |                                      jstring j_histogram_name, | 
 |                                      jint j_histogram_key, | 
 |                                      jint j_boundary) { | 
 |     DCHECK(j_histogram_name); | 
 |     DCHECK(j_histogram_key); | 
 |     HistogramBase* histogram = FindLocked(j_histogram_key); | 
 |     int boundary = static_cast<int>(j_boundary); | 
 |     if (histogram) { | 
 |       DCHECK(histogram->HasConstructionArguments(1, boundary, boundary + 1)); | 
 |       return histogram; | 
 |     } | 
 |  | 
 |     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
 |     histogram = | 
 |         LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1, | 
 |                                     HistogramBase::kUmaTargetedHistogramFlag); | 
 |     return InsertLocked(j_histogram_key, histogram); | 
 |   } | 
 |  | 
 |   HistogramBase* CustomCountHistogram(JNIEnv* env, | 
 |                                       jstring j_histogram_name, | 
 |                                       jint j_histogram_key, | 
 |                                       jint j_min, | 
 |                                       jint j_max, | 
 |                                       jint j_num_buckets) { | 
 |     DCHECK(j_histogram_name); | 
 |     DCHECK(j_histogram_key); | 
 |     int64 min = static_cast<int64>(j_min); | 
 |     int64 max = static_cast<int64>(j_max); | 
 |     int num_buckets = static_cast<int>(j_num_buckets); | 
 |     HistogramBase* histogram = FindLocked(j_histogram_key); | 
 |     if (histogram) { | 
 |       DCHECK(histogram->HasConstructionArguments(min, max, num_buckets)); | 
 |       return histogram; | 
 |     } | 
 |  | 
 |     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
 |     histogram = | 
 |         Histogram::FactoryGet(histogram_name, min, max, num_buckets, | 
 |                               HistogramBase::kUmaTargetedHistogramFlag); | 
 |     return InsertLocked(j_histogram_key, histogram); | 
 |   } | 
 |  | 
 |   HistogramBase* SparseHistogram(JNIEnv* env, | 
 |                                  jstring j_histogram_name, | 
 |                                  jint j_histogram_key) { | 
 |     DCHECK(j_histogram_name); | 
 |     DCHECK(j_histogram_key); | 
 |     HistogramBase* histogram = FindLocked(j_histogram_key); | 
 |     if (histogram) | 
 |       return histogram; | 
 |  | 
 |     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
 |     histogram = SparseHistogram::FactoryGet( | 
 |         histogram_name, HistogramBase::kUmaTargetedHistogramFlag); | 
 |     return InsertLocked(j_histogram_key, histogram); | 
 |   } | 
 |  | 
 |   HistogramBase* CustomTimesHistogram(JNIEnv* env, | 
 |                                       jstring j_histogram_name, | 
 |                                       jint j_histogram_key, | 
 |                                       jlong j_min, | 
 |                                       jlong j_max, | 
 |                                       jint j_bucket_count) { | 
 |     DCHECK(j_histogram_name); | 
 |     DCHECK(j_histogram_key); | 
 |     HistogramBase* histogram = FindLocked(j_histogram_key); | 
 |     int64 min = static_cast<int64>(j_min); | 
 |     int64 max = static_cast<int64>(j_max); | 
 |     int bucket_count = static_cast<int>(j_bucket_count); | 
 |     if (histogram) { | 
 |       DCHECK(histogram->HasConstructionArguments(min, max, bucket_count)); | 
 |       return histogram; | 
 |     } | 
 |  | 
 |     std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
 |     // This intentionally uses FactoryGet and not FactoryTimeGet. FactoryTimeGet | 
 |     // is just a convenience for constructing the underlying Histogram with | 
 |     // TimeDelta arguments. | 
 |     histogram = Histogram::FactoryGet(histogram_name, min, max, bucket_count, | 
 |                                       HistogramBase::kUmaTargetedHistogramFlag); | 
 |     return InsertLocked(j_histogram_key, histogram); | 
 |   } | 
 |  | 
 |  private: | 
 |   HistogramBase* FindLocked(jint j_histogram_key) { | 
 |     base::AutoLock locked(lock_); | 
 |     auto histogram_it = histograms_.find(j_histogram_key); | 
 |     return histogram_it != histograms_.end() ? histogram_it->second : nullptr; | 
 |   } | 
 |  | 
 |   HistogramBase* InsertLocked(jint j_histogram_key, HistogramBase* histogram) { | 
 |     base::AutoLock locked(lock_); | 
 |     histograms_.insert(std::make_pair(j_histogram_key, histogram)); | 
 |     return histogram; | 
 |   } | 
 |  | 
 |   base::Lock lock_; | 
 |   std::map<jint, HistogramBase*> histograms_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(HistogramCache); | 
 | }; | 
 |  | 
 | base::LazyInstance<HistogramCache>::Leaky g_histograms; | 
 |  | 
 | }  // namespace | 
 |  | 
 | void RecordBooleanHistogram(JNIEnv* env, | 
 |                             jclass clazz, | 
 |                             jstring j_histogram_name, | 
 |                             jint j_histogram_key, | 
 |                             jboolean j_sample) { | 
 |   bool sample = static_cast<bool>(j_sample); | 
 |   g_histograms.Get() | 
 |       .BooleanHistogram(env, j_histogram_name, j_histogram_key) | 
 |       ->AddBoolean(sample); | 
 | } | 
 |  | 
 | void RecordEnumeratedHistogram(JNIEnv* env, | 
 |                                jclass clazz, | 
 |                                jstring j_histogram_name, | 
 |                                jint j_histogram_key, | 
 |                                jint j_sample, | 
 |                                jint j_boundary) { | 
 |   int sample = static_cast<int>(j_sample); | 
 |  | 
 |   g_histograms.Get() | 
 |       .EnumeratedHistogram(env, j_histogram_name, j_histogram_key, j_boundary) | 
 |       ->Add(sample); | 
 | } | 
 |  | 
 | void RecordCustomCountHistogram(JNIEnv* env, | 
 |                                 jclass clazz, | 
 |                                 jstring j_histogram_name, | 
 |                                 jint j_histogram_key, | 
 |                                 jint j_sample, | 
 |                                 jint j_min, | 
 |                                 jint j_max, | 
 |                                 jint j_num_buckets) { | 
 |   int sample = static_cast<int>(j_sample); | 
 |  | 
 |   g_histograms.Get() | 
 |       .CustomCountHistogram(env, j_histogram_name, j_histogram_key, j_min, | 
 |                             j_max, j_num_buckets) | 
 |       ->Add(sample); | 
 | } | 
 |  | 
 | void RecordSparseHistogram(JNIEnv* env, | 
 |                                  jclass clazz, | 
 |                                  jstring j_histogram_name, | 
 |                                  jint j_histogram_key, | 
 |                                  jint j_sample) { | 
 |     int sample = static_cast<int>(j_sample); | 
 |     g_histograms.Get() | 
 |         .SparseHistogram(env, j_histogram_name, j_histogram_key) | 
 |         ->Add(sample); | 
 | } | 
 |  | 
 | void RecordCustomTimesHistogramMilliseconds(JNIEnv* env, | 
 |                                             jclass clazz, | 
 |                                             jstring j_histogram_name, | 
 |                                             jint j_histogram_key, | 
 |                                             jlong j_duration, | 
 |                                             jlong j_min, | 
 |                                             jlong j_max, | 
 |                                             jint j_num_buckets) { | 
 |   g_histograms.Get() | 
 |       .CustomTimesHistogram(env, j_histogram_name, j_histogram_key, j_min, | 
 |                             j_max, j_num_buckets) | 
 |       ->AddTime(TimeDelta::FromMilliseconds(static_cast<int64>(j_duration))); | 
 | } | 
 |  | 
 | void Initialize(JNIEnv* env, jclass) { | 
 |   StatisticsRecorder::Initialize(); | 
 | } | 
 |  | 
 | // This backs a Java test util for testing histograms - | 
 | // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we | 
 | // currently can't have test-specific native code packaged in test-specific Java | 
 | // targets - see http://crbug.com/415945. | 
 | jint GetHistogramValueCountForTesting(JNIEnv* env, | 
 |                                       jclass clazz, | 
 |                                       jstring histogram_name, | 
 |                                       jint sample) { | 
 |   HistogramBase* histogram = StatisticsRecorder::FindHistogram( | 
 |       android::ConvertJavaStringToUTF8(env, histogram_name)); | 
 |   if (histogram == nullptr) { | 
 |     // No samples have been recorded for this histogram (yet?). | 
 |     return 0; | 
 |   } | 
 |  | 
 |   scoped_ptr<HistogramSamples> samples = histogram->SnapshotSamples(); | 
 |   return samples->GetCount(static_cast<int>(sample)); | 
 | } | 
 |  | 
 | bool RegisterRecordHistogram(JNIEnv* env) { | 
 |   return RegisterNativesImpl(env); | 
 | } | 
 |  | 
 | }  // namespace android | 
 | }  // namespace base |