[metrics] Improve metrics definition API

This CL combines the ART_COUNTERS and ART_HISTOGRAMS lists into a
single ART_METRICS list. Additionally, this will make it possible to
add additional metrics types with less boilerplate.

Bug: 170149255
Test: gtests
Change-Id: I09e161de1ae574dfcc9ab95e8827b071f0a53892
diff --git a/libartbase/base/metrics/README.md b/libartbase/base/metrics/README.md
new file mode 100644
index 0000000..93c13b7
--- /dev/null
+++ b/libartbase/base/metrics/README.md
@@ -0,0 +1,29 @@
+# ART Metrics
+
+This directory contains most of ART's metrics framework. Some portions that
+rely on the runtime can be found in the `runtime/metrics` directory.
+
+## Declaring Metrics
+
+ART's internal metrics are listed in the `ART_METRICS` macro in `metrics.h`.
+Each metric has a `METRIC` entry which takes a name for the metric, a type
+ (such as counter or histogram), and any additional arguments that are needed.
+
+### Counters
+
+    METRIC(MyCounter, MetricsCounter)
+
+### Histograms
+
+    METRIC(MyHistogram, MetricsHistogram, num_buckets, minimum_value, maximum_value)
+
+The `num_buckets` parameter affects memory usage for the histogram and data
+usage for exported metrics. It is recommended to keep this below 16. The
+`minimum_value` and `maximum_value` parameters are needed because we need to
+know what range the fixed number of buckets cover. We could keep track of the
+observed ranges and try to rescale the buckets or allocate new buckets, but
+this would make incrementing them more expensive than just some index
+arithmetic and an add. Values outside the range get clamped to the nearest
+bucket (basically, the two buckets on either side are infinitely long). If we
+see those buckets being way taller than the others, it means we should consider
+expanding the range.
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index ab535ea..9e3e95a 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -35,35 +35,20 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic error "-Wconversion"
 
-// COUNTER(counter_name)
-#define ART_COUNTERS(COUNTER)    \
-  COUNTER(ClassLoadingTotalTime) \
-  COUNTER(ClassVerificationTotalTime) \
-  COUNTER(MutatorPauseTimeDuringGC) \
-  COUNTER(YoungGcCount) \
-  COUNTER(FullGcCount) \
-  COUNTER(TotalBytesAllocated) \
-  COUNTER(TotalGcMetaDataSize)
-
-// HISTOGRAM(counter_name, num_buckets, minimum_value, maximum_value)
-//
-// The num_buckets parameter affects memory usage for the histogram and data usage for exported
-// metrics. It is recommended to keep this below 16.
-//
-// The minimum_value and maximum_value parameters are needed because we need to know what range the
-// fixed number of buckets cover. We could keep track of the observed ranges and try to rescale the
-// buckets or allocate new buckets, but this would make incrementing them more expensive than just
-// some index arithmetic and an add.
-//
-// Values outside the range get clamped to the nearest bucket (basically, the two buckets on either
-// side are infinitely long). If we see those buckets being way taller than the others, it means we
-// should consider expanding the range.
-#define ART_HISTOGRAMS(HISTOGRAM) \
-  HISTOGRAM(JitMethodCompileTime, 15, 0, 1'000'000) \
-  HISTOGRAM(YoungGcCollectionTime, 15, 0, 60'000) \
-  HISTOGRAM(FullGcCollectionTime, 15, 0, 60'000) \
-  HISTOGRAM(YoungGcThroughput, 15, 0, 1'000) \
-  HISTOGRAM(FullGcThroughput, 15, 0, 1'000)
+// See README.md in this directory for how to define metrics.
+#define ART_METRICS(METRIC)                                        \
+  METRIC(ClassLoadingTotalTime, MetricsCounter)                    \
+  METRIC(ClassVerificationTotalTime, MetricsCounter)               \
+  METRIC(MutatorPauseTimeDuringGC, MetricsCounter)                 \
+  METRIC(YoungGcCount, MetricsCounter)                             \
+  METRIC(FullGcCount, MetricsCounter)                              \
+  METRIC(TotalBytesAllocated, MetricsCounter)                      \
+  METRIC(TotalGcMetaDataSize, MetricsCounter)                      \
+  METRIC(JitMethodCompileTime, MetricsHistogram, 15, 0, 1'000'000) \
+  METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000)   \
+  METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000)    \
+  METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 1'000)        \
+  METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 1'000)
 
 // A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
 // and ART_HISTOGRAMS. This means metrics.h and metrics.cc are very #define-heavy, which can be
@@ -84,13 +69,9 @@
  * An enumeration of all ART counters and histograms.
  */
 enum class DatumId {
-#define ART_COUNTER(name) k##name,
-  ART_COUNTERS(ART_COUNTER)
-#undef ART_COUNTER
-
-#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) k##name,
-  ART_HISTOGRAMS(ART_HISTOGRAM)
-#undef ART_HISTOGRAM
+#define METRIC(name, type, ...) k##name,
+  ART_METRICS(METRIC)
+#undef METRIC
 };
 
 // We log compilation reasons as part of the metadata we report. Since elsewhere compilation reasons
@@ -416,33 +397,18 @@
   void ReportAllMetrics(MetricsBackend* backend) const;
   void DumpForSigQuit(std::ostream& os) const;
 
-#define ART_COUNTER(name)                                       \
-  MetricsCounter<DatumId::k##name>* name() { return &name##_; } \
-  const MetricsCounter<DatumId::k##name>* name() const { return &name##_; }
-  ART_COUNTERS(ART_COUNTER)
-#undef ART_COUNTER
-
-#define ART_HISTOGRAM(name, num_buckets, low_value, high_value)                                \
-  MetricsHistogram<DatumId::k##name, num_buckets, low_value, high_value>* name() {             \
-    return &name##_;                                                                           \
-  }                                                                                            \
-  const MetricsHistogram<DatumId::k##name, num_buckets, low_value, high_value>* name() const { \
-    return &name##_;                                                                           \
-  }
-  ART_HISTOGRAMS(ART_HISTOGRAM)
-#undef ART_HISTOGRAM
+#define METRIC_ACCESSORS(name, Kind, ...)                                        \
+  Kind<DatumId::k##name, ##__VA_ARGS__>* name() { return &name##_; } \
+  const Kind<DatumId::k##name, ##__VA_ARGS__>* name() const { return &name##_; }
+  ART_METRICS(METRIC_ACCESSORS)
+#undef METRIC_ACCESSORS
 
  private:
   uint64_t beginning_timestamp_;
 
-#define ART_COUNTER(name) MetricsCounter<DatumId::k##name> name##_;
-  ART_COUNTERS(ART_COUNTER)
-#undef ART_COUNTER
-
-#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) \
-  MetricsHistogram<DatumId::k##name, num_buckets, low_value, high_value> name##_;
-  ART_HISTOGRAMS(ART_HISTOGRAM)
-#undef ART_HISTOGRAM
+#define METRIC(name, Kind, ...) Kind<DatumId::k##name, ##__VA_ARGS__> name##_;
+  ART_METRICS(METRIC)
+#undef METRIC
 };
 
 // Returns a human readable name for the given DatumId.
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index c29605e..0c2ec09 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -30,17 +30,11 @@
 
 std::string DatumName(DatumId datum) {
   switch (datum) {
-#define ART_COUNTER(name) \
+#define ART_METRIC(name, Kind, ...) \
   case DatumId::k##name:  \
     return #name;
-    ART_COUNTERS(ART_COUNTER)
-#undef ART_COUNTER
-
-#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) \
-  case DatumId::k##name:                                        \
-    return #name;
-    ART_HISTOGRAMS(ART_HISTOGRAM)
-#undef ART_HISTOGRAM
+    ART_METRICS(ART_METRIC)
+#undef ART_METRIC
 
     default:
       LOG(FATAL) << "Unknown datum id: " << static_cast<unsigned>(datum);
@@ -64,29 +58,19 @@
 }
 
 ArtMetrics::ArtMetrics() : beginning_timestamp_ {MilliTime()}
-#define ART_COUNTER(name) \
+#define ART_METRIC(name, Kind, ...) \
   , name##_ {}
-ART_COUNTERS(ART_COUNTER)
-#undef ART_COUNTER
-#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) \
-  , name##_ {}
-ART_HISTOGRAMS(ART_HISTOGRAM)
-#undef ART_HISTOGRAM
+ART_METRICS(ART_METRIC)
+#undef ART_METRIC
 {
 }
 
 void ArtMetrics::ReportAllMetrics(MetricsBackend* backend) const {
   backend->BeginReport(MilliTime() - beginning_timestamp_);
 
-// Dump counters
-#define ART_COUNTER(name) name()->Report(backend);
-  ART_COUNTERS(ART_COUNTER)
-#undef ART_COUNTERS
-
-// Dump histograms
-#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) name()->Report(backend);
-  ART_HISTOGRAMS(ART_HISTOGRAM)
-#undef ART_HISTOGRAM
+#define ART_METRIC(name, Kind, ...) name()->Report(backend);
+  ART_METRICS(ART_METRIC)
+#undef ART_METRICS
 
   backend->EndReport();
 }
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index 44a3b60..93701e6 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -212,18 +212,12 @@
 
   metrics.ReportAllMetrics(&backend);
 
-  // Make sure the resulting string lists all the counters.
+  // Make sure the resulting string lists all the metrics.
   const std::string result = backend.GetAndResetBuffer();
-#define COUNTER(name) \
+#define METRIC(name, type, ...) \
   EXPECT_NE(result.find(DatumName(DatumId::k##name)), std::string::npos);
-  ART_COUNTERS(COUNTER);
-#undef COUNTER
-
-  // Make sure the resulting string lists all the histograms.
-#define HISTOGRAM(name, num_buckets, minimum_value, maximum_value) \
-  EXPECT_NE(result.find(DatumName(DatumId::k##name)), std::string::npos);
-  ART_HISTOGRAMS(HISTOGRAM);
-#undef HISTOGRAM
+  ART_METRICS(METRIC);
+#undef METRIC
 }
 
 }  // namespace metrics