diff options
| -rw-r--r-- | compiler/jit/jit_compiler.cc | 2 | ||||
| -rw-r--r-- | libartbase/base/metrics/metrics.h | 150 | ||||
| -rw-r--r-- | libartbase/base/metrics/metrics_common.cc | 34 | ||||
| -rw-r--r-- | libartbase/base/metrics/metrics_test.cc | 94 | ||||
| -rw-r--r-- | libartbase/base/metrics/metrics_test.h | 4 | ||||
| -rw-r--r-- | libartbase/base/utils.h | 13 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 1 | ||||
| -rw-r--r-- | runtime/gc/collector/concurrent_copying.cc | 8 | ||||
| -rw-r--r-- | runtime/gc/collector/garbage_collector.cc | 11 | ||||
| -rw-r--r-- | runtime/gc/collector/garbage_collector.h | 4 | ||||
| -rw-r--r-- | runtime/gc/heap-inl.h | 1 | ||||
| -rw-r--r-- | runtime/metrics/reporter.cc | 10 | ||||
| -rw-r--r-- | runtime/metrics/reporter.h | 2 | ||||
| -rw-r--r-- | runtime/metrics/reporter_test.cc | 9 | ||||
| -rw-r--r-- | runtime/metrics/statsd.cc | 105 | ||||
| -rw-r--r-- | runtime/verifier/class_verifier.cc | 3 | ||||
| -rw-r--r-- | test/2232-write-metrics-to-log/expected-stderr.txt | 2 | ||||
| -rw-r--r-- | test/2232-write-metrics-to-log/run.py | 2 |
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}'" ) |