v8/test/unittests/heap/cppgc/metric-recorder-unittest.cc
Omer Katz 88e5b8f503 cppgc, heap: Implement UMA reporting for cppgc library.
This CL does 2 things:
1) Implements forwarding of histogram reporting from cppgc to v8 via
CppHeap.
2) Establishes the pipeline in GCTracer for sending the histograms to
the embedder.

Currently only cppgc histograms are populated.

See crrev.com/c/2916956 for usage.

Bug: chromium:1154636
Change-Id: I8150116f757e105d0dfac96a3f6e7dd95717f5bd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2917033
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74830}
2021-05-27 16:02:35 +00:00

326 lines
12 KiB
C++

// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/cppgc/metric-recorder.h"
#include "src/heap/cppgc/stats-collector.h"
#include "test/unittests/heap/cppgc/tests.h"
namespace cppgc {
namespace internal {
namespace {
class MetricRecorderImpl final : public MetricRecorder {
public:
void AddMainThreadEvent(const FullCycle& event) final {
FullCycle_event = event;
FullCycle_callcount++;
}
void AddMainThreadEvent(const MainThreadIncrementalMark& event) final {
MainThreadIncrementalMark_event = event;
MainThreadIncrementalMark_callcount++;
}
void AddMainThreadEvent(const MainThreadIncrementalSweep& event) final {
MainThreadIncrementalSweep_event = event;
MainThreadIncrementalSweep_callcount++;
}
static size_t FullCycle_callcount;
static FullCycle FullCycle_event;
static size_t MainThreadIncrementalMark_callcount;
static MainThreadIncrementalMark MainThreadIncrementalMark_event;
static size_t MainThreadIncrementalSweep_callcount;
static MainThreadIncrementalSweep MainThreadIncrementalSweep_event;
};
// static
size_t MetricRecorderImpl::FullCycle_callcount = 0u;
MetricRecorderImpl::FullCycle MetricRecorderImpl::FullCycle_event;
size_t MetricRecorderImpl::MainThreadIncrementalMark_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalMark
MetricRecorderImpl::MainThreadIncrementalMark_event;
size_t MetricRecorderImpl::MainThreadIncrementalSweep_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalSweep
MetricRecorderImpl::MainThreadIncrementalSweep_event;
class MetricRecorderTest : public testing::TestWithHeap {
public:
MetricRecorderTest() : stats(Heap::From(GetHeap())->stats_collector()) {
stats->SetMetricRecorder(std::make_unique<MetricRecorderImpl>());
}
void StartGC() {
stats->NotifyMarkingStarted(
GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
}
void EndGC(size_t marked_bytes) {
stats->NotifyMarkingCompleted(marked_bytes);
stats->NotifySweepingCompleted();
}
StatsCollector* stats;
};
} // namespace
TEST_F(MetricRecorderTest, IncrementalScopesReportedImmediately) {
MetricRecorderImpl::FullCycle_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalMark_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalSweep_callcount = 0u;
StartGC();
{
EXPECT_EQ(0u, MetricRecorderImpl::MainThreadIncrementalMark_callcount);
{
StatsCollector::EnabledScope scope(
Heap::From(GetHeap())->stats_collector(),
StatsCollector::kIncrementalMark);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(1));
}
EXPECT_EQ(1u, MetricRecorderImpl::MainThreadIncrementalMark_callcount);
EXPECT_LT(0u,
MetricRecorderImpl::MainThreadIncrementalMark_event.duration_us);
}
{
EXPECT_EQ(0u, MetricRecorderImpl::MainThreadIncrementalSweep_callcount);
{
StatsCollector::EnabledScope scope(
Heap::From(GetHeap())->stats_collector(),
StatsCollector::kIncrementalSweep);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(1));
}
EXPECT_EQ(1u, MetricRecorderImpl::MainThreadIncrementalSweep_callcount);
EXPECT_LT(0u,
MetricRecorderImpl::MainThreadIncrementalSweep_event.duration_us);
}
EXPECT_EQ(0u, MetricRecorderImpl::FullCycle_callcount);
EndGC(0);
}
TEST_F(MetricRecorderTest, NonIncrementlaScopesNotReportedImmediately) {
MetricRecorderImpl::FullCycle_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalMark_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalSweep_callcount = 0u;
StartGC();
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicMark);
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicWeak);
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicCompact);
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicSweep);
}
{
StatsCollector::EnabledConcurrentScope scope(
Heap::From(GetHeap())->stats_collector(),
StatsCollector::kConcurrentMark);
}
{
StatsCollector::EnabledConcurrentScope scope(
Heap::From(GetHeap())->stats_collector(),
StatsCollector::kConcurrentSweep);
}
EXPECT_EQ(0u, MetricRecorderImpl::MainThreadIncrementalMark_callcount);
EXPECT_EQ(0u, MetricRecorderImpl::MainThreadIncrementalSweep_callcount);
EXPECT_EQ(0u, MetricRecorderImpl::FullCycle_callcount);
EndGC(0);
}
TEST_F(MetricRecorderTest, CycleEndMetricsReportedOnGcEnd) {
MetricRecorderImpl::FullCycle_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalMark_callcount = 0u;
MetricRecorderImpl::MainThreadIncrementalSweep_callcount = 0u;
StartGC();
EndGC(0);
EXPECT_EQ(0u, MetricRecorderImpl::MainThreadIncrementalMark_callcount);
EXPECT_EQ(0u, MetricRecorderImpl::MainThreadIncrementalSweep_callcount);
EXPECT_EQ(1u, MetricRecorderImpl::FullCycle_callcount);
}
TEST_F(MetricRecorderTest, CycleEndHistogramReportsCorrectValues) {
StartGC();
{
// Warmup scope to make sure everything is loaded in memory and reduce noise
// in timing measurements.
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kIncrementalMark);
}
EndGC(1000);
StartGC();
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kIncrementalMark);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(10));
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kIncrementalSweep);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(20));
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicMark);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(30));
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicWeak);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(50));
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicCompact);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(60));
}
{
StatsCollector::EnabledScope scope(Heap::From(GetHeap())->stats_collector(),
StatsCollector::kAtomicSweep);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(70));
}
{
StatsCollector::EnabledConcurrentScope scope(
Heap::From(GetHeap())->stats_collector(),
StatsCollector::kConcurrentMark);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(80));
}
{
StatsCollector::EnabledConcurrentScope scope(
Heap::From(GetHeap())->stats_collector(),
StatsCollector::kConcurrentSweep);
scope.DecreaseStartTimeForTesting(
v8::base::TimeDelta::FromMilliseconds(100));
}
EndGC(300);
// Check durations.
static constexpr int64_t kDurationComparisonTolerance = 5000;
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event.main_thread_incremental
.mark_duration_us -
10000),
kDurationComparisonTolerance);
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event.main_thread_incremental
.sweep_duration_us -
20000),
kDurationComparisonTolerance);
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event.main_thread_atomic
.mark_duration_us -
30000),
kDurationComparisonTolerance);
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event.main_thread_atomic
.weak_duration_us -
50000),
kDurationComparisonTolerance);
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event.main_thread_atomic
.compact_duration_us -
60000),
kDurationComparisonTolerance);
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event.main_thread_atomic
.sweep_duration_us -
70000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(
MetricRecorderImpl::FullCycle_event.main_thread.mark_duration_us -
40000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(
MetricRecorderImpl::FullCycle_event.main_thread.weak_duration_us -
50000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(
MetricRecorderImpl::FullCycle_event.main_thread.compact_duration_us -
60000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(
MetricRecorderImpl::FullCycle_event.main_thread.sweep_duration_us -
90000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(MetricRecorderImpl::FullCycle_event.total.mark_duration_us -
120000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(MetricRecorderImpl::FullCycle_event.total.weak_duration_us -
50000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(MetricRecorderImpl::FullCycle_event.total.compact_duration_us -
60000),
kDurationComparisonTolerance);
EXPECT_LT(
std::abs(MetricRecorderImpl::FullCycle_event.total.sweep_duration_us -
190000),
kDurationComparisonTolerance);
// Check collection rate and efficiency.
EXPECT_DOUBLE_EQ(
0.3, MetricRecorderImpl::FullCycle_event.collection_rate_in_percent);
static constexpr double kEfficiencyComparisonTolerance = 0.0005;
EXPECT_LT(
std::abs(MetricRecorderImpl::FullCycle_event.efficiency_in_bytes_per_us -
(700.0 / (120000 + 50000 + 60000 + 190000))),
kEfficiencyComparisonTolerance);
EXPECT_LT(std::abs(MetricRecorderImpl::FullCycle_event
.main_thread_efficiency_in_bytes_per_us -
(700.0 / (40000 + 50000 + 60000 + 90000))),
kEfficiencyComparisonTolerance);
}
TEST_F(MetricRecorderTest, ObjectSizeMetricsNoAllocations) {
// Populate previous event.
StartGC();
EndGC(1000);
// Populate current event.
StartGC();
EndGC(800);
EXPECT_EQ(1000u, MetricRecorderImpl::FullCycle_event.objects.before_bytes);
EXPECT_EQ(800u, MetricRecorderImpl::FullCycle_event.objects.after_bytes);
EXPECT_EQ(200u, MetricRecorderImpl::FullCycle_event.objects.freed_bytes);
EXPECT_EQ(0u, MetricRecorderImpl::FullCycle_event.memory.before_bytes);
EXPECT_EQ(0u, MetricRecorderImpl::FullCycle_event.memory.after_bytes);
EXPECT_EQ(0u, MetricRecorderImpl::FullCycle_event.memory.freed_bytes);
}
TEST_F(MetricRecorderTest, ObjectSizeMetricsWithAllocations) {
// Populate previous event.
StartGC();
EndGC(1000);
// Populate current event.
StartGC();
stats->NotifyAllocation(300);
stats->NotifyAllocatedMemory(1400);
stats->NotifyFreedMemory(700);
stats->NotifyMarkingCompleted(800);
stats->NotifyAllocation(150);
stats->NotifyAllocatedMemory(1000);
stats->NotifyFreedMemory(400);
stats->NotifySweepingCompleted();
EXPECT_EQ(1300u, MetricRecorderImpl::FullCycle_event.objects.before_bytes);
EXPECT_EQ(800, MetricRecorderImpl::FullCycle_event.objects.after_bytes);
EXPECT_EQ(500u, MetricRecorderImpl::FullCycle_event.objects.freed_bytes);
EXPECT_EQ(700u, MetricRecorderImpl::FullCycle_event.memory.before_bytes);
EXPECT_EQ(300u, MetricRecorderImpl::FullCycle_event.memory.after_bytes);
EXPECT_EQ(400u, MetricRecorderImpl::FullCycle_event.memory.freed_bytes);
}
} // namespace internal
} // namespace cppgc