native_uma_recorder.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // Copyright 2019 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. #include "base/android/callback_android.h"
  5. #include "base/android/jni_android.h"
  6. #include "base/android/jni_string.h"
  7. #include "base/base_jni_headers/NativeUmaRecorder_jni.h"
  8. #include "base/format_macros.h"
  9. #include "base/lazy_instance.h"
  10. #include "base/metrics/histogram.h"
  11. #include "base/metrics/histogram_base.h"
  12. #include "base/metrics/sparse_histogram.h"
  13. #include "base/metrics/statistics_recorder.h"
  14. #include "base/metrics/user_metrics.h"
  15. #include "base/strings/stringprintf.h"
  16. #include "base/synchronization/lock.h"
  17. #include "base/time/time.h"
  18. namespace base {
  19. namespace android {
  20. namespace {
  21. using HistogramsSnapshot =
  22. std::map<std::string, std::unique_ptr<HistogramSamples>>;
  23. // Simple thread-safe wrapper for caching histograms. This avoids
  24. // relatively expensive JNI string translation for each recording.
  25. class HistogramCache {
  26. public:
  27. HistogramCache() {}
  28. HistogramCache(const HistogramCache&) = delete;
  29. HistogramCache& operator=(const HistogramCache&) = delete;
  30. std::string HistogramConstructionParamsToString(HistogramBase* histogram) {
  31. std::string params_str = histogram->histogram_name();
  32. switch (histogram->GetHistogramType()) {
  33. case HISTOGRAM:
  34. case LINEAR_HISTOGRAM:
  35. case BOOLEAN_HISTOGRAM:
  36. case CUSTOM_HISTOGRAM: {
  37. Histogram* hist = static_cast<Histogram*>(histogram);
  38. params_str += StringPrintf("/%d/%d/%" PRIuS, hist->declared_min(),
  39. hist->declared_max(), hist->bucket_count());
  40. break;
  41. }
  42. case SPARSE_HISTOGRAM:
  43. case DUMMY_HISTOGRAM:
  44. break;
  45. }
  46. return params_str;
  47. }
  48. void CheckHistogramArgs(JNIEnv* env,
  49. jstring j_histogram_name,
  50. int32_t expected_min,
  51. int32_t expected_max,
  52. size_t expected_bucket_count,
  53. HistogramBase* histogram) {
  54. std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
  55. bool valid_arguments = Histogram::InspectConstructionArguments(
  56. histogram_name, &expected_min, &expected_max, &expected_bucket_count);
  57. DCHECK(valid_arguments);
  58. DCHECK(histogram->HasConstructionArguments(expected_min, expected_max,
  59. expected_bucket_count))
  60. << histogram_name << "/" << expected_min << "/" << expected_max << "/"
  61. << expected_bucket_count << " vs. "
  62. << HistogramConstructionParamsToString(histogram);
  63. }
  64. HistogramBase* BooleanHistogram(JNIEnv* env,
  65. jstring j_histogram_name,
  66. jlong j_histogram_hint) {
  67. DCHECK(j_histogram_name);
  68. HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
  69. if (histogram)
  70. return histogram;
  71. std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
  72. histogram = BooleanHistogram::FactoryGet(
  73. histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
  74. return histogram;
  75. }
  76. HistogramBase* ExponentialHistogram(JNIEnv* env,
  77. jstring j_histogram_name,
  78. jlong j_histogram_hint,
  79. jint j_min,
  80. jint j_max,
  81. jint j_num_buckets) {
  82. DCHECK(j_histogram_name);
  83. int32_t min = static_cast<int32_t>(j_min);
  84. int32_t max = static_cast<int32_t>(j_max);
  85. size_t num_buckets = static_cast<size_t>(j_num_buckets);
  86. HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
  87. if (histogram) {
  88. CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets,
  89. histogram);
  90. return histogram;
  91. }
  92. DCHECK_GE(min, 1) << "The min expected sample must be >= 1";
  93. std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
  94. histogram = Histogram::FactoryGet(histogram_name, min, max, num_buckets,
  95. HistogramBase::kUmaTargetedHistogramFlag);
  96. return histogram;
  97. }
  98. HistogramBase* LinearHistogram(JNIEnv* env,
  99. jstring j_histogram_name,
  100. jlong j_histogram_hint,
  101. jint j_min,
  102. jint j_max,
  103. jint j_num_buckets) {
  104. DCHECK(j_histogram_name);
  105. int32_t min = static_cast<int32_t>(j_min);
  106. int32_t max = static_cast<int32_t>(j_max);
  107. size_t num_buckets = static_cast<size_t>(j_num_buckets);
  108. HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
  109. if (histogram) {
  110. CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets,
  111. histogram);
  112. return histogram;
  113. }
  114. std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
  115. histogram =
  116. LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets,
  117. HistogramBase::kUmaTargetedHistogramFlag);
  118. return histogram;
  119. }
  120. HistogramBase* SparseHistogram(JNIEnv* env,
  121. jstring j_histogram_name,
  122. jlong j_histogram_hint) {
  123. DCHECK(j_histogram_name);
  124. HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
  125. if (histogram)
  126. return histogram;
  127. std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
  128. histogram = SparseHistogram::FactoryGet(
  129. histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
  130. return histogram;
  131. }
  132. private:
  133. // Convert a jlong |histogram_hint| from Java to a HistogramBase* via a cast.
  134. // The Java side caches these in a map (see NativeUmaRecorder.java), which is
  135. // safe to do since C++ Histogram objects are never freed.
  136. static HistogramBase* HistogramFromHint(jlong j_histogram_hint) {
  137. return reinterpret_cast<HistogramBase*>(j_histogram_hint);
  138. }
  139. };
  140. LazyInstance<HistogramCache>::Leaky g_histograms = LAZY_INSTANCE_INITIALIZER;
  141. struct ActionCallbackWrapper {
  142. base::ActionCallback action_callback;
  143. };
  144. static void OnActionRecorded(const JavaRef<jobject>& callback,
  145. const std::string& action,
  146. TimeTicks action_time) {
  147. RunStringCallbackAndroid(callback, action);
  148. }
  149. } // namespace
  150. jlong JNI_NativeUmaRecorder_RecordBooleanHistogram(
  151. JNIEnv* env,
  152. const JavaParamRef<jstring>& j_histogram_name,
  153. jlong j_histogram_hint,
  154. jboolean j_sample) {
  155. bool sample = static_cast<bool>(j_sample);
  156. HistogramBase* histogram = g_histograms.Get().BooleanHistogram(
  157. env, j_histogram_name, j_histogram_hint);
  158. histogram->AddBoolean(sample);
  159. return reinterpret_cast<jlong>(histogram);
  160. }
  161. jlong JNI_NativeUmaRecorder_RecordExponentialHistogram(
  162. JNIEnv* env,
  163. const JavaParamRef<jstring>& j_histogram_name,
  164. jlong j_histogram_hint,
  165. jint j_sample,
  166. jint j_min,
  167. jint j_max,
  168. jint j_num_buckets) {
  169. int sample = static_cast<int>(j_sample);
  170. HistogramBase* histogram = g_histograms.Get().ExponentialHistogram(
  171. env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets);
  172. histogram->Add(sample);
  173. return reinterpret_cast<jlong>(histogram);
  174. }
  175. jlong JNI_NativeUmaRecorder_RecordLinearHistogram(
  176. JNIEnv* env,
  177. const JavaParamRef<jstring>& j_histogram_name,
  178. jlong j_histogram_hint,
  179. jint j_sample,
  180. jint j_min,
  181. jint j_max,
  182. jint j_num_buckets) {
  183. int sample = static_cast<int>(j_sample);
  184. HistogramBase* histogram = g_histograms.Get().LinearHistogram(
  185. env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets);
  186. histogram->Add(sample);
  187. return reinterpret_cast<jlong>(histogram);
  188. }
  189. jlong JNI_NativeUmaRecorder_RecordSparseHistogram(
  190. JNIEnv* env,
  191. const JavaParamRef<jstring>& j_histogram_name,
  192. jlong j_histogram_hint,
  193. jint j_sample) {
  194. int sample = static_cast<int>(j_sample);
  195. HistogramBase* histogram = g_histograms.Get().SparseHistogram(
  196. env, j_histogram_name, j_histogram_hint);
  197. histogram->Add(sample);
  198. return reinterpret_cast<jlong>(histogram);
  199. }
  200. void JNI_NativeUmaRecorder_RecordUserAction(
  201. JNIEnv* env,
  202. const JavaParamRef<jstring>& j_user_action_name,
  203. jlong j_millis_since_event) {
  204. // Time values coming from Java need to be synchronized with TimeTick clock.
  205. RecordComputedActionSince(ConvertJavaStringToUTF8(env, j_user_action_name),
  206. Milliseconds(j_millis_since_event));
  207. }
  208. // This backs a Java test util for testing histograms -
  209. // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
  210. // currently can't have test-specific native code packaged in test-specific Java
  211. // targets - see http://crbug.com/415945.
  212. jint JNI_NativeUmaRecorder_GetHistogramValueCountForTesting(
  213. JNIEnv* env,
  214. const JavaParamRef<jstring>& histogram_name,
  215. jint sample,
  216. jlong snapshot_ptr) {
  217. std::string name = android::ConvertJavaStringToUTF8(env, histogram_name);
  218. HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
  219. if (histogram == nullptr) {
  220. // No samples have been recorded for this histogram (yet?).
  221. return 0;
  222. }
  223. int actual_count = histogram->SnapshotSamples()->GetCount(sample);
  224. if (snapshot_ptr) {
  225. auto* snapshot = reinterpret_cast<HistogramsSnapshot*>(snapshot_ptr);
  226. auto snapshot_data = snapshot->find(name);
  227. if (snapshot_data != snapshot->end())
  228. actual_count -= snapshot_data->second->GetCount(sample);
  229. }
  230. return actual_count;
  231. }
  232. jint JNI_NativeUmaRecorder_GetHistogramTotalCountForTesting(
  233. JNIEnv* env,
  234. const JavaParamRef<jstring>& histogram_name,
  235. jlong snapshot_ptr) {
  236. std::string name = android::ConvertJavaStringToUTF8(env, histogram_name);
  237. HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
  238. if (histogram == nullptr) {
  239. // No samples have been recorded for this histogram.
  240. return 0;
  241. }
  242. int actual_count = histogram->SnapshotSamples()->TotalCount();
  243. if (snapshot_ptr) {
  244. auto* snapshot = reinterpret_cast<HistogramsSnapshot*>(snapshot_ptr);
  245. auto snapshot_data = snapshot->find(name);
  246. if (snapshot_data != snapshot->end())
  247. actual_count -= snapshot_data->second->TotalCount();
  248. }
  249. return actual_count;
  250. }
  251. jlong JNI_NativeUmaRecorder_CreateHistogramSnapshotForTesting(JNIEnv* env) {
  252. HistogramsSnapshot* snapshot = new HistogramsSnapshot();
  253. for (const auto* const histogram : StatisticsRecorder::GetHistograms()) {
  254. (*snapshot)[histogram->histogram_name()] = histogram->SnapshotSamples();
  255. }
  256. return reinterpret_cast<intptr_t>(snapshot);
  257. }
  258. void JNI_NativeUmaRecorder_DestroyHistogramSnapshotForTesting(
  259. JNIEnv* env,
  260. jlong snapshot_ptr) {
  261. delete reinterpret_cast<HistogramsSnapshot*>(snapshot_ptr);
  262. }
  263. static jlong JNI_NativeUmaRecorder_AddActionCallbackForTesting(
  264. JNIEnv* env,
  265. const JavaParamRef<jobject>& callback) {
  266. // Create a wrapper for the ActionCallback, so it can life on the heap until
  267. // RemoveActionCallbackForTesting() is called.
  268. auto* wrapper = new ActionCallbackWrapper{base::BindRepeating(
  269. &OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
  270. base::AddActionCallback(wrapper->action_callback);
  271. return reinterpret_cast<intptr_t>(wrapper);
  272. }
  273. static void JNI_NativeUmaRecorder_RemoveActionCallbackForTesting(
  274. JNIEnv* env,
  275. jlong callback_id) {
  276. DCHECK(callback_id);
  277. auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
  278. base::RemoveActionCallback(wrapper->action_callback);
  279. delete wrapper;
  280. }
  281. } // namespace android
  282. } // namespace base