ab671ee816
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}
241 lines
8.4 KiB
C++
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
|