summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-02 16:01:53 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-02 16:01:53 +0000
commit4be93fc090bb9d070945a4b1e7aa1b6ba671a80e (patch)
tree1cf815962aa35f1780272d629873f63af25167f4
parent32a19c194b537d143c75bed9bda5578757a734e8 (diff)
parent6ca68eb96d6bdea3f7c565168e5b93ea1a234a33 (diff)
downloadart-android13-mainline-media-swcodec-release.tar.gz
Snap for 9680066 from 6ca68eb96d6bdea3f7c565168e5b93ea1a234a33 to mainline-media-swcodec-releaseaml_swc_331712000android13-mainline-media-swcodec-release
Change-Id: I1b764d2205a242ccc8a08c5652da5b3e2fce6536
-rw-r--r--libartbase/base/metrics/metrics.h48
-rw-r--r--runtime/gc/collector/mark_compact.cc18
-rw-r--r--runtime/gc/heap_test.cc145
3 files changed, 207 insertions, 4 deletions
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index 9d92ed904c..fd0ae54dac 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -29,6 +29,7 @@
#include "android-base/logging.h"
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "base/time_utils.h"
#include "tinyxml2.h"
@@ -106,6 +107,17 @@ class Runtime;
struct RuntimeArgumentMap;
namespace metrics {
+template <typename value_t>
+class MetricsBase;
+} // namespace metrics
+
+namespace gc {
+class HeapTest_GCMetrics_Test;
+template <typename T>
+bool AnyIsNonNull(const metrics::MetricsBase<T>* x, const metrics::MetricsBase<T>* y);
+} // namespace gc
+
+namespace metrics {
/**
* An enumeration of all ART counters and histograms.
@@ -285,6 +297,15 @@ class MetricsBase {
public:
virtual void Add(value_t value) = 0;
virtual ~MetricsBase() { }
+
+ private:
+ // Is the metric "null", i.e. never updated or freshly reset?
+ // Used for testing purpose only.
+ virtual bool IsNull() const = 0;
+
+ ART_FRIEND_TEST(gc::HeapTest, GCMetrics);
+ template <typename T>
+ friend bool gc::AnyIsNonNull(const MetricsBase<T>* x, const MetricsBase<T>* y);
};
template <DatumId counter_type, typename T = uint64_t>
@@ -300,7 +321,9 @@ 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 Add(value_t value) override {
+ value_.fetch_add(value, std::memory_order::memory_order_relaxed);
+ }
void Report(const std::vector<MetricsBackend*>& backends) const {
for (MetricsBackend* backend : backends) {
@@ -313,6 +336,8 @@ class MetricsCounter : public MetricsBase<T> {
value_t Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
private:
+ bool IsNull() const override { return Value() == 0; }
+
std::atomic<value_t> value_;
static_assert(std::atomic<value_t>::is_always_lock_free);
@@ -341,7 +366,7 @@ class MetricsAverage final : public MetricsCounter<datum_id, T> {
// 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) {
+ void Add(value_t value) override {
MetricsCounter<datum_id, value_t>::Add(value);
count_.fetch_add(1, std::memory_order::memory_order_release);
}
@@ -363,6 +388,10 @@ class MetricsAverage final : public MetricsCounter<datum_id, T> {
}
private:
+ count_t Count() const { return count_.load(std::memory_order::memory_order_relaxed); }
+
+ bool IsNull() const override { return Count() == 0; }
+
std::atomic<count_t> count_;
static_assert(std::atomic<count_t>::is_always_lock_free);
@@ -397,6 +426,10 @@ class MetricsDeltaCounter : public MetricsBase<T> {
void Reset() { value_ = 0; }
private:
+ value_t Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
+
+ bool IsNull() const override { return Value() == 0; }
+
std::atomic<value_t> value_;
static_assert(std::atomic<value_t>::is_always_lock_free);
@@ -422,7 +455,7 @@ class MetricsHistogram final : public MetricsBase<int64_t> {
== RoundUp(sizeof(intptr_t) + sizeof(value_t) * num_buckets_, sizeof(uint64_t)));
}
- void Add(int64_t value) {
+ void Add(int64_t value) override {
const size_t i = FindBucketId(value);
buckets_[i].fetch_add(1u, std::memory_order::memory_order_relaxed);
}
@@ -462,6 +495,11 @@ class MetricsHistogram final : public MetricsBase<int64_t> {
return std::vector<value_t>{buckets_.begin(), buckets_.end()};
}
+ bool IsNull() const override {
+ std::vector<value_t> buckets = GetBuckets();
+ return std::all_of(buckets.cbegin(), buckets.cend(), [](value_t i) { return i == 0; });
+ }
+
std::array<std::atomic<value_t>, num_buckets_> buckets_;
static_assert(std::atomic<value_t>::is_always_lock_free);
@@ -479,7 +517,7 @@ class MetricsAccumulator final : MetricsBase<T> {
RoundUp(sizeof(intptr_t) + sizeof(T), sizeof(uint64_t)));
}
- void Add(T value) {
+ void Add(T value) override {
T current = value_.load(std::memory_order::memory_order_relaxed);
T new_value;
do {
@@ -505,6 +543,8 @@ class MetricsAccumulator final : MetricsBase<T> {
private:
T Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
+ bool IsNull() const override { return Value() == 0; }
+
std::atomic<T> value_;
friend class ArtMetrics;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index f262b66f7a..8aba47fb3f 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -353,6 +353,24 @@ MarkCompact::MarkCompact(Heap* heap)
CHECK_EQ(*conc_compaction_termination_page_, 0);
// In most of the cases, we don't expect more than one LinearAlloc space.
linear_alloc_spaces_data_.reserve(1);
+
+ // Initialize GC metrics.
+ metrics::ArtMetrics* metrics = GetMetrics();
+ // The mark-compact collector supports only full-heap collections at the moment.
+ 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();
+ are_metrics_initialized_ = true;
}
void MarkCompact::AddLinearAllocSpaceData(uint8_t* begin, size_t len) {
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 5e8c1e368a..e8a0f711fa 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <algorithm>
+
+#include "base/metrics/metrics.h"
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "gc/accounting/card_table-inl.h"
@@ -99,6 +102,148 @@ TEST_F(HeapTest, DumpGCPerformanceOnShutdown) {
Runtime::Current()->SetDumpGCPerformanceOnShutdown(true);
}
+template <typename T>
+bool AnyIsNonNull(const metrics::MetricsBase<T>* x, const metrics::MetricsBase<T>* y) {
+ return !x->IsNull() || !y->IsNull();
+}
+
+TEST_F(HeapTest, GCMetrics) {
+ // Allocate a few string objects (to be collected), then trigger garbage
+ // collection, and check that GC metrics are updated (where applicable).
+ {
+ constexpr const size_t kNumObj = 128;
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<kNumObj> hs(soa.Self());
+ for (size_t i = 0u; i < kNumObj; ++i) {
+ Handle<mirror::String> string [[maybe_unused]] (
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
+ }
+ }
+ Heap* heap = Runtime::Current()->GetHeap();
+ heap->CollectGarbage(/* clear_soft_references= */ false);
+
+ // ART Metrics.
+ metrics::ArtMetrics* metrics = Runtime::Current()->GetMetrics();
+ // ART full-heap GC metrics.
+ metrics::MetricsBase<int64_t>* full_gc_collection_time = metrics->FullGcCollectionTime();
+ metrics::MetricsBase<uint64_t>* full_gc_count = metrics->FullGcCount();
+ metrics::MetricsBase<uint64_t>* full_gc_count_delta = metrics->FullGcCountDelta();
+ metrics::MetricsBase<int64_t>* full_gc_throughput = metrics->FullGcThroughput();
+ metrics::MetricsBase<int64_t>* full_gc_tracing_throughput = metrics->FullGcTracingThroughput();
+ metrics::MetricsBase<uint64_t>* full_gc_throughput_avg = metrics->FullGcThroughputAvg();
+ metrics::MetricsBase<uint64_t>* full_gc_tracing_throughput_avg =
+ metrics->FullGcTracingThroughputAvg();
+ metrics::MetricsBase<uint64_t>* full_gc_scanned_bytes = metrics->FullGcScannedBytes();
+ metrics::MetricsBase<uint64_t>* full_gc_scanned_bytes_delta = metrics->FullGcScannedBytesDelta();
+ metrics::MetricsBase<uint64_t>* full_gc_freed_bytes = metrics->FullGcFreedBytes();
+ metrics::MetricsBase<uint64_t>* full_gc_freed_bytes_delta = metrics->FullGcFreedBytesDelta();
+ metrics::MetricsBase<uint64_t>* full_gc_duration = metrics->FullGcDuration();
+ metrics::MetricsBase<uint64_t>* full_gc_duration_delta = metrics->FullGcDurationDelta();
+ // ART young-generation GC metrics.
+ metrics::MetricsBase<int64_t>* young_gc_collection_time = metrics->YoungGcCollectionTime();
+ metrics::MetricsBase<uint64_t>* young_gc_count = metrics->YoungGcCount();
+ metrics::MetricsBase<uint64_t>* young_gc_count_delta = metrics->YoungGcCountDelta();
+ metrics::MetricsBase<int64_t>* young_gc_throughput = metrics->YoungGcThroughput();
+ metrics::MetricsBase<int64_t>* young_gc_tracing_throughput = metrics->YoungGcTracingThroughput();
+ metrics::MetricsBase<uint64_t>* young_gc_throughput_avg = metrics->YoungGcThroughputAvg();
+ metrics::MetricsBase<uint64_t>* young_gc_tracing_throughput_avg =
+ metrics->YoungGcTracingThroughputAvg();
+ metrics::MetricsBase<uint64_t>* young_gc_scanned_bytes = metrics->YoungGcScannedBytes();
+ metrics::MetricsBase<uint64_t>* young_gc_scanned_bytes_delta =
+ metrics->YoungGcScannedBytesDelta();
+ metrics::MetricsBase<uint64_t>* young_gc_freed_bytes = metrics->YoungGcFreedBytes();
+ metrics::MetricsBase<uint64_t>* young_gc_freed_bytes_delta = metrics->YoungGcFreedBytesDelta();
+ metrics::MetricsBase<uint64_t>* young_gc_duration = metrics->YoungGcDuration();
+ metrics::MetricsBase<uint64_t>* young_gc_duration_delta = metrics->YoungGcDurationDelta();
+
+ CollectorType fg_collector_type = heap->GetForegroundCollectorType();
+ if (fg_collector_type == kCollectorTypeCC || fg_collector_type == kCollectorTypeCMC) {
+ // Only the Concurrent Copying and Concurrent Mark-Compact collectors enable
+ // GC metrics at the moment.
+ if (heap->GetUseGenerationalCC()) {
+ // Check that full-heap and/or young-generation GC metrics are non-null
+ // after trigerring the collection.
+ EXPECT_PRED2(AnyIsNonNull<int64_t>, full_gc_collection_time, young_gc_collection_time);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_count, young_gc_count);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_count_delta, young_gc_count_delta);
+ EXPECT_PRED2(AnyIsNonNull<int64_t>, full_gc_throughput, young_gc_throughput);
+ EXPECT_PRED2(AnyIsNonNull<int64_t>, full_gc_tracing_throughput, young_gc_tracing_throughput);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_throughput_avg, young_gc_throughput_avg);
+ EXPECT_PRED2(
+ AnyIsNonNull<uint64_t>, full_gc_tracing_throughput_avg, young_gc_tracing_throughput_avg);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_scanned_bytes, young_gc_scanned_bytes);
+ EXPECT_PRED2(
+ AnyIsNonNull<uint64_t>, full_gc_scanned_bytes_delta, young_gc_scanned_bytes_delta);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_freed_bytes, young_gc_freed_bytes);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_freed_bytes_delta, young_gc_freed_bytes_delta);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_duration, young_gc_duration);
+ EXPECT_PRED2(AnyIsNonNull<uint64_t>, full_gc_duration_delta, young_gc_duration_delta);
+ } else {
+ // Check that only full-heap GC metrics are non-null after trigerring the collection.
+ EXPECT_FALSE(full_gc_collection_time->IsNull());
+ EXPECT_FALSE(full_gc_count->IsNull());
+ EXPECT_FALSE(full_gc_count_delta->IsNull());
+ EXPECT_FALSE(full_gc_throughput->IsNull());
+ EXPECT_FALSE(full_gc_tracing_throughput->IsNull());
+ EXPECT_FALSE(full_gc_throughput_avg->IsNull());
+ EXPECT_FALSE(full_gc_tracing_throughput_avg->IsNull());
+ if (fg_collector_type != kCollectorTypeCMC) {
+ // TODO(b/270957146): For some reason, these metrics are still null
+ // after running the Concurrent Mark-Compact collector; investigate why.
+ EXPECT_FALSE(full_gc_scanned_bytes->IsNull());
+ EXPECT_FALSE(full_gc_scanned_bytes_delta->IsNull());
+ }
+ EXPECT_FALSE(full_gc_freed_bytes->IsNull());
+ EXPECT_FALSE(full_gc_freed_bytes_delta->IsNull());
+ EXPECT_FALSE(full_gc_duration->IsNull());
+ EXPECT_FALSE(full_gc_duration_delta->IsNull());
+
+ EXPECT_TRUE(young_gc_collection_time->IsNull());
+ EXPECT_TRUE(young_gc_count->IsNull());
+ EXPECT_TRUE(young_gc_count_delta->IsNull());
+ EXPECT_TRUE(young_gc_throughput->IsNull());
+ EXPECT_TRUE(young_gc_tracing_throughput->IsNull());
+ EXPECT_TRUE(young_gc_throughput_avg->IsNull());
+ EXPECT_TRUE(young_gc_tracing_throughput_avg->IsNull());
+ EXPECT_TRUE(young_gc_scanned_bytes->IsNull());
+ EXPECT_TRUE(young_gc_scanned_bytes_delta->IsNull());
+ EXPECT_TRUE(young_gc_freed_bytes->IsNull());
+ EXPECT_TRUE(young_gc_freed_bytes_delta->IsNull());
+ EXPECT_TRUE(young_gc_duration->IsNull());
+ EXPECT_TRUE(young_gc_duration_delta->IsNull());
+ }
+ } else {
+ // Check that all metrics are null after trigerring the collection.
+ EXPECT_TRUE(full_gc_collection_time->IsNull());
+ EXPECT_TRUE(full_gc_count->IsNull());
+ EXPECT_TRUE(full_gc_count_delta->IsNull());
+ EXPECT_TRUE(full_gc_throughput->IsNull());
+ EXPECT_TRUE(full_gc_tracing_throughput->IsNull());
+ EXPECT_TRUE(full_gc_throughput_avg->IsNull());
+ EXPECT_TRUE(full_gc_tracing_throughput_avg->IsNull());
+ EXPECT_TRUE(full_gc_scanned_bytes->IsNull());
+ EXPECT_TRUE(full_gc_scanned_bytes_delta->IsNull());
+ EXPECT_TRUE(full_gc_freed_bytes->IsNull());
+ EXPECT_TRUE(full_gc_freed_bytes_delta->IsNull());
+ EXPECT_TRUE(full_gc_duration->IsNull());
+ EXPECT_TRUE(full_gc_duration_delta->IsNull());
+
+ EXPECT_TRUE(young_gc_collection_time->IsNull());
+ EXPECT_TRUE(young_gc_count->IsNull());
+ EXPECT_TRUE(young_gc_count_delta->IsNull());
+ EXPECT_TRUE(young_gc_throughput->IsNull());
+ EXPECT_TRUE(young_gc_tracing_throughput->IsNull());
+ EXPECT_TRUE(young_gc_throughput_avg->IsNull());
+ EXPECT_TRUE(young_gc_tracing_throughput_avg->IsNull());
+ EXPECT_TRUE(young_gc_scanned_bytes->IsNull());
+ EXPECT_TRUE(young_gc_scanned_bytes_delta->IsNull());
+ EXPECT_TRUE(young_gc_freed_bytes->IsNull());
+ EXPECT_TRUE(young_gc_freed_bytes_delta->IsNull());
+ EXPECT_TRUE(young_gc_duration->IsNull());
+ EXPECT_TRUE(young_gc_duration_delta->IsNull());
+ }
+}
+
class ZygoteHeapTest : public CommonRuntimeTest {
void SetUpRuntimeOptions(RuntimeOptions* options) override {
CommonRuntimeTest::SetUpRuntimeOptions(options);