1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/cppgc/marking-verifier.h"
6
7 #include "include/cppgc/internal/caged-heap-local-data.h"
8 #include "src/base/logging.h"
9 #include "src/heap/cppgc/gc-info-table.h"
10 #include "src/heap/cppgc/heap-object-header.h"
11 #include "src/heap/cppgc/heap.h"
12 #include "src/heap/cppgc/marking-visitor.h"
13 #include "src/heap/cppgc/object-view.h"
14
15 namespace cppgc {
16 namespace internal {
17
VerifyMarked(const void * base_object_payload) const18 void VerificationState::VerifyMarked(const void* base_object_payload) const {
19 const HeapObjectHeader& child_header =
20 HeapObjectHeader::FromObject(base_object_payload);
21
22 if (!child_header.IsMarked()) {
23 FATAL(
24 "MarkingVerifier: Encountered unmarked object.\n"
25 "#\n"
26 "# Hint:\n"
27 "# %s (%p)\n"
28 "# \\-> %s (%p)",
29 parent_ ? parent_->GetName().value : "Stack",
30 parent_ ? parent_->ObjectStart() : nullptr,
31 child_header.GetName().value, child_header.ObjectStart());
32 }
33 }
34
MarkingVerifierBase(HeapBase & heap,Heap::Config::CollectionType collection_type,VerificationState & verification_state,std::unique_ptr<cppgc::Visitor> visitor)35 MarkingVerifierBase::MarkingVerifierBase(
36 HeapBase& heap, Heap::Config::CollectionType collection_type,
37 VerificationState& verification_state,
38 std::unique_ptr<cppgc::Visitor> visitor)
39 : ConservativeTracingVisitor(heap, *heap.page_backend(), *visitor.get()),
40 verification_state_(verification_state),
41 visitor_(std::move(visitor)),
42 collection_type_(collection_type) {}
43
Run(Heap::Config::StackState stack_state,uintptr_t stack_end,v8::base::Optional<size_t> expected_marked_bytes)44 void MarkingVerifierBase::Run(
45 Heap::Config::StackState stack_state, uintptr_t stack_end,
46 v8::base::Optional<size_t> expected_marked_bytes) {
47 Traverse(heap_.raw_heap());
48 // Avoid verifying the stack when running with TSAN as the TSAN runtime changes
49 // stack contents when e.g. working with locks. Specifically, the marker uses
50 // locks in slow path operations which results in stack changes throughout
51 // marking. This means that the conservative iteration below may find more
52 // objects then the regular marker. The difference is benign as the delta of
53 // objects is not reachable from user code but it prevents verification.
54 #if !defined(THREAD_SANITIZER)
55 if (stack_state == Heap::Config::StackState::kMayContainHeapPointers) {
56 in_construction_objects_ = &in_construction_objects_stack_;
57 heap_.stack()->IteratePointersUnsafe(this, stack_end);
58 // The objects found through the unsafe iteration are only a subset of the
59 // regular iteration as they miss objects held alive only from callee-saved
60 // registers that are never pushed on the stack and SafeStack.
61 CHECK_LE(in_construction_objects_stack_.size(),
62 in_construction_objects_heap_.size());
63 for (auto* header : in_construction_objects_stack_) {
64 CHECK_NE(in_construction_objects_heap_.end(),
65 in_construction_objects_heap_.find(header));
66 }
67 }
68 #endif // !defined(THREAD_SANITIZER)
69 if (expected_marked_bytes && verifier_found_marked_bytes_are_exact_) {
70 CHECK_EQ(expected_marked_bytes.value(), verifier_found_marked_bytes_);
71 }
72 }
73
VisitInConstructionConservatively(HeapObjectHeader & header,TraceConservativelyCallback callback)74 void MarkingVerifierBase::VisitInConstructionConservatively(
75 HeapObjectHeader& header, TraceConservativelyCallback callback) {
76 if (in_construction_objects_->find(&header) !=
77 in_construction_objects_->end())
78 return;
79 in_construction_objects_->insert(&header);
80
81 // Stack case: Parent is stack and this is merely ensuring that the object
82 // itself is marked. If the object is marked, then it is being processed by
83 // the on-heap phase.
84 if (verification_state_.IsParentOnStack()) {
85 verification_state_.VerifyMarked(header.ObjectStart());
86 return;
87 }
88
89 // Heap case: Dispatching parent object that must be marked (pre-condition).
90 CHECK(header.IsMarked());
91 callback(this, header);
92 }
93
VisitPointer(const void * address)94 void MarkingVerifierBase::VisitPointer(const void* address) {
95 // Entry point for stack walk. The conservative visitor dispatches as follows:
96 // - Fully constructed objects: Visit()
97 // - Objects in construction: VisitInConstructionConservatively()
98 TraceConservativelyIfNeeded(address);
99 }
100
VisitHeapObjectHeader(HeapObjectHeader & header)101 bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) {
102 // Verify only non-free marked objects.
103 if (!header.IsMarked()) return true;
104
105 DCHECK(!header.IsFree());
106
107 #if defined(CPPGC_YOUNG_GENERATION)
108 if (collection_type_ == Heap::Config::CollectionType::kMinor) {
109 const auto age = heap_.caged_heap().local_data().age_table.GetAge(
110 heap_.caged_heap().OffsetFromAddress(header.ObjectStart()));
111 if (age == AgeTable::Age::kOld) {
112 // Do not verify old objects.
113 return true;
114 } else if (age == AgeTable::Age::kMixed) {
115 // If the age is not known, the marked bytes may not be exact as possibly
116 // old objects are verified as well.
117 verifier_found_marked_bytes_are_exact_ = false;
118 }
119 // Verify young and unknown objects.
120 }
121 #endif // defined(CPPGC_YOUNG_GENERATION)
122
123 verification_state_.SetCurrentParent(&header);
124
125 if (!header.IsInConstruction()) {
126 header.Trace(visitor_.get());
127 } else {
128 // Dispatches to conservative tracing implementation.
129 TraceConservativelyIfNeeded(header);
130 }
131
132 verifier_found_marked_bytes_ +=
133 ObjectView<>(header).Size() + sizeof(HeapObjectHeader);
134
135 verification_state_.SetCurrentParent(nullptr);
136
137 return true;
138 }
139
140 namespace {
141
142 class VerificationVisitor final : public cppgc::Visitor {
143 public:
VerificationVisitor(VerificationState & state)144 explicit VerificationVisitor(VerificationState& state)
145 : cppgc::Visitor(VisitorFactory::CreateKey()), state_(state) {}
146
Visit(const void *,TraceDescriptor desc)147 void Visit(const void*, TraceDescriptor desc) final {
148 state_.VerifyMarked(desc.base_object_payload);
149 }
150
VisitWeak(const void *,TraceDescriptor desc,WeakCallback,const void *)151 void VisitWeak(const void*, TraceDescriptor desc, WeakCallback,
152 const void*) final {
153 // Weak objects should have been cleared at this point. As a consequence,
154 // all objects found through weak references have to point to live objects
155 // at this point.
156 state_.VerifyMarked(desc.base_object_payload);
157 }
158
VisitWeakContainer(const void * object,TraceDescriptor,TraceDescriptor weak_desc,WeakCallback,const void *)159 void VisitWeakContainer(const void* object, TraceDescriptor,
160 TraceDescriptor weak_desc, WeakCallback,
161 const void*) final {
162 if (!object) return;
163
164 // Contents of weak containers are found themselves through page iteration
165 // and are treated strongly, similar to how they are treated strongly when
166 // found through stack scanning. The verification here only makes sure that
167 // the container itself is properly marked.
168 state_.VerifyMarked(weak_desc.base_object_payload);
169 }
170
171 private:
172 VerificationState& state_;
173 };
174
175 } // namespace
176
MarkingVerifier(HeapBase & heap_base,Heap::Config::CollectionType collection_type)177 MarkingVerifier::MarkingVerifier(HeapBase& heap_base,
178 Heap::Config::CollectionType collection_type)
179 : MarkingVerifierBase(heap_base, collection_type, state_,
180 std::make_unique<VerificationVisitor>(state_)) {}
181
182 } // namespace internal
183 } // namespace cppgc
184