Home | History | Annotate | Download | only in android
      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