[metrics] Add total-gc-time and adjust other metrics
This change introduces the following:
1) Added total-gc-collection-time in ms as a counter so that, at anytime,
we can know how much time is spent in GC.
2) Added MetricsAverage to report average of the given data point.
3) Changed mutator-paue-time to average, which makes more sense. Also it
is renamed to world-stop-time.
4) Added averages of gc-throughputs.
5) Removed gc-meta-data-size metric as we don't capture it normally due
to the high cost of capturing this data.
Test: Observe pitot data
Bug: 191404436
Change-Id: I9da7f8c588ac4b42414beedb1b4004e0ac4b5fc2
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index 78a6387..c9110ee 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -36,22 +36,26 @@
#pragma clang diagnostic error "-Wconversion"
// See README.md in this directory for how to define metrics.
-#define ART_METRICS(METRIC) \
- METRIC(ClassLoadingTotalTime, MetricsCounter) \
- METRIC(ClassVerificationTotalTime, MetricsCounter) \
- METRIC(ClassVerificationCount, 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(JitMethodCompileCount, MetricsCounter) \
- METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
- METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
- METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000) \
- METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000) \
- METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
+#define ART_METRICS(METRIC) \
+ METRIC(ClassLoadingTotalTime, MetricsCounter) \
+ METRIC(ClassVerificationTotalTime, MetricsCounter) \
+ METRIC(ClassVerificationCount, MetricsCounter) \
+ METRIC(WorldStopTimeDuringGCAvg, MetricsAverage) \
+ METRIC(YoungGcCount, MetricsCounter) \
+ METRIC(FullGcCount, MetricsCounter) \
+ METRIC(TotalBytesAllocated, MetricsCounter) \
+ METRIC(TotalGcCollectionTime, MetricsCounter) \
+ METRIC(YoungGcThroughputAvg, MetricsAverage) \
+ METRIC(FullGcThroughputAvg, MetricsAverage) \
+ METRIC(YoungGcTracingThroughputAvg, MetricsAverage) \
+ METRIC(FullGcTracingThroughputAvg, MetricsAverage) \
+ METRIC(JitMethodCompileTime, MetricsHistogram, 15, 0, 1'000'000) \
+ METRIC(JitMethodCompileCount, MetricsCounter) \
+ METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
+ METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
+ METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000)
// A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
@@ -262,6 +266,8 @@
friend class MetricsHistogram;
template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
friend class MetricsAccumulator;
+ template <DatumId datum_id, typename T>
+ friend class MetricsAverage;
friend class ArtMetrics;
};
@@ -273,7 +279,7 @@
};
template <DatumId counter_type, typename T = uint64_t>
-class MetricsCounter final : public MetricsBase<T> {
+class MetricsCounter : public MetricsBase<T> {
public:
using value_t = T;
explicit constexpr MetricsCounter(uint64_t value = 0) : value_{value} {
@@ -294,15 +300,62 @@
value_ = 0;
}
- private:
value_t Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
+ private:
std::atomic<value_t> value_;
static_assert(std::atomic<value_t>::is_always_lock_free);
friend class ArtMetrics;
};
+template <DatumId datum_id, typename T = uint64_t>
+class MetricsAverage final : public MetricsCounter<datum_id, T> {
+ public:
+ using value_t = T;
+ using count_t = T;
+ explicit constexpr MetricsAverage(uint64_t value = 0, uint64_t count = 0) :
+ MetricsCounter<datum_id, value_t>(value), count_(count) {
+ // Ensure we do not have any unnecessary data in this class.
+ // Adding intptr_t to accommodate vtable, and rounding up to incorporate
+ // padding.
+ static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
+ == RoundUp(sizeof(intptr_t) + sizeof(value_t) + sizeof(count_t),
+ sizeof(uint64_t)));
+ }
+
+ // We use release memory-order here and then acquire in Report() to ensure
+ // that at least the non-racy reads/writes to this metric are consistent. This
+ // doesn't guarantee the atomicity of the change to both fields, but that
+ // may not be desired because:
+ // 1. The metric eventually becomes consistent.
+ // 2. For sufficiently large count_, a few data points which are off shouldn't
+ // make a huge difference to the reporter.
+ void Add(value_t value) {
+ MetricsCounter<datum_id, value_t>::Add(value);
+ count_.fetch_add(1, std::memory_order::memory_order_release);
+ }
+
+ void Report(MetricsBackend* backend) const {
+ count_t count = count_.load(std::memory_order::memory_order_acquire);
+ backend->ReportCounter(datum_id,
+ // Avoid divide-by-0.
+ count != 0 ? MetricsCounter<datum_id, value_t>::Value() / count : 0);
+ }
+
+ protected:
+ void Reset() {
+ count_ = 0;
+ MetricsCounter<datum_id, value_t>::Reset();
+ }
+
+ private:
+ std::atomic<count_t> count_;
+ static_assert(std::atomic<count_t>::is_always_lock_free);
+
+ friend class ArtMetrics;
+};
+
template <DatumId histogram_type_,
size_t num_buckets_,
int64_t minimum_value_,