Commit 4b437bee authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

cppgc: Add support for young generation only marking verification

Marking verification should only process young generation objects when
verifying the marking state of a young generation garbage collection.

Bug: v8:12324
Change-Id: I01db261437ec5c42ddb6c79c44e31b5fe0e536d7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3231343Reviewed-by: 's avatarOmer Katz <omerkatz@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77518}
parent 5b5a2002
...@@ -504,13 +504,18 @@ void CppHeap::TraceEpilogue(TraceSummary* trace_summary) { ...@@ -504,13 +504,18 @@ void CppHeap::TraceEpilogue(TraceSummary* trace_summary) {
buffered_allocated_bytes_ = 0; buffered_allocated_bytes_ = 0;
const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers(); const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers();
#if CPPGC_VERIFY_HEAP #if CPPGC_VERIFY_HEAP
UnifiedHeapMarkingVerifier verifier(*this); UnifiedHeapMarkingVerifier verifier(
*this, cppgc::internal::Heap::Config::CollectionType::kMajor);
verifier.Run( verifier.Run(
stack_state_of_prev_gc(), stack_end_of_current_gc(), stack_state_of_prev_gc(), stack_end_of_current_gc(),
stats_collector()->marked_bytes() + bytes_allocated_in_prefinalizers); stats_collector()->marked_bytes() + bytes_allocated_in_prefinalizers);
#endif // CPPGC_VERIFY_HEAP #endif // CPPGC_VERIFY_HEAP
USE(bytes_allocated_in_prefinalizers); USE(bytes_allocated_in_prefinalizers);
#if defined(CPPGC_YOUNG_GENERATION)
ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)
{ {
cppgc::subtle::NoGarbageCollectionScope no_gc(*this); cppgc::subtle::NoGarbageCollectionScope no_gc(*this);
cppgc::internal::Sweeper::SweepingConfig::CompactableSpaceHandling cppgc::internal::Sweeper::SweepingConfig::CompactableSpaceHandling
......
...@@ -56,9 +56,10 @@ class UnifiedHeapVerificationVisitor final : public JSVisitor { ...@@ -56,9 +56,10 @@ class UnifiedHeapVerificationVisitor final : public JSVisitor {
} // namespace } // namespace
UnifiedHeapMarkingVerifier::UnifiedHeapMarkingVerifier( UnifiedHeapMarkingVerifier::UnifiedHeapMarkingVerifier(
cppgc::internal::HeapBase& heap_base) cppgc::internal::HeapBase& heap_base,
cppgc::internal::Heap::Config::CollectionType collection_type)
: MarkingVerifierBase( : MarkingVerifierBase(
heap_base, state_, heap_base, collection_type, state_,
std::make_unique<UnifiedHeapVerificationVisitor>(state_)) {} std::make_unique<UnifiedHeapVerificationVisitor>(state_)) {}
} // namespace internal } // namespace internal
......
...@@ -13,7 +13,8 @@ namespace internal { ...@@ -13,7 +13,8 @@ namespace internal {
class V8_EXPORT_PRIVATE UnifiedHeapMarkingVerifier final class V8_EXPORT_PRIVATE UnifiedHeapMarkingVerifier final
: public cppgc::internal::MarkingVerifierBase { : public cppgc::internal::MarkingVerifierBase {
public: public:
explicit UnifiedHeapMarkingVerifier(cppgc::internal::HeapBase&); UnifiedHeapMarkingVerifier(cppgc::internal::HeapBase&,
cppgc::internal::Heap::Config::CollectionType);
~UnifiedHeapMarkingVerifier() final = default; ~UnifiedHeapMarkingVerifier() final = default;
private: private:
......
...@@ -108,6 +108,31 @@ size_t HeapBase::ExecutePreFinalizers() { ...@@ -108,6 +108,31 @@ size_t HeapBase::ExecutePreFinalizers() {
return prefinalizer_handler_->ExtractBytesAllocatedInPrefinalizers(); return prefinalizer_handler_->ExtractBytesAllocatedInPrefinalizers();
} }
#if defined(CPPGC_YOUNG_GENERATION)
void HeapBase::ResetRememberedSet() {
class AllLABsAreEmpty final : protected HeapVisitor<AllLABsAreEmpty> {
friend class HeapVisitor<AllLABsAreEmpty>;
public:
explicit AllLABsAreEmpty(RawHeap& raw_heap) { Traverse(raw_heap); }
bool value() const { return !some_lab_is_set_; }
protected:
bool VisitNormalPageSpace(NormalPageSpace& space) {
some_lab_is_set_ |= space.linear_allocation_buffer().size();
return true;
}
private:
bool some_lab_is_set_ = false;
};
DCHECK(AllLABsAreEmpty(raw_heap()).value());
caged_heap().local_data().age_table.Reset(&caged_heap().allocator());
remembered_slots().clear();
}
#endif // defined(CPPGC_YOUNG_GENERATION)
void HeapBase::Terminate() { void HeapBase::Terminate() {
DCHECK(!IsMarking()); DCHECK(!IsMarking());
CHECK(!in_disallow_gc_scope()); CHECK(!in_disallow_gc_scope());
......
...@@ -163,7 +163,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -163,7 +163,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
#if defined(CPPGC_YOUNG_GENERATION) #if defined(CPPGC_YOUNG_GENERATION)
std::set<void*>& remembered_slots() { return remembered_slots_; } std::set<void*>& remembered_slots() { return remembered_slots_; }
#endif #endif // defined(CPPGC_YOUNG_GENERATION)
size_t ObjectPayloadSize() const; size_t ObjectPayloadSize() const;
...@@ -218,6 +218,10 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -218,6 +218,10 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
// Returns amount of bytes allocated while executing prefinalizers. // Returns amount of bytes allocated while executing prefinalizers.
size_t ExecutePreFinalizers(); size_t ExecutePreFinalizers();
#if defined(CPPGC_YOUNG_GENERATION)
void ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)
PageAllocator* page_allocator() const; PageAllocator* page_allocator() const;
RawHeap raw_heap_; RawHeap raw_heap_;
......
...@@ -149,7 +149,7 @@ void Heap::StartGarbageCollection(Config config) { ...@@ -149,7 +149,7 @@ void Heap::StartGarbageCollection(Config config) {
#if defined(CPPGC_YOUNG_GENERATION) #if defined(CPPGC_YOUNG_GENERATION)
if (config.collection_type == Config::CollectionType::kMajor) if (config.collection_type == Config::CollectionType::kMajor)
SequentialUnmarker unmarker(raw_heap()); SequentialUnmarker unmarker(raw_heap());
#endif #endif // defined(CPPGC_YOUNG_GENERATION)
const Marker::MarkingConfig marking_config{ const Marker::MarkingConfig marking_config{
config.collection_type, config.stack_state, config.marking_type, config.collection_type, config.stack_state, config.marking_type,
...@@ -177,7 +177,7 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) { ...@@ -177,7 +177,7 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
marker_.reset(); marker_.reset();
const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers(); const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers();
#if CPPGC_VERIFY_HEAP #if CPPGC_VERIFY_HEAP
MarkingVerifier verifier(*this); MarkingVerifier verifier(*this, config_.collection_type);
verifier.Run( verifier.Run(
config_.stack_state, stack_end_of_current_gc(), config_.stack_state, stack_end_of_current_gc(),
stats_collector()->marked_bytes() + bytes_allocated_in_prefinalizers); stats_collector()->marked_bytes() + bytes_allocated_in_prefinalizers);
...@@ -187,6 +187,10 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) { ...@@ -187,6 +187,10 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
#endif #endif
USE(bytes_allocated_in_prefinalizers); USE(bytes_allocated_in_prefinalizers);
#if defined(CPPGC_YOUNG_GENERATION)
ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)
subtle::NoGarbageCollectionScope no_gc(*this); subtle::NoGarbageCollectionScope no_gc(*this);
const Sweeper::SweepingConfig sweeping_config{ const Sweeper::SweepingConfig sweeping_config{
config_.sweeping_type, config_.sweeping_type,
......
...@@ -82,15 +82,6 @@ void VisitRememberedSlots(HeapBase& heap, ...@@ -82,15 +82,6 @@ void VisitRememberedSlots(HeapBase& heap,
#endif #endif
} }
// Assumes that all spaces have their LABs reset.
void ResetRememberedSet(HeapBase& heap) {
#if defined(CPPGC_YOUNG_GENERATION)
auto& local_data = heap.caged_heap().local_data();
local_data.age_table.Reset(&heap.caged_heap().allocator());
heap.remembered_slots().clear();
#endif
}
static constexpr size_t kDefaultDeadlineCheckInterval = 150u; static constexpr size_t kDefaultDeadlineCheckInterval = 150u;
template <size_t kDeadlineCheckInterval = kDefaultDeadlineCheckInterval, template <size_t kDeadlineCheckInterval = kDefaultDeadlineCheckInterval,
...@@ -294,7 +285,6 @@ void MarkerBase::LeaveAtomicPause() { ...@@ -294,7 +285,6 @@ void MarkerBase::LeaveAtomicPause() {
StatsCollector::EnabledScope stats_scope( StatsCollector::EnabledScope stats_scope(
heap().stats_collector(), StatsCollector::kMarkAtomicEpilogue); heap().stats_collector(), StatsCollector::kMarkAtomicEpilogue);
DCHECK(!incremental_marking_handle_); DCHECK(!incremental_marking_handle_);
ResetRememberedSet(heap());
heap().stats_collector()->NotifyMarkingCompleted( heap().stats_collector()->NotifyMarkingCompleted(
// GetOverallMarkedBytes also includes concurrently marked bytes. // GetOverallMarkedBytes also includes concurrently marked bytes.
schedule_.GetOverallMarkedBytes()); schedule_.GetOverallMarkedBytes());
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/heap/cppgc/marking-verifier.h" #include "src/heap/cppgc/marking-verifier.h"
#include "include/cppgc/internal/caged-heap-local-data.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/heap/cppgc/gc-info-table.h" #include "src/heap/cppgc/gc-info-table.h"
#include "src/heap/cppgc/heap-object-header.h" #include "src/heap/cppgc/heap-object-header.h"
...@@ -14,12 +15,31 @@ ...@@ -14,12 +15,31 @@
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
void VerificationState::VerifyMarked(const void* base_object_payload) const {
const HeapObjectHeader& child_header =
HeapObjectHeader::FromObject(base_object_payload);
if (!child_header.IsMarked()) {
FATAL(
"MarkingVerifier: Encountered unmarked object.\n"
"#\n"
"# Hint:\n"
"# %s (%p)\n"
"# \\-> %s (%p)",
parent_ ? parent_->GetName().value : "Stack",
parent_ ? parent_->ObjectStart() : nullptr,
child_header.GetName().value, child_header.ObjectStart());
}
}
MarkingVerifierBase::MarkingVerifierBase( MarkingVerifierBase::MarkingVerifierBase(
HeapBase& heap, VerificationState& verification_state, HeapBase& heap, Heap::Config::CollectionType collection_type,
VerificationState& verification_state,
std::unique_ptr<cppgc::Visitor> visitor) std::unique_ptr<cppgc::Visitor> visitor)
: ConservativeTracingVisitor(heap, *heap.page_backend(), *visitor.get()), : ConservativeTracingVisitor(heap, *heap.page_backend(), *visitor.get()),
verification_state_(verification_state), verification_state_(verification_state),
visitor_(std::move(visitor)) {} visitor_(std::move(visitor)),
collection_type_(collection_type) {}
void MarkingVerifierBase::Run( void MarkingVerifierBase::Run(
Heap::Config::StackState stack_state, uintptr_t stack_end, Heap::Config::StackState stack_state, uintptr_t stack_end,
...@@ -38,25 +58,8 @@ void MarkingVerifierBase::Run( ...@@ -38,25 +58,8 @@ void MarkingVerifierBase::Run(
in_construction_objects_heap_.find(header)); in_construction_objects_heap_.find(header));
} }
} }
if (expected_marked_bytes) { if (expected_marked_bytes && verifier_found_marked_bytes_are_exact_) {
CHECK_EQ(expected_marked_bytes.value(), found_marked_bytes_); CHECK_EQ(expected_marked_bytes.value(), verifier_found_marked_bytes_);
}
}
void VerificationState::VerifyMarked(const void* base_object_payload) const {
const HeapObjectHeader& child_header =
HeapObjectHeader::FromObject(base_object_payload);
if (!child_header.IsMarked()) {
FATAL(
"MarkingVerifier: Encountered unmarked object.\n"
"#\n"
"# Hint:\n"
"# %s (%p)\n"
"# \\-> %s (%p)",
parent_ ? parent_->GetName().value : "Stack",
parent_ ? parent_->ObjectStart() : nullptr,
child_header.GetName().value, child_header.ObjectStart());
} }
} }
...@@ -93,6 +96,24 @@ bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) { ...@@ -93,6 +96,24 @@ bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) {
DCHECK(!header.IsFree()); DCHECK(!header.IsFree());
#if defined(CPPGC_YOUNG_GENERATION)
if (collection_type_ == Heap::Config::CollectionType::kMinor) {
const auto age = heap_.caged_heap()
.local_data()
.age_table[heap_.caged_heap().OffsetFromAddress(
header.ObjectStart())];
if (age == AgeTable::Age::kOld) {
// Do not verify old objects.
return true;
} else if (age == AgeTable::Age::kUnknown) {
// If the age is not known, the marked bytes may not be exact as possibly
// old objects are verified as well.
verifier_found_marked_bytes_are_exact_ = false;
}
// Verify young and unknown objects.
}
#endif // defined(CPPGC_YOUNG_GENERATION)
verification_state_.SetCurrentParent(&header); verification_state_.SetCurrentParent(&header);
if (!header.IsInConstruction()) { if (!header.IsInConstruction()) {
...@@ -102,7 +123,8 @@ bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) { ...@@ -102,7 +123,8 @@ bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) {
TraceConservativelyIfNeeded(header); TraceConservativelyIfNeeded(header);
} }
found_marked_bytes_ += ObjectView(header).Size() + sizeof(HeapObjectHeader); verifier_found_marked_bytes_ +=
ObjectView(header).Size() + sizeof(HeapObjectHeader);
verification_state_.SetCurrentParent(nullptr); verification_state_.SetCurrentParent(nullptr);
...@@ -146,8 +168,9 @@ class VerificationVisitor final : public cppgc::Visitor { ...@@ -146,8 +168,9 @@ class VerificationVisitor final : public cppgc::Visitor {
} // namespace } // namespace
MarkingVerifier::MarkingVerifier(HeapBase& heap_base) MarkingVerifier::MarkingVerifier(HeapBase& heap_base,
: MarkingVerifierBase(heap_base, state_, Heap::Config::CollectionType collection_type)
: MarkingVerifierBase(heap_base, collection_type, state_,
std::make_unique<VerificationVisitor>(state_)) {} std::make_unique<VerificationVisitor>(state_)) {}
} // namespace internal } // namespace internal
......
...@@ -44,8 +44,8 @@ class V8_EXPORT_PRIVATE MarkingVerifierBase ...@@ -44,8 +44,8 @@ class V8_EXPORT_PRIVATE MarkingVerifierBase
void Run(Heap::Config::StackState, uintptr_t, v8::base::Optional<size_t>); void Run(Heap::Config::StackState, uintptr_t, v8::base::Optional<size_t>);
protected: protected:
MarkingVerifierBase(HeapBase&, VerificationState&, MarkingVerifierBase(HeapBase&, Heap::Config::CollectionType,
std::unique_ptr<cppgc::Visitor>); VerificationState&, std::unique_ptr<cppgc::Visitor>);
private: private:
void VisitInConstructionConservatively(HeapObjectHeader&, void VisitInConstructionConservatively(HeapObjectHeader&,
...@@ -61,12 +61,14 @@ class V8_EXPORT_PRIVATE MarkingVerifierBase ...@@ -61,12 +61,14 @@ class V8_EXPORT_PRIVATE MarkingVerifierBase
std::unordered_set<const HeapObjectHeader*> in_construction_objects_stack_; std::unordered_set<const HeapObjectHeader*> in_construction_objects_stack_;
std::unordered_set<const HeapObjectHeader*>* in_construction_objects_ = std::unordered_set<const HeapObjectHeader*>* in_construction_objects_ =
&in_construction_objects_heap_; &in_construction_objects_heap_;
size_t found_marked_bytes_ = 0; size_t verifier_found_marked_bytes_ = 0;
bool verifier_found_marked_bytes_are_exact_ = true;
Heap::Config::CollectionType collection_type_;
}; };
class V8_EXPORT_PRIVATE MarkingVerifier final : public MarkingVerifierBase { class V8_EXPORT_PRIVATE MarkingVerifier final : public MarkingVerifierBase {
public: public:
explicit MarkingVerifier(HeapBase&); MarkingVerifier(HeapBase&, Heap::Config::CollectionType);
~MarkingVerifier() final = default; ~MarkingVerifier() final = default;
private: private:
......
...@@ -25,7 +25,7 @@ class MarkingVerifierTest : public testing::TestWithHeap { ...@@ -25,7 +25,7 @@ class MarkingVerifierTest : public testing::TestWithHeap {
V8_NOINLINE void VerifyMarking(HeapBase& heap, StackState stack_state, V8_NOINLINE void VerifyMarking(HeapBase& heap, StackState stack_state,
size_t expected_marked_bytes) { size_t expected_marked_bytes) {
Heap::From(GetHeap())->object_allocator().ResetLinearAllocationBuffers(); Heap::From(GetHeap())->object_allocator().ResetLinearAllocationBuffers();
MarkingVerifier verifier(heap); MarkingVerifier verifier(heap, Heap::Config::CollectionType::kMajor);
verifier.Run(stack_state, v8::base::Stack::GetCurrentStackPosition(), verifier.Run(stack_state, v8::base::Stack::GetCurrentStackPosition(),
expected_marked_bytes); expected_marked_bytes);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "include/cppgc/allocation.h" #include "include/cppgc/allocation.h"
#include "include/cppgc/heap-consistency.h" #include "include/cppgc/heap-consistency.h"
#include "include/cppgc/internal/caged-heap-local-data.h"
#include "include/cppgc/persistent.h" #include "include/cppgc/persistent.h"
#include "src/heap/cppgc/heap-object-header.h" #include "src/heap/cppgc/heap-object-header.h"
#include "src/heap/cppgc/heap.h" #include "src/heap/cppgc/heap.h"
...@@ -136,6 +137,7 @@ TYPED_TEST(MinorGCTestForType, OldObjectIsNotVisited) { ...@@ -136,6 +137,7 @@ TYPED_TEST(MinorGCTestForType, OldObjectIsNotVisited) {
template <typename Type1, typename Type2> template <typename Type1, typename Type2>
void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) { void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) {
auto* internal_heap = Heap::From(heap);
Persistent<Type1> old = Persistent<Type1> old =
MakeGarbageCollected<Type1>(heap->GetAllocationHandle()); MakeGarbageCollected<Type1>(heap->GetAllocationHandle());
test->CollectMinor(); test->CollectMinor();
...@@ -152,6 +154,11 @@ void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) { ...@@ -152,6 +154,11 @@ void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) {
ptr->next = young; ptr->next = young;
young = ptr; young = ptr;
EXPECT_TRUE(HeapObjectHeader::FromObject(young).IsYoung()); EXPECT_TRUE(HeapObjectHeader::FromObject(young).IsYoung());
const uintptr_t offset =
internal_heap->caged_heap().OffsetFromAddress(young);
// Age may be young or unknown.
EXPECT_NE(AgeTable::Age::kOld,
Heap::From(heap)->caged_heap().local_data().age_table[offset]);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment