v8/test/unittests/heap/cppgc/stats-collector-unittest.cc
Michael Lippautz fb28cfe603 cppgc: Wire up discarded size
So far, discarded size was maintained by the sweeper but not wired up
anywere.

Changes in this patch:
- Wire up resident size in heap statistics collection.
- Fix bugs in reporting committed and resident size.
- Sweeper test: Enforce some internal details. The details should not
  not be checked broadly but be kept as a detail to the sweeper
  itself.
- Stats collection: Test that committed and resident set size are
  reported and differ after discarding GCs.

Bug: chromium:1056170
Change-Id: Icf8871c7ea3b28253233485c736b2ca4816fd6f2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3020971
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75684}
2021-07-12 16:03:50 +00:00

269 lines
9.6 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/stats-collector.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
constexpr size_t kNoMarkedBytes = 0;
constexpr size_t kMinReportedSize = StatsCollector::kAllocationThresholdBytes;
class StatsCollectorTest : public ::testing::Test {
public:
static constexpr Platform* kNoPlatform = nullptr;
StatsCollectorTest() : stats(kNoPlatform) {}
void FakeAllocate(size_t bytes) {
stats.NotifyAllocation(bytes);
stats.NotifySafePointForConservativeCollection();
}
void FakeFree(size_t bytes) {
stats.NotifyExplicitFree(bytes);
stats.NotifySafePointForConservativeCollection();
}
StatsCollector stats;
};
} // namespace
TEST_F(StatsCollectorTest, NoMarkedBytes) {
stats.NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
stats.NotifyMarkingCompleted(kNoMarkedBytes);
stats.NotifySweepingCompleted();
auto event = stats.GetPreviousEventForTesting();
EXPECT_EQ(0u, event.marked_bytes);
}
TEST_F(StatsCollectorTest, EventPrevGCMarkedObjectSize) {
stats.NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
stats.NotifyMarkingCompleted(1024);
stats.NotifySweepingCompleted();
auto event = stats.GetPreviousEventForTesting();
EXPECT_EQ(1024u, event.marked_bytes);
}
TEST_F(StatsCollectorTest, AllocationNoReportBelowAllocationThresholdBytes) {
constexpr size_t kObjectSize = 17;
EXPECT_LT(kObjectSize, StatsCollector::kAllocationThresholdBytes);
FakeAllocate(kObjectSize);
EXPECT_EQ(0u, stats.allocated_object_size());
}
TEST_F(StatsCollectorTest, AlllocationReportAboveAllocationThresholdBytes) {
constexpr size_t kObjectSize = StatsCollector::kAllocationThresholdBytes;
EXPECT_GE(kObjectSize, StatsCollector::kAllocationThresholdBytes);
FakeAllocate(kObjectSize);
EXPECT_EQ(kObjectSize, stats.allocated_object_size());
}
TEST_F(StatsCollectorTest, InitialAllocatedObjectSize) {
stats.NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
EXPECT_EQ(0u, stats.allocated_object_size());
stats.NotifyMarkingCompleted(kNoMarkedBytes);
EXPECT_EQ(0u, stats.allocated_object_size());
stats.NotifySweepingCompleted();
EXPECT_EQ(0u, stats.allocated_object_size());
}
TEST_F(StatsCollectorTest, AllocatedObjectSize) {
stats.NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
FakeAllocate(kMinReportedSize);
EXPECT_EQ(kMinReportedSize, stats.allocated_object_size());
stats.NotifyMarkingCompleted(kMinReportedSize);
EXPECT_EQ(kMinReportedSize, stats.allocated_object_size());
stats.NotifySweepingCompleted();
EXPECT_EQ(kMinReportedSize, stats.allocated_object_size());
}
TEST_F(StatsCollectorTest, AllocatedObjectSizeNoMarkedBytes) {
stats.NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
FakeAllocate(kMinReportedSize);
EXPECT_EQ(kMinReportedSize, stats.allocated_object_size());
stats.NotifyMarkingCompleted(kNoMarkedBytes);
EXPECT_EQ(0u, stats.allocated_object_size());
stats.NotifySweepingCompleted();
EXPECT_EQ(0u, stats.allocated_object_size());
}
TEST_F(StatsCollectorTest, AllocatedObjectSizeAllocateAfterMarking) {
stats.NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
FakeAllocate(kMinReportedSize);
EXPECT_EQ(kMinReportedSize, stats.allocated_object_size());
stats.NotifyMarkingCompleted(kMinReportedSize);
FakeAllocate(kMinReportedSize);
EXPECT_EQ(2 * kMinReportedSize, stats.allocated_object_size());
stats.NotifySweepingCompleted();
EXPECT_EQ(2 * kMinReportedSize, stats.allocated_object_size());
}
class MockAllocationObserver : public StatsCollector::AllocationObserver {
public:
MOCK_METHOD(void, AllocatedObjectSizeIncreased, (size_t), (override));
MOCK_METHOD(void, AllocatedObjectSizeDecreased, (size_t), (override));
MOCK_METHOD(void, ResetAllocatedObjectSize, (size_t), (override));
MOCK_METHOD(void, AllocatedSizeIncreased, (size_t), (override));
MOCK_METHOD(void, AllocatedSizeDecreased, (size_t), (override));
};
TEST_F(StatsCollectorTest, RegisterUnregisterObserver) {
MockAllocationObserver observer;
stats.RegisterObserver(&observer);
stats.UnregisterObserver(&observer);
}
TEST_F(StatsCollectorTest, ObserveAllocatedObjectSizeIncreaseAndDecrease) {
MockAllocationObserver observer;
stats.RegisterObserver(&observer);
EXPECT_CALL(observer, AllocatedObjectSizeIncreased(kMinReportedSize));
FakeAllocate(kMinReportedSize);
EXPECT_CALL(observer, AllocatedObjectSizeDecreased(kMinReportedSize));
FakeFree(kMinReportedSize);
stats.UnregisterObserver(&observer);
}
namespace {
void FakeGC(StatsCollector* stats, size_t marked_bytes) {
stats->NotifyMarkingStarted(GarbageCollector::Config::CollectionType::kMajor,
GarbageCollector::Config::IsForcedGC::kNotForced);
stats->NotifyMarkingCompleted(marked_bytes);
stats->NotifySweepingCompleted();
}
} // namespace
TEST_F(StatsCollectorTest, ObserveResetAllocatedObjectSize) {
MockAllocationObserver observer;
stats.RegisterObserver(&observer);
EXPECT_CALL(observer, AllocatedObjectSizeIncreased(kMinReportedSize));
FakeAllocate(kMinReportedSize);
EXPECT_CALL(observer, ResetAllocatedObjectSize(64));
FakeGC(&stats, 64);
stats.UnregisterObserver(&observer);
}
TEST_F(StatsCollectorTest, ObserveAllocatedMemoryIncreaseAndDecrease) {
MockAllocationObserver observer;
stats.RegisterObserver(&observer);
static constexpr size_t kAllocatedMemorySize = 4096;
EXPECT_CALL(observer, AllocatedSizeIncreased(kAllocatedMemorySize));
stats.NotifyAllocatedMemory(kAllocatedMemorySize);
static constexpr size_t kFreedMemorySize = 2048;
EXPECT_CALL(observer, AllocatedSizeDecreased(kFreedMemorySize));
stats.NotifyFreedMemory(kFreedMemorySize);
stats.UnregisterObserver(&observer);
}
namespace {
class AllocationObserverTriggeringGC final
: public StatsCollector::AllocationObserver {
public:
AllocationObserverTriggeringGC(StatsCollector* stats, double survival_ratio)
: stats(stats), survival_ratio_(survival_ratio) {}
void AllocatedObjectSizeIncreased(size_t bytes) final {
increase_call_count++;
increased_size_bytes += bytes;
if (increase_call_count == 1) {
FakeGC(stats, bytes * survival_ratio_);
}
}
// // Mock out the rest to trigger warnings if used.
MOCK_METHOD(void, AllocatedObjectSizeDecreased, (size_t), (override));
MOCK_METHOD(void, ResetAllocatedObjectSize, (size_t), (override));
size_t increase_call_count = 0;
size_t increased_size_bytes = 0;
StatsCollector* stats;
double survival_ratio_;
};
} // namespace
TEST_F(StatsCollectorTest, ObserverTriggersGC) {
constexpr double kSurvivalRatio = 0.5;
AllocationObserverTriggeringGC gc_observer(&stats, kSurvivalRatio);
MockAllocationObserver mock_observer;
// Internal detail: First registered observer is also notified first.
stats.RegisterObserver(&gc_observer);
stats.RegisterObserver(&mock_observer);
// Both observers see the exact allocated object size byte count.
EXPECT_CALL(mock_observer,
ResetAllocatedObjectSize(kMinReportedSize * kSurvivalRatio));
EXPECT_CALL(gc_observer,
ResetAllocatedObjectSize(kMinReportedSize * kSurvivalRatio));
// Since the GC clears counters, mock_observer should see an increase call
// with a delta of zero bytes. This expectation makes use of the internal
// detail that first registered observer triggers GC.
EXPECT_CALL(mock_observer, AllocatedObjectSizeIncreased(0));
// Trigger scenario.
FakeAllocate(kMinReportedSize);
EXPECT_EQ(1u, gc_observer.increase_call_count);
EXPECT_EQ(kMinReportedSize, gc_observer.increased_size_bytes);
stats.UnregisterObserver(&gc_observer);
stats.UnregisterObserver(&mock_observer);
}
TEST_F(StatsCollectorTest, AllocatedMemorySize) {
EXPECT_EQ(0u, stats.allocated_memory_size());
stats.NotifyAllocatedMemory(1024);
EXPECT_EQ(1024u, stats.allocated_memory_size());
stats.NotifyFreedMemory(1024);
EXPECT_EQ(0u, stats.allocated_memory_size());
}
TEST_F(StatsCollectorTest, DiscardedMemorySize) {
EXPECT_EQ(0u, stats.discarded_memory_size());
stats.IncrementDiscardedMemory(1024);
EXPECT_EQ(1024u, stats.discarded_memory_size());
stats.DecrementDiscardedMemory(1024);
EXPECT_EQ(0u, stats.discarded_memory_size());
}
TEST_F(StatsCollectorTest, ResidentMemorySizeWithoutDiscarded) {
EXPECT_EQ(0u, stats.resident_memory_size());
stats.NotifyAllocatedMemory(1024);
EXPECT_EQ(1024u, stats.resident_memory_size());
stats.NotifyFreedMemory(1024);
EXPECT_EQ(0u, stats.resident_memory_size());
}
TEST_F(StatsCollectorTest, ResidentMemorySizeWithDiscarded) {
EXPECT_EQ(0u, stats.resident_memory_size());
stats.NotifyAllocatedMemory(8192);
EXPECT_EQ(8192u, stats.resident_memory_size());
stats.IncrementDiscardedMemory(4096);
EXPECT_EQ(4096u, stats.resident_memory_size());
stats.DecrementDiscardedMemory(4096);
EXPECT_EQ(8192u, stats.resident_memory_size());
stats.NotifyFreedMemory(8192);
EXPECT_EQ(0u, stats.resident_memory_size());
}
} // namespace internal
} // namespace cppgc