v8/test/unittests/heap/cppgc/marker-unittest.cc
Michael Lippautz ab671ee816 cppgc: Add HeapStatsCollector
This ports HeapStatsCollector (former ThreadHeapStatsCollector) from
Blink. The CL only ports accounting of allocated object size which is
needed for a simple growing strategy in a follow up.

HeapStatsCollector is a global dependency for most sub components as
it provides infrastructure for measuring time (through trace scopes)
and space.

The general idea of HeapStatsCollector is to act as sink where all sub
components push time and space information. This information is then
gathered and made available via an event that is implemented as POD.
Time-dependent info is available through regular getters (pull) and
observers (push).

Change-Id: I40b4d76e1a40c56e5df1a7353622318cde730e26
Bug: chromium:1056170
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2225902
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68150}
2020-06-03 16:08:48 +00:00

241 lines
8.4 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/marker.h"
#include "include/cppgc/allocation.h"
#include "include/cppgc/member.h"
#include "include/cppgc/persistent.h"
#include "src/heap/cppgc/heap-object-header-inl.h"
#include "src/heap/cppgc/marking-visitor.h"
#include "src/heap/cppgc/stats-collector.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
class MarkerTest : public testing::TestWithHeap {
public:
using MarkingConfig = Marker::MarkingConfig;
void DoMarking(MarkingConfig config) {
auto* heap = Heap::From(GetHeap());
Marker marker(heap);
marker.StartMarking(config);
marker.FinishMarking(config);
marker.ProcessWeakness();
// Pretend do finish sweeping as StatsCollector verifies that Notify*
// methods are called in the right order.
heap->stats_collector()->NotifySweepingCompleted();
}
};
class GCed : public GarbageCollected<GCed> {
public:
void SetChild(GCed* child) { child_ = child; }
void SetWeakChild(GCed* child) { weak_child_ = child; }
GCed* child() const { return child_.Get(); }
GCed* weak_child() const { return weak_child_.Get(); }
void Trace(cppgc::Visitor* visitor) const {
visitor->Trace(child_);
visitor->Trace(weak_child_);
}
private:
Member<GCed> child_;
WeakMember<GCed> weak_child_;
};
template <typename T>
V8_NOINLINE T access(volatile const T& t) {
return t;
}
} // namespace
TEST_F(MarkerTest, PersistentIsMarked) {
Persistent<GCed> object = MakeGarbageCollected<GCed>(GetHeap());
HeapObjectHeader& header = HeapObjectHeader::FromPayload(object);
EXPECT_FALSE(header.IsMarked());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(header.IsMarked());
}
TEST_F(MarkerTest, ReachableMemberIsMarked) {
Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetHeap());
parent->SetChild(MakeGarbageCollected<GCed>(GetHeap()));
HeapObjectHeader& header = HeapObjectHeader::FromPayload(parent->child());
EXPECT_FALSE(header.IsMarked());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(header.IsMarked());
}
TEST_F(MarkerTest, UnreachableMemberIsNotMarked) {
Member<GCed> object = MakeGarbageCollected<GCed>(GetHeap());
HeapObjectHeader& header = HeapObjectHeader::FromPayload(object);
EXPECT_FALSE(header.IsMarked());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_FALSE(header.IsMarked());
}
TEST_F(MarkerTest, ObjectReachableFromStackIsMarked) {
GCed* object = MakeGarbageCollected<GCed>(GetHeap());
EXPECT_FALSE(HeapObjectHeader::FromPayload(object).IsMarked());
DoMarking({MarkingConfig::StackState::kMayContainHeapPointers});
EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
access(object);
}
TEST_F(MarkerTest, ObjectReachableOnlyFromStackIsNotMarkedIfStackIsEmpty) {
GCed* object = MakeGarbageCollected<GCed>(GetHeap());
HeapObjectHeader& header = HeapObjectHeader::FromPayload(object);
EXPECT_FALSE(header.IsMarked());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_FALSE(header.IsMarked());
access(object);
}
TEST_F(MarkerTest, WeakReferenceToUnreachableObjectIsCleared) {
{
WeakPersistent<GCed> weak_object = MakeGarbageCollected<GCed>(GetHeap());
EXPECT_TRUE(weak_object);
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_FALSE(weak_object);
}
{
Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetHeap());
parent->SetWeakChild(MakeGarbageCollected<GCed>(GetHeap()));
EXPECT_TRUE(parent->weak_child());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_FALSE(parent->weak_child());
}
}
TEST_F(MarkerTest, WeakReferenceToReachableObjectIsNotCleared) {
// Reachable from Persistent
{
Persistent<GCed> object = MakeGarbageCollected<GCed>(GetHeap());
WeakPersistent<GCed> weak_object(object);
EXPECT_TRUE(weak_object);
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(weak_object);
}
{
Persistent<GCed> object = MakeGarbageCollected<GCed>(GetHeap());
Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetHeap());
parent->SetWeakChild(object);
EXPECT_TRUE(parent->weak_child());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(parent->weak_child());
}
// Reachable from Member
{
Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetHeap());
WeakPersistent<GCed> weak_object(MakeGarbageCollected<GCed>(GetHeap()));
parent->SetChild(weak_object);
EXPECT_TRUE(weak_object);
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(weak_object);
}
{
Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetHeap());
parent->SetChild(MakeGarbageCollected<GCed>(GetHeap()));
parent->SetWeakChild(parent->child());
EXPECT_TRUE(parent->weak_child());
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(parent->weak_child());
}
// Reachable from stack
{
GCed* object = MakeGarbageCollected<GCed>(GetHeap());
WeakPersistent<GCed> weak_object(object);
EXPECT_TRUE(weak_object);
DoMarking({MarkingConfig::StackState::kMayContainHeapPointers});
EXPECT_TRUE(weak_object);
access(object);
}
{
GCed* object = MakeGarbageCollected<GCed>(GetHeap());
Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetHeap());
parent->SetWeakChild(object);
EXPECT_TRUE(parent->weak_child());
DoMarking({MarkingConfig::StackState::kMayContainHeapPointers});
EXPECT_TRUE(parent->weak_child());
access(object);
}
}
TEST_F(MarkerTest, DeepHierarchyIsMarked) {
static constexpr int kHierarchyDepth = 10;
Persistent<GCed> root = MakeGarbageCollected<GCed>(GetHeap());
GCed* parent = root;
for (int i = 0; i < kHierarchyDepth; ++i) {
parent->SetChild(MakeGarbageCollected<GCed>(GetHeap()));
parent->SetWeakChild(parent->child());
parent = parent->child();
}
DoMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(HeapObjectHeader::FromPayload(root).IsMarked());
parent = root;
for (int i = 0; i < kHierarchyDepth; ++i) {
EXPECT_TRUE(HeapObjectHeader::FromPayload(parent->child()).IsMarked());
EXPECT_TRUE(parent->weak_child());
parent = parent->child();
}
}
TEST_F(MarkerTest, NestedObjectsOnStackAreMarked) {
GCed* root = MakeGarbageCollected<GCed>(GetHeap());
root->SetChild(MakeGarbageCollected<GCed>(GetHeap()));
root->child()->SetChild(MakeGarbageCollected<GCed>(GetHeap()));
DoMarking({MarkingConfig::StackState::kMayContainHeapPointers});
EXPECT_TRUE(HeapObjectHeader::FromPayload(root).IsMarked());
EXPECT_TRUE(HeapObjectHeader::FromPayload(root->child()).IsMarked());
EXPECT_TRUE(HeapObjectHeader::FromPayload(root->child()->child()).IsMarked());
}
namespace {
class GCedWithCallback : public GarbageCollected<GCedWithCallback> {
public:
template <typename Callback>
explicit GCedWithCallback(Callback callback) {
callback(this);
}
void Trace(Visitor*) const {}
};
} // namespace
TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedEmptyStack) {
Marker marker(Heap::From(GetHeap()));
marker.StartMarking({MarkingConfig::StackState::kMayContainHeapPointers});
GCedWithCallback* object = MakeGarbageCollected<GCedWithCallback>(
GetHeap(), [&marker](GCedWithCallback* obj) {
Member<GCedWithCallback> member(obj);
marker.GetMarkingVisitorForTesting()->Trace(member);
});
EXPECT_FALSE(HeapObjectHeader::FromPayload(object).IsMarked());
marker.FinishMarking({MarkingConfig::StackState::kNoHeapPointers});
EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
}
TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedNonEmptyStack) {
Marker marker(Heap::From(GetHeap()));
marker.StartMarking({MarkingConfig::StackState::kMayContainHeapPointers});
MakeGarbageCollected<GCedWithCallback>(GetHeap(), [&marker](
GCedWithCallback* obj) {
Member<GCedWithCallback> member(obj);
marker.GetMarkingVisitorForTesting()->Trace(member);
EXPECT_FALSE(HeapObjectHeader::FromPayload(obj).IsMarked());
marker.FinishMarking({MarkingConfig::StackState::kMayContainHeapPointers});
EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
});
}
} // namespace internal
} // namespace cppgc