summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/jit/jit_compiler.cc2
-rw-r--r--libartbase/base/metrics/metrics.h150
-rw-r--r--libartbase/base/metrics/metrics_common.cc34
-rw-r--r--libartbase/base/metrics/metrics_test.cc94
-rw-r--r--libartbase/base/metrics/metrics_test.h4
-rw-r--r--libartbase/base/utils.h13
-rw-r--r--runtime/class_linker.cc1
-rw-r--r--runtime/gc/collector/concurrent_copying.cc8
-rw-r--r--runtime/gc/collector/garbage_collector.cc11
-rw-r--r--runtime/gc/collector/garbage_collector.h4
-rw-r--r--runtime/gc/heap-inl.h1
-rw-r--r--runtime/metrics/reporter.cc10
-rw-r--r--runtime/metrics/reporter.h2
-rw-r--r--runtime/metrics/reporter_test.cc9
-rw-r--r--runtime/metrics/statsd.cc105
-rw-r--r--runtime/verifier/class_verifier.cc3
-rw-r--r--test/2232-write-metrics-to-log/expected-stderr.txt2
-rw-r--r--test/2232-write-metrics-to-log/run.py2
18 files changed, 360 insertions, 95 deletions
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index d2f5aa6799..e67236769e 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -203,6 +203,8 @@ bool JitCompiler::CompileMethod(
VLOG(jit) << "Compilation of " << method->PrettyMethod() << " took "
<< PrettyDuration(UsToNs(duration_us));
runtime->GetMetrics()->JitMethodCompileCount()->AddOne();
+ runtime->GetMetrics()->JitMethodCompileTotalTimeDelta()->Add(duration_us);
+ runtime->GetMetrics()->JitMethodCompileCountDelta()->AddOne();
}
// Trim maps to reduce memory usage.
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index 6d856da410..8432be50a4 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -36,36 +36,62 @@
#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(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(JitMethodCompileTotalTime, MetricsCounter) \
- 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) \
- METRIC(GcWorldStopTime, MetricsCounter) \
- METRIC(GcWorldStopCount, MetricsCounter) \
- METRIC(YoungGcScannedBytes, MetricsCounter) \
- METRIC(YoungGcFreedBytes, MetricsCounter) \
- METRIC(YoungGcDuration, MetricsCounter) \
- METRIC(FullGcScannedBytes, MetricsCounter) \
- METRIC(FullGcFreedBytes, MetricsCounter) \
+
+// Metrics reported as Event Metrics.
+#define ART_EVENT_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(JitMethodCompileTotalTime, MetricsCounter) \
+ 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) \
+ METRIC(GcWorldStopTime, MetricsCounter) \
+ METRIC(GcWorldStopCount, MetricsCounter) \
+ METRIC(YoungGcScannedBytes, MetricsCounter) \
+ METRIC(YoungGcFreedBytes, MetricsCounter) \
+ METRIC(YoungGcDuration, MetricsCounter) \
+ METRIC(FullGcScannedBytes, MetricsCounter) \
+ METRIC(FullGcFreedBytes, MetricsCounter) \
METRIC(FullGcDuration, MetricsCounter)
+// Increasing counter metrics, reported as Value Metrics in delta increments.
+#define ART_VALUE_METRICS(METRIC) \
+ METRIC(GcWorldStopTimeDelta, MetricsDeltaCounter) \
+ METRIC(GcWorldStopCountDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcScannedBytesDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcFreedBytesDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcDurationDelta, MetricsDeltaCounter) \
+ METRIC(FullGcScannedBytesDelta, MetricsDeltaCounter) \
+ METRIC(FullGcFreedBytesDelta, MetricsDeltaCounter) \
+ METRIC(FullGcDurationDelta, MetricsDeltaCounter) \
+ METRIC(JitMethodCompileTotalTimeDelta, MetricsDeltaCounter) \
+ METRIC(JitMethodCompileCountDelta, MetricsDeltaCounter) \
+ METRIC(ClassVerificationTotalTimeDelta, MetricsDeltaCounter) \
+ METRIC(ClassVerificationCountDelta, MetricsDeltaCounter) \
+ METRIC(ClassLoadingTotalTimeDelta, MetricsDeltaCounter) \
+ METRIC(TotalBytesAllocatedDelta, MetricsDeltaCounter) \
+ METRIC(TotalGcCollectionTimeDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcCountDelta, MetricsDeltaCounter) \
+ METRIC(FullGcCountDelta, MetricsDeltaCounter)
+
+#define ART_METRICS(METRIC) \
+ ART_EVENT_METRICS(METRIC) \
+ ART_VALUE_METRICS(METRIC)
+
// 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
// challenging to read. The alternative was to require a lot of boilerplate code for each new metric
@@ -243,6 +269,8 @@ class MetricsBackend {
template <DatumId counter_type, typename T>
friend class MetricsCounter;
+ template <DatumId counter_type, typename T>
+ friend class MetricsDeltaCounter;
template <DatumId histogram_type, size_t num_buckets, int64_t low_value, int64_t high_value>
friend class MetricsHistogram;
template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
@@ -274,13 +302,14 @@ class MetricsCounter : public MetricsBase<T> {
void AddOne() { Add(1u); }
void Add(value_t value) { value_.fetch_add(value, std::memory_order::memory_order_relaxed); }
- void Report(MetricsBackend* backend) const { backend->ReportCounter(counter_type, Value()); }
-
- protected:
- void Reset() {
- value_ = 0;
+ void Report(const std::vector<MetricsBackend*>& backends) const {
+ for (MetricsBackend* backend : backends) {
+ backend->ReportCounter(counter_type, Value());
+ }
}
+ protected:
+ void Reset() { value_ = 0; }
value_t Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
private:
@@ -317,11 +346,14 @@ class MetricsAverage final : public MetricsCounter<datum_id, T> {
count_.fetch_add(1, std::memory_order::memory_order_release);
}
- void Report(MetricsBackend* backend) const {
+ void Report(const std::vector<MetricsBackend*>& backends) const {
+ count_t value = MetricsCounter<datum_id, value_t>::Value();
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);
+ // Avoid divide-by-0.
+ count_t average_value = count != 0 ? value / count : 0;
+ for (MetricsBackend* backend : backends) {
+ backend->ReportCounter(datum_id, average_value);
+ }
}
protected:
@@ -337,6 +369,40 @@ class MetricsAverage final : public MetricsCounter<datum_id, T> {
friend class ArtMetrics;
};
+template <DatumId datum_id, typename T = uint64_t>
+class MetricsDeltaCounter : public MetricsBase<T> {
+ public:
+ using value_t = T;
+
+ explicit constexpr MetricsDeltaCounter(uint64_t value = 0) : value_{value} {
+ // 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(uint64_t)));
+ }
+
+ void Add(value_t value) override {
+ value_.fetch_add(value, std::memory_order::memory_order_relaxed);
+ }
+ void AddOne() { Add(1u); }
+
+ void ReportAndReset(const std::vector<MetricsBackend*>& backends) {
+ value_t value = value_.exchange(0, std::memory_order::memory_order_relaxed);
+ for (MetricsBackend* backend : backends) {
+ backend->ReportCounter(datum_id, value);
+ }
+ }
+
+ void Reset() { value_ = 0; }
+
+ private:
+ std::atomic<value_t> value_;
+ static_assert(std::atomic<value_t>::is_always_lock_free);
+
+ friend class ArtMetrics;
+};
+
template <DatumId histogram_type_,
size_t num_buckets_,
int64_t minimum_value_,
@@ -361,8 +427,10 @@ class MetricsHistogram final : public MetricsBase<int64_t> {
buckets_[i].fetch_add(1u, std::memory_order::memory_order_relaxed);
}
- void Report(MetricsBackend* backend) const {
- backend->ReportHistogram(histogram_type_, minimum_value_, maximum_value_, GetBuckets());
+ void Report(const std::vector<MetricsBackend*>& backends) const {
+ for (MetricsBackend* backend : backends) {
+ backend->ReportHistogram(histogram_type_, minimum_value_, maximum_value_, GetBuckets());
+ }
}
protected:
@@ -639,8 +707,8 @@ class ArtMetrics {
public:
ArtMetrics();
- void ReportAllMetrics(MetricsBackend* backend) const;
- void DumpForSigQuit(std::ostream& os) const;
+ void ReportAllMetricsAndResetValueMetrics(const std::vector<MetricsBackend*>& backends);
+ void DumpForSigQuit(std::ostream& os);
// Resets all metrics to their initial value. This is intended to be used after forking from the
// zygote so we don't attribute parent values to the child process.
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index 025f5eb79e..2732088e85 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -65,32 +65,40 @@ ART_METRICS(ART_METRIC)
{
}
-void ArtMetrics::ReportAllMetrics(MetricsBackend* backend) const {
- backend->BeginReport(MilliTime() - beginning_timestamp_);
+void ArtMetrics::ReportAllMetricsAndResetValueMetrics(
+ const std::vector<MetricsBackend*>& backends) {
+ for (auto& backend : backends) {
+ backend->BeginReport(MilliTime() - beginning_timestamp_);
+ }
-#define ART_METRIC(name, Kind, ...) name()->Report(backend);
- ART_METRICS(ART_METRIC)
-#undef ART_METRIC
+#define REPORT_METRIC(name, Kind, ...) name()->Report(backends);
+ ART_EVENT_METRICS(REPORT_METRIC)
+#undef REPORT_METRIC
+
+#define REPORT_METRIC(name, Kind, ...) name()->ReportAndReset(backends);
+ ART_VALUE_METRICS(REPORT_METRIC)
+#undef REPORT_METRIC
- backend->EndReport();
+ for (auto& backend : backends) {
+ backend->EndReport();
+ }
}
-void ArtMetrics::DumpForSigQuit(std::ostream& os) const {
+void ArtMetrics::DumpForSigQuit(std::ostream& os) {
StringBackend backend(std::make_unique<TextFormatter>());
- ReportAllMetrics(&backend);
+ ReportAllMetricsAndResetValueMetrics({&backend});
os << backend.GetAndResetBuffer();
}
void ArtMetrics::Reset() {
beginning_timestamp_ = MilliTime();
-#define ART_METRIC(name, kind, ...) name##_.Reset();
- ART_METRICS(ART_METRIC);
-#undef ART_METRIC
+#define RESET_METRIC(name, ...) name##_.Reset();
+ ART_METRICS(RESET_METRIC)
+#undef RESET_METRIC
}
StringBackend::StringBackend(std::unique_ptr<MetricsFormatter> formatter)
- : formatter_(std::move(formatter))
-{}
+ : formatter_(std::move(formatter)) {}
std::string StringBackend::GetAndResetBuffer() {
return formatter_->GetAndResetBuffer();
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index fd7124be8b..6c2e0451f4 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -16,6 +16,7 @@
#include "metrics.h"
+#include "base/macros.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "metrics_test.h"
@@ -232,7 +233,7 @@ TEST_F(MetricsTest, ArtMetricsReport) {
bool found_histogram_{false};
} backend;
- metrics.ReportAllMetrics(&backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&backend});
}
TEST_F(MetricsTest, HistogramTimer) {
@@ -251,7 +252,7 @@ TEST_F(MetricsTest, StreamBackendDumpAllMetrics) {
ArtMetrics metrics;
StringBackend backend(std::make_unique<TextFormatter>());
- metrics.ReportAllMetrics(&backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&backend});
// Make sure the resulting string lists all the metrics.
const std::string result = backend.GetAndResetBuffer();
@@ -271,11 +272,14 @@ TEST_F(MetricsTest, ResetMetrics) {
class NonZeroBackend : public TestBackendBase {
public:
- void ReportCounter(DatumId, uint64_t value) override {
+ void ReportCounter(DatumId counter_type [[gnu::unused]], uint64_t value) override {
EXPECT_NE(value, 0u);
}
- void ReportHistogram(DatumId, int64_t, int64_t, const std::vector<uint32_t>& buckets) override {
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
bool nonzero = false;
for (const auto value : buckets) {
nonzero |= (value != 0u);
@@ -285,25 +289,99 @@ TEST_F(MetricsTest, ResetMetrics) {
} non_zero_backend;
// Make sure the metrics all have a nonzero value.
- metrics.ReportAllMetrics(&non_zero_backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&non_zero_backend});
// Reset the metrics and make sure they are all zero again
metrics.Reset();
class ZeroBackend : public TestBackendBase {
public:
- void ReportCounter(DatumId, uint64_t value) override {
+ void ReportCounter(DatumId counter_type [[gnu::unused]], uint64_t value) override {
EXPECT_EQ(value, 0u);
}
- void ReportHistogram(DatumId, int64_t, int64_t, const std::vector<uint32_t>& buckets) override {
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
for (const auto value : buckets) {
EXPECT_EQ(value, 0u);
}
}
} zero_backend;
- metrics.ReportAllMetrics(&zero_backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&zero_backend});
+}
+
+TEST_F(MetricsTest, KeepEventMetricsResetValueMetricsAfterReporting) {
+ ArtMetrics metrics;
+
+ // Add something to each of the metrics.
+#define METRIC(name, type, ...) metrics.name()->Add(42);
+ ART_METRICS(METRIC)
+#undef METRIC
+
+ class FirstBackend : public TestBackendBase {
+ public:
+ void ReportCounter(DatumId counter_type [[gnu::unused]], uint64_t value) override {
+ EXPECT_NE(value, 0u);
+ }
+
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
+ bool nonzero = false;
+ for (const auto value : buckets) {
+ nonzero |= (value != 0u);
+ }
+ EXPECT_TRUE(nonzero);
+ }
+ } first_backend;
+
+ // Make sure the metrics all have a nonzero value, and they are not reset between backends.
+ metrics.ReportAllMetricsAndResetValueMetrics({&first_backend, &first_backend});
+
+ // After reporting, the Value Metrics should have been reset.
+ class SecondBackend : public TestBackendBase {
+ public:
+ void ReportCounter(DatumId datum_id, uint64_t value) override {
+ switch (datum_id) {
+ // Value metrics - expected to have been reset
+#define CHECK_METRIC(name, ...) case DatumId::k##name:
+ ART_VALUE_METRICS(CHECK_METRIC)
+#undef CHECK_METRIC
+ EXPECT_EQ(value, 0u);
+ return;
+
+ // Event metrics - expected to have retained their previous value
+#define CHECK_METRIC(name, ...) case DatumId::k##name:
+ ART_EVENT_METRICS(CHECK_METRIC)
+#undef CHECK_METRIC
+ EXPECT_NE(value, 0u);
+ return;
+
+ default:
+ // unknown metric - it should not be possible to reach this path
+ FAIL();
+ UNREACHABLE();
+ }
+ }
+
+ // All histograms are event metrics.
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
+ bool nonzero = false;
+ for (const auto value : buckets) {
+ nonzero |= (value != 0u);
+ }
+ EXPECT_TRUE(nonzero);
+ }
+ } second_backend;
+
+ metrics.ReportAllMetricsAndResetValueMetrics({&second_backend});
}
TEST(TextFormatterTest, ReportMetrics_WithBuckets) {
diff --git a/libartbase/base/metrics/metrics_test.h b/libartbase/base/metrics/metrics_test.h
index 3e8b42a5cc..07b4e9dfcd 100644
--- a/libartbase/base/metrics/metrics_test.h
+++ b/libartbase/base/metrics/metrics_test.h
@@ -58,7 +58,7 @@ uint64_t CounterValue(const MetricType& counter) {
uint64_t* counter_value_;
} backend{&counter_value};
- counter.Report(&backend);
+ counter.Report({&backend});
return counter_value;
}
@@ -75,7 +75,7 @@ std::vector<uint32_t> GetBuckets(
std::vector<uint32_t>* buckets_;
} backend{&buckets};
- histogram.Report(&backend);
+ histogram.Report({&backend});
return buckets;
}
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index f311f09975..b32dcec665 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -186,6 +186,19 @@ bool IsAddressKnownBackedByFileOrShared(const void* addr);
// Returns the number of threads running.
int GetTaskCount();
+// Returns a vector holding the raw pointer targets of the elements of "unique_pointers", a vector
+// of smart pointers. This does not affect the target objects e.g. by incrementing ref-counts; it's
+// a raw pointer read.
+template <typename T>
+static std::vector<T*> ToRawPointers(const std::vector<std::unique_ptr<T>>& unique_pointers) {
+ std::vector<T*> raw_pointers;
+ raw_pointers.reserve(unique_pointers.size());
+ for (const std::unique_ptr<T>& p : unique_pointers) {
+ raw_pointers.push_back(p.get());
+ }
+ return raw_pointers;
+}
+
} // namespace art
#endif // ART_LIBARTBASE_BASE_UTILS_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b98f704af3..f5a4ce689c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3234,6 +3234,7 @@ ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,
ScopedDefiningClass sdc(self);
StackHandleScope<3> hs(self);
metrics::AutoTimer timer{GetMetrics()->ClassLoadingTotalTime()};
+ metrics::AutoTimer timeDelta{GetMetrics()->ClassLoadingTotalTimeDelta()};
auto klass = hs.NewHandle<mirror::Class>(nullptr);
// Load the class from the dex file.
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index d540873e77..5118523a95 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -160,23 +160,31 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap,
if (young_gen_) {
gc_time_histogram_ = metrics->YoungGcCollectionTime();
metrics_gc_count_ = metrics->YoungGcCount();
+ metrics_gc_count_delta_ = metrics->YoungGcCountDelta();
gc_throughput_histogram_ = metrics->YoungGcThroughput();
gc_tracing_throughput_hist_ = metrics->YoungGcTracingThroughput();
gc_throughput_avg_ = metrics->YoungGcThroughputAvg();
gc_tracing_throughput_avg_ = metrics->YoungGcTracingThroughputAvg();
gc_scanned_bytes_ = metrics->YoungGcScannedBytes();
+ gc_scanned_bytes_delta_ = metrics->YoungGcScannedBytesDelta();
gc_freed_bytes_ = metrics->YoungGcFreedBytes();
+ gc_freed_bytes_delta_ = metrics->YoungGcFreedBytesDelta();
gc_duration_ = metrics->YoungGcDuration();
+ gc_duration_delta_ = metrics->YoungGcDurationDelta();
} else {
gc_time_histogram_ = metrics->FullGcCollectionTime();
metrics_gc_count_ = metrics->FullGcCount();
+ metrics_gc_count_delta_ = metrics->FullGcCountDelta();
gc_throughput_histogram_ = metrics->FullGcThroughput();
gc_tracing_throughput_hist_ = metrics->FullGcTracingThroughput();
gc_throughput_avg_ = metrics->FullGcThroughputAvg();
gc_tracing_throughput_avg_ = metrics->FullGcTracingThroughputAvg();
gc_scanned_bytes_ = metrics->FullGcScannedBytes();
+ gc_scanned_bytes_delta_ = metrics->FullGcScannedBytesDelta();
gc_freed_bytes_ = metrics->FullGcFreedBytes();
+ gc_freed_bytes_delta_ = metrics->FullGcFreedBytesDelta();
gc_duration_ = metrics->FullGcDuration();
+ gc_duration_delta_ = metrics->FullGcDurationDelta();
}
}
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 4efe48c318..03a432dbf4 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -72,13 +72,17 @@ GarbageCollector::GarbageCollector(Heap* heap, const std::string& name)
freed_bytes_histogram_((name_ + " freed-bytes").c_str(), kMemBucketSize, kMemBucketCount),
gc_time_histogram_(nullptr),
metrics_gc_count_(nullptr),
+ metrics_gc_count_delta_(nullptr),
gc_throughput_histogram_(nullptr),
gc_tracing_throughput_hist_(nullptr),
gc_throughput_avg_(nullptr),
gc_tracing_throughput_avg_(nullptr),
gc_scanned_bytes_(nullptr),
+ gc_scanned_bytes_delta_(nullptr),
gc_freed_bytes_(nullptr),
+ gc_freed_bytes_delta_(nullptr),
gc_duration_(nullptr),
+ gc_duration_delta_(nullptr),
cumulative_timings_(name),
pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true),
is_transaction_active_(false),
@@ -203,11 +207,15 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
const uint64_t total_pause_time_us = total_pause_time_ns / 1'000;
metrics->WorldStopTimeDuringGCAvg()->Add(total_pause_time_us);
metrics->GcWorldStopTime()->Add(total_pause_time_us);
+ metrics->GcWorldStopTimeDelta()->Add(total_pause_time_us);
metrics->GcWorldStopCount()->AddOne();
+ metrics->GcWorldStopCountDelta()->AddOne();
// Report total collection time of all GCs put together.
metrics->TotalGcCollectionTime()->Add(NsToMs(duration_ns));
+ metrics->TotalGcCollectionTimeDelta()->Add(NsToMs(duration_ns));
if (are_metrics_initialized_) {
metrics_gc_count_->Add(1);
+ metrics_gc_count_delta_->Add(1);
// Report GC time in milliseconds.
gc_time_histogram_->Add(NsToMs(duration_ns));
// Throughput in bytes/s. Add 1us to prevent possible division by 0.
@@ -224,8 +232,11 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
gc_throughput_avg_->Add(throughput);
gc_scanned_bytes_->Add(current_iteration->GetScannedBytes());
+ gc_scanned_bytes_delta_->Add(current_iteration->GetScannedBytes());
gc_freed_bytes_->Add(current_iteration->GetFreedBytes());
+ gc_freed_bytes_delta_->Add(current_iteration->GetFreedBytes());
gc_duration_->Add(NsToMs(current_iteration->GetDurationNs()));
+ gc_duration_delta_->Add(NsToMs(current_iteration->GetDurationNs()));
}
is_transaction_active_ = false;
}
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index d11aea36c9..948a868bd2 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -162,13 +162,17 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark
Histogram<size_t> freed_bytes_histogram_;
metrics::MetricsBase<int64_t>* gc_time_histogram_;
metrics::MetricsBase<uint64_t>* metrics_gc_count_;
+ metrics::MetricsBase<uint64_t>* metrics_gc_count_delta_;
metrics::MetricsBase<int64_t>* gc_throughput_histogram_;
metrics::MetricsBase<int64_t>* gc_tracing_throughput_hist_;
metrics::MetricsBase<uint64_t>* gc_throughput_avg_;
metrics::MetricsBase<uint64_t>* gc_tracing_throughput_avg_;
metrics::MetricsBase<uint64_t>* gc_scanned_bytes_;
+ metrics::MetricsBase<uint64_t>* gc_scanned_bytes_delta_;
metrics::MetricsBase<uint64_t>* gc_freed_bytes_;
+ metrics::MetricsBase<uint64_t>* gc_freed_bytes_delta_;
metrics::MetricsBase<uint64_t>* gc_duration_;
+ metrics::MetricsBase<uint64_t>* gc_duration_delta_;
uint64_t total_thread_cpu_time_ns_;
uint64_t total_time_ns_;
uint64_t total_freed_objects_;
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 9c76060062..922b58870d 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -214,6 +214,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
need_gc = true;
}
GetMetrics()->TotalBytesAllocated()->Add(bytes_tl_bulk_allocated);
+ GetMetrics()->TotalBytesAllocatedDelta()->Add(bytes_tl_bulk_allocated);
}
}
if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
diff --git a/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc
index 28ca997cec..736ef7f879 100644
--- a/runtime/metrics/reporter.cc
+++ b/runtime/metrics/reporter.cc
@@ -196,12 +196,10 @@ void MetricsReporter::MaybeResetTimeout() {
}
}
-const ArtMetrics* MetricsReporter::GetMetrics() {
- return runtime_->GetMetrics();
-}
+ArtMetrics* MetricsReporter::GetMetrics() { return runtime_->GetMetrics(); }
void MetricsReporter::ReportMetrics() {
- const ArtMetrics* metrics = GetMetrics();
+ ArtMetrics* metrics = GetMetrics();
if (!session_started_) {
for (auto& backend : backends_) {
@@ -210,9 +208,7 @@ void MetricsReporter::ReportMetrics() {
session_started_ = true;
}
- for (auto& backend : backends_) {
- metrics->ReportAllMetrics(backend.get());
- }
+ metrics->ReportAllMetricsAndResetValueMetrics(ToRawPointers(backends_));
}
void MetricsReporter::UpdateSessionInBackends() {
diff --git a/runtime/metrics/reporter.h b/runtime/metrics/reporter.h
index af9e0ca151..865815e0bf 100644
--- a/runtime/metrics/reporter.h
+++ b/runtime/metrics/reporter.h
@@ -136,7 +136,7 @@ class MetricsReporter {
// Returns the metrics to be reported.
// This exists only for testing purposes so that we can verify reporting with minimum
// runtime interference.
- virtual const ArtMetrics* GetMetrics();
+ virtual ArtMetrics* GetMetrics();
MetricsReporter(const ReportingConfig& config, Runtime* runtime);
diff --git a/runtime/metrics/reporter_test.cc b/runtime/metrics/reporter_test.cc
index 62131d8e87..848a74ee61 100644
--- a/runtime/metrics/reporter_test.cc
+++ b/runtime/metrics/reporter_test.cc
@@ -34,13 +34,10 @@ namespace metrics {
// other runtime setup logic.
class MockMetricsReporter : public MetricsReporter {
protected:
- MockMetricsReporter(const ReportingConfig& config, Runtime* runtime) :
- MetricsReporter(config, runtime),
- art_metrics_(new ArtMetrics()) {}
+ MockMetricsReporter(const ReportingConfig& config, Runtime* runtime)
+ : MetricsReporter(config, runtime), art_metrics_(std::make_unique<ArtMetrics>()) {}
- const ArtMetrics* GetMetrics() override {
- return art_metrics_.get();
- }
+ ArtMetrics* GetMetrics() override { return art_metrics_.get(); }
std::unique_ptr<ArtMetrics> art_metrics_;
diff --git a/runtime/metrics/statsd.cc b/runtime/metrics/statsd.cc
index 036f667171..3b498db414 100644
--- a/runtime/metrics/statsd.cc
+++ b/runtime/metrics/statsd.cc
@@ -47,27 +47,49 @@ constexpr std::optional<int32_t> EncodeDatumId(DatumId datum_id) {
case DatumId::kClassVerificationTotalTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_CLASS_VERIFICATION_TIME_COUNTER_MICROS);
+ case DatumId::kClassVerificationTotalTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_CLASS_VERIFICATION_TIME_MICROS);
case DatumId::kJitMethodCompileTotalTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_JIT_METHOD_COMPILE_TIME_MICROS);
+ case DatumId::kJitMethodCompileTotalTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_JIT_METHOD_COMPILE_TIME_MICROS);
case DatumId::kClassLoadingTotalTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_CLASS_LOADING_TIME_COUNTER_MICROS);
+ case DatumId::kClassLoadingTotalTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_CLASS_LOADING_TIME_MICROS);
case DatumId::kClassVerificationCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_CLASS_VERIFICATION_COUNT);
+ case DatumId::kClassVerificationCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_CLASS_VERIFICATION_COUNT);
case DatumId::kWorldStopTimeDuringGCAvg:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_TIME_AVG_MICROS);
case DatumId::kYoungGcCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_COUNT);
+ case DatumId::kYoungGcCountDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_COUNT);
case DatumId::kFullGcCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_COUNT);
+ case DatumId::kFullGcCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_COUNT);
case DatumId::kTotalBytesAllocated:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_TOTAL_BYTES_ALLOCATED);
+ case DatumId::kTotalBytesAllocatedDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_TOTAL_BYTES_ALLOCATED);
case DatumId::kYoungGcCollectionTime:
return std::make_optional(
statsd::
@@ -86,6 +108,9 @@ constexpr std::optional<int32_t> EncodeDatumId(DatumId datum_id) {
case DatumId::kJitMethodCompileCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_JIT_METHOD_COMPILE_COUNT);
+ case DatumId::kJitMethodCompileCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_JIT_METHOD_COMPILE_COUNT);
case DatumId::kYoungGcTracingThroughput:
return std::make_optional(
statsd::
@@ -97,6 +122,9 @@ constexpr std::optional<int32_t> EncodeDatumId(DatumId datum_id) {
case DatumId::kTotalGcCollectionTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_TOTAL_COLLECTION_TIME_MS);
+ case DatumId::kTotalGcCollectionTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_TOTAL_COLLECTION_TIME_MS);
case DatumId::kYoungGcThroughputAvg:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_THROUGHPUT_AVG_MB_PER_SEC);
@@ -112,27 +140,57 @@ constexpr std::optional<int32_t> EncodeDatumId(DatumId datum_id) {
case DatumId::kGcWorldStopTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_TIME_US);
+ case DatumId::kGcWorldStopTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_WORLD_STOP_TIME_US);
case DatumId::kGcWorldStopCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_COUNT);
+ case DatumId::kGcWorldStopCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_WORLD_STOP_COUNT);
case DatumId::kYoungGcScannedBytes:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_SCANNED_BYTES);
+ case DatumId::kYoungGcScannedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_SCANNED_BYTES);
case DatumId::kYoungGcFreedBytes:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_FREED_BYTES);
+ case DatumId::kYoungGcFreedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_FREED_BYTES);
case DatumId::kYoungGcDuration:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_DURATION_MS);
+ case DatumId::kYoungGcDurationDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_DURATION_MS);
case DatumId::kFullGcScannedBytes:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_SCANNED_BYTES);
+ case DatumId::kFullGcScannedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_SCANNED_BYTES);
case DatumId::kFullGcFreedBytes:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_FREED_BYTES);
+ case DatumId::kFullGcFreedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_FREED_BYTES);
case DatumId::kFullGcDuration:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_DURATION_MS);
+ case DatumId::kFullGcDurationDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_DURATION_MS);
}
}
@@ -247,22 +305,39 @@ class StatsdBackend : public MetricsBackend {
void ReportCounter(DatumId counter_type, uint64_t value) override {
std::optional<int32_t> datum_id = EncodeDatumId(counter_type);
- if (datum_id.has_value()) {
- statsd::stats_write(
- statsd::ART_DATUM_REPORTED,
- session_data_.session_id,
- session_data_.uid,
- EncodeCompileFilter(session_data_.compiler_filter),
- EncodeCompilationReason(session_data_.compilation_reason),
- current_timestamp_,
- 0, // TODO: collect and report thread type (0 means UNKNOWN, but that
- // constant is not present in all branches)
- datum_id.value(),
- static_cast<int64_t>(value),
- statsd::ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN,
- statsd::ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN,
- EncodeInstructionSet(kRuntimeISA));
+ if (!datum_id.has_value()) {
+ return;
}
+
+ int32_t atom;
+ switch (counter_type) {
+#define EVENT_METRIC_CASE(name, ...) case DatumId::k##name:
+ ART_EVENT_METRICS(EVENT_METRIC_CASE)
+#undef EVENT_METRIC_CASE
+ atom = statsd::ART_DATUM_REPORTED;
+ break;
+
+#define VALUE_METRIC_CASE(name, type, ...) case DatumId::k##name:
+ ART_VALUE_METRICS(VALUE_METRIC_CASE)
+#undef VALUE_METRIC_CASE
+ atom = statsd::ART_DATUM_DELTA_REPORTED;
+ break;
+ }
+
+ statsd::stats_write(
+ atom,
+ session_data_.session_id,
+ session_data_.uid,
+ EncodeCompileFilter(session_data_.compiler_filter),
+ EncodeCompilationReason(session_data_.compilation_reason),
+ current_timestamp_,
+ 0, // TODO: collect and report thread type (0 means UNKNOWN, but that
+ // constant is not present in all branches)
+ datum_id.value(),
+ static_cast<int64_t>(value),
+ statsd::ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN,
+ statsd::ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN,
+ EncodeInstructionSet(kRuntimeISA));
}
void ReportHistogram(DatumId /*histogram_type*/,
diff --git a/runtime/verifier/class_verifier.cc b/runtime/verifier/class_verifier.cc
index 8c541f877e..8946bb2081 100644
--- a/runtime/verifier/class_verifier.cc
+++ b/runtime/verifier/class_verifier.cc
@@ -176,6 +176,9 @@ FailureKind ClassVerifier::VerifyClass(Thread* self,
GetMetrics()->ClassVerificationCount()->AddOne();
+ GetMetrics()->ClassVerificationTotalTimeDelta()->Add(elapsed_time_microseconds);
+ GetMetrics()->ClassVerificationCountDelta()->AddOne();
+
if (failure_data.kind == verifier::FailureKind::kHardFailure && callbacks != nullptr) {
ClassReference ref(dex_file, dex_file->GetIndexForClassDef(class_def));
callbacks->ClassRejected(ref);
diff --git a/test/2232-write-metrics-to-log/expected-stderr.txt b/test/2232-write-metrics-to-log/expected-stderr.txt
index 9546999d34..5bc27fe98f 100644
--- a/test/2232-write-metrics-to-log/expected-stderr.txt
+++ b/test/2232-write-metrics-to-log/expected-stderr.txt
@@ -1 +1 @@
-ClassVerificationTotalTime
+ClassVerificationTotalTimeDelta
diff --git a/test/2232-write-metrics-to-log/run.py b/test/2232-write-metrics-to-log/run.py
index 980da22824..89fe040087 100644
--- a/test/2232-write-metrics-to-log/run.py
+++ b/test/2232-write-metrics-to-log/run.py
@@ -26,5 +26,5 @@ def run(ctx, args):
# Check that one of the metrics appears in stderr.
ctx.run(
- fr"sed -i -n 's/.*\(ClassVerificationTotalTime\).*/\1/p' '{args.stderr_file}'"
+ fr"sed -i -n 's/.*\(ClassVerificationTotalTimeDelta\).*/\1/p' '{args.stderr_file}'"
)