// 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 #include #include #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/lazy_instance.h" #include "base/macros.h" #include "base/metrics/histogram.h" #include "base/metrics/sparse_histogram.h" #include "base/metrics/statistics_recorder.h" #include "base/strings/stringprintf.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() {} std::string HistogramConstructionParamsToString(HistogramBase* histogram) { std::string params_str = histogram->histogram_name(); switch (histogram->GetHistogramType()) { case HISTOGRAM: case LINEAR_HISTOGRAM: case BOOLEAN_HISTOGRAM: case CUSTOM_HISTOGRAM: { Histogram* hist = static_cast(histogram); params_str += StringPrintf("/%d/%d/%d", hist->declared_min(), hist->declared_max(), hist->bucket_count()); break; } case SPARSE_HISTOGRAM: break; } return params_str; } void CheckHistogramArgs(JNIEnv* env, jstring j_histogram_name, int32_t expected_min, int32_t expected_max, uint32_t expected_bucket_count, HistogramBase* histogram) { std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); bool valid_arguments = Histogram::InspectConstructionArguments( histogram_name, &expected_min, &expected_max, &expected_bucket_count); DCHECK(valid_arguments); DCHECK(histogram->HasConstructionArguments(expected_min, expected_max, expected_bucket_count)) << histogram_name << "/" << expected_min << "/" << expected_max << "/" << expected_bucket_count << " vs. " << HistogramConstructionParamsToString(histogram); } HistogramBase* BooleanHistogram(JNIEnv* env, jstring j_histogram_name, jlong j_histogram_key) { DCHECK(j_histogram_name); HistogramBase* histogram = HistogramFromKey(j_histogram_key); if (histogram) return histogram; std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); histogram = BooleanHistogram::FactoryGet( histogram_name, HistogramBase::kUmaTargetedHistogramFlag); return histogram; } HistogramBase* EnumeratedHistogram(JNIEnv* env, jstring j_histogram_name, jlong j_histogram_key, jint j_boundary) { DCHECK(j_histogram_name); HistogramBase* histogram = HistogramFromKey(j_histogram_key); int32_t boundary = static_cast(j_boundary); if (histogram) { CheckHistogramArgs(env, j_histogram_name, 1, boundary, boundary + 1, histogram); return histogram; } std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); histogram = LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1, HistogramBase::kUmaTargetedHistogramFlag); return histogram; } HistogramBase* CustomCountHistogram(JNIEnv* env, jstring j_histogram_name, jlong j_histogram_key, jint j_min, jint j_max, jint j_num_buckets) { DCHECK(j_histogram_name); int32_t min = static_cast(j_min); int32_t max = static_cast(j_max); int32_t num_buckets = static_cast(j_num_buckets); HistogramBase* histogram = HistogramFromKey(j_histogram_key); if (histogram) { CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets, histogram); return histogram; } DCHECK_GE(min, 1) << "The min expected sample must be >= 1"; std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); histogram = Histogram::FactoryGet(histogram_name, min, max, num_buckets, HistogramBase::kUmaTargetedHistogramFlag); return histogram; } HistogramBase* LinearCountHistogram(JNIEnv* env, jstring j_histogram_name, jlong j_histogram_key, jint j_min, jint j_max, jint j_num_buckets) { DCHECK(j_histogram_name); int32_t min = static_cast(j_min); int32_t max = static_cast(j_max); int32_t num_buckets = static_cast(j_num_buckets); HistogramBase* histogram = HistogramFromKey(j_histogram_key); if (histogram) { CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets, histogram); return histogram; } std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); histogram = LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets, HistogramBase::kUmaTargetedHistogramFlag); return histogram; } HistogramBase* SparseHistogram(JNIEnv* env, jstring j_histogram_name, jlong j_histogram_key) { DCHECK(j_histogram_name); HistogramBase* histogram = HistogramFromKey(j_histogram_key); if (histogram) return histogram; std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); histogram = SparseHistogram::FactoryGet( histogram_name, HistogramBase::kUmaTargetedHistogramFlag); return histogram; } HistogramBase* CustomTimesHistogram(JNIEnv* env, jstring j_histogram_name, jlong j_histogram_key, jint j_min, jint j_max, jint j_bucket_count) { DCHECK(j_histogram_name); HistogramBase* histogram = HistogramFromKey(j_histogram_key); int32_t min = static_cast(j_min); int32_t max = static_cast(j_max); int32_t bucket_count = static_cast(j_bucket_count); if (histogram) { CheckHistogramArgs(env, j_histogram_name, min, max, bucket_count, histogram); 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 histogram; } private: // Convert a jlong |histogram_key| from Java to a HistogramBase* via a cast. // The Java side caches these in a map (see RecordHistogram.java), which is // safe to do since C++ Histogram objects are never freed. static HistogramBase* HistogramFromKey(jlong j_histogram_key) { return reinterpret_cast(j_histogram_key); } DISALLOW_COPY_AND_ASSIGN(HistogramCache); }; LazyInstance::Leaky g_histograms; } // namespace jlong RecordBooleanHistogram(JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& j_histogram_name, jlong j_histogram_key, jboolean j_sample) { bool sample = static_cast(j_sample); HistogramBase* histogram = g_histograms.Get().BooleanHistogram( env, j_histogram_name, j_histogram_key); histogram->AddBoolean(sample); return reinterpret_cast(histogram); } jlong RecordEnumeratedHistogram(JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& j_histogram_name, jlong j_histogram_key, jint j_sample, jint j_boundary) { int sample = static_cast(j_sample); HistogramBase* histogram = g_histograms.Get().EnumeratedHistogram( env, j_histogram_name, j_histogram_key, j_boundary); histogram->Add(sample); return reinterpret_cast(histogram); } jlong RecordCustomCountHistogram(JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& j_histogram_name, jlong j_histogram_key, jint j_sample, jint j_min, jint j_max, jint j_num_buckets) { int sample = static_cast(j_sample); HistogramBase* histogram = g_histograms.Get().CustomCountHistogram( env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets); histogram->Add(sample); return reinterpret_cast(histogram); } jlong RecordLinearCountHistogram(JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& j_histogram_name, jlong j_histogram_key, jint j_sample, jint j_min, jint j_max, jint j_num_buckets) { int sample = static_cast(j_sample); HistogramBase* histogram = g_histograms.Get().LinearCountHistogram( env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets); histogram->Add(sample); return reinterpret_cast(histogram); } jlong RecordSparseHistogram(JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& j_histogram_name, jlong j_histogram_key, jint j_sample) { int sample = static_cast(j_sample); HistogramBase* histogram = g_histograms.Get().SparseHistogram( env, j_histogram_name, j_histogram_key); histogram->Add(sample); return reinterpret_cast(histogram); } jlong RecordCustomTimesHistogramMilliseconds( JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& j_histogram_name, jlong j_histogram_key, jint j_duration, jint j_min, jint j_max, jint j_num_buckets) { HistogramBase* histogram = g_histograms.Get().CustomTimesHistogram( env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets); histogram->AddTime( TimeDelta::FromMilliseconds(static_cast(j_duration))); return reinterpret_cast(histogram); } void Initialize(JNIEnv* env, const JavaParamRef&) { 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, const JavaParamRef& clazz, const JavaParamRef& 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; } std::unique_ptr samples = histogram->SnapshotSamples(); return samples->GetCount(static_cast(sample)); } jint GetHistogramTotalCountForTesting( JNIEnv* env, const JavaParamRef& clazz, const JavaParamRef& histogram_name) { HistogramBase* histogram = StatisticsRecorder::FindHistogram( android::ConvertJavaStringToUTF8(env, histogram_name)); if (histogram == nullptr) { // No samples have been recorded for this histogram. return 0; } return histogram->SnapshotSamples()->TotalCount(); } } // namespace android } // namespace base