fb28cfe603
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}
269 lines
9.6 KiB
C++
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
|