v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
marking-verifier.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
9#include "src/base/logging.h"
13
14#if defined(CPPGC_CAGED_HEAP)
16#endif // defined(CPPGC_CAGED_HEAP)
17
18namespace cppgc {
19namespace internal {
20
21void VerificationState::VerifyMarked(const void* base_object_payload) const {
22 const HeapObjectHeader& child_header =
23 HeapObjectHeader::FromObject(base_object_payload);
24
25 if (!child_header.IsMarked()) {
26 FATAL(
27 "MarkingVerifier: Encountered unmarked object.\n"
28 "#\n"
29 "# Hint:\n"
30 "# %s (%p)\n"
31 "# \\-> %s (%p)",
33 ? parent_
34 ->GetName(
36 .value
37 : "Stack",
38 parent_ ? parent_->ObjectStart() : nullptr,
39 child_header
41 .value,
42 child_header.ObjectStart());
43 }
44}
45
47 HeapBase& heap, CollectionType collection_type,
48 VerificationState& verification_state,
49 std::unique_ptr<cppgc::Visitor> visitor)
50 : ConservativeTracingVisitor(heap, *heap.page_backend(), *visitor),
51 verification_state_(verification_state),
52 visitor_(std::move(visitor)),
53 collection_type_(collection_type) {}
54
56 std::optional<size_t> expected_marked_bytes) {
58// Avoid verifying the stack when running with TSAN as the TSAN runtime changes
59// stack contents when e.g. working with locks. Specifically, the marker uses
60// locks in slow path operations which results in stack changes throughout
61// marking. This means that the conservative iteration below may find more
62// objects then the regular marker. The difference is benign as the delta of
63// objects is not reachable from user code but it prevents verification.
64// We also avoid verifying the stack when pointer compression is enabled.
65// Currently, verification happens after compaction, V8 compaction can change
66// slots on stack, which could lead to false positives in verifier. Those are
67// more likely with checking compressed pointers on stack.
68// TODO(chromium:1325007): Investigate if Oilpan verification can be moved
69// before V8 compaction or compaction never runs with stack.
70#if !defined(THREAD_SANITIZER) && !defined(CPPGC_POINTER_COMPRESSION)
71 if (stack_state == StackState::kMayContainHeapPointers) {
74 // The objects found through the unsafe iteration are only a subset of the
75 // regular iteration as they miss objects held alive only from callee-saved
76 // registers that are never pushed on the stack and SafeStack.
79 for (auto* header : in_construction_objects_stack_) {
82 }
83 }
84#endif // !defined(THREAD_SANITIZER)
85 if (expected_marked_bytes && verifier_found_marked_bytes_are_exact_) {
86 // Report differences in marked objects, if possible.
87 if (V8_UNLIKELY(expected_marked_bytes.value() !=
90 ReportDifferences(expected_marked_bytes.value());
91 }
92 CHECK_EQ(expected_marked_bytes.value(), verifier_found_marked_bytes_);
93 // Minor GCs use sticky markbits and as such cannot expect that the marked
94 // bytes on pages match the marked bytes accumulated by the marker.
96 CHECK_EQ(expected_marked_bytes.value(),
98 }
99 }
100}
101
104 if (in_construction_objects_->find(&header) !=
106 return;
107 in_construction_objects_->insert(&header);
108
109 // Stack case: Parent is stack and this is merely ensuring that the object
110 // itself is marked. If the object is marked, then it is being processed by
111 // the on-heap phase.
114 return;
115 }
116
117 // Heap case: Dispatching parent object that must be marked (pre-condition).
118 CHECK(header.IsMarked());
119 callback(this, header);
120}
121
122void MarkingVerifierBase::VisitPointer(const void* address) {
123 // Entry point for stack walk. The conservative visitor dispatches as follows:
124 // - Fully constructed objects: Visit()
125 // - Objects in construction: VisitInConstructionConservatively()
127}
128
130 verifier_found_marked_bytes_in_pages_ += page.marked_bytes();
131 return false; // Continue visitation.
132}
133
135 verifier_found_marked_bytes_in_pages_ += page.marked_bytes();
136 return false; // Continue visitation.
137}
138
140 // Verify only non-free marked objects.
141 if (!header.IsMarked()) return true;
142
143 DCHECK(!header.IsFree());
144
145#if defined(CPPGC_YOUNG_GENERATION)
147 auto& caged_heap = CagedHeap::Instance();
148 const auto age = CagedHeapLocalData::Get().age_table.GetAge(
149 caged_heap.OffsetFromAddress(header.ObjectStart()));
150 if (age == AgeTable::Age::kOld) {
151 // Do not verify old objects.
152 return true;
153 } else if (age == AgeTable::Age::kMixed) {
154 // If the age is not known, the marked bytes may not be exact as possibly
155 // old objects are verified as well.
157 }
158 // Verify young and unknown objects.
159 }
160#endif // defined(CPPGC_YOUNG_GENERATION)
161
163
164 if (!header.IsInConstruction()) {
165 header.TraceImpl(visitor_.get());
166 } else {
167 // Dispatches to conservative tracing implementation.
169 }
170
172 ObjectView<>(header).Size() + sizeof(HeapObjectHeader);
173
175
176 return true;
177}
178
180 size_t expected_marked_bytes) const {
181 v8::base::OS::PrintError("\n<--- Mismatch in marking verifier --->\n");
182 v8::base::OS::PrintError(
183 "Marked bytes: expected %zu vs. verifier found %zu, difference %zd\n",
184 expected_marked_bytes, verifier_found_marked_bytes_,
185 expected_marked_bytes - verifier_found_marked_bytes_);
186 v8::base::OS::PrintError(
187 "A list of pages with possibly mismatched marked objects follows.\n");
188 for (auto& space : heap_.raw_heap()) {
189 for (auto* page : *space) {
190 size_t marked_bytes_on_page = 0;
191 if (page->is_large()) {
192 const auto& large_page = *LargePage::From(page);
193 const auto& header = *large_page.ObjectHeader();
194 if (header.IsMarked())
195 marked_bytes_on_page +=
196 ObjectView<>(header).Size() + sizeof(HeapObjectHeader);
197 if (marked_bytes_on_page == large_page.marked_bytes()) continue;
198 ReportLargePage(large_page, marked_bytes_on_page);
200 } else {
201 const auto& normal_page = *NormalPage::From(page);
202 for (const auto& header : normal_page) {
203 if (header.IsMarked())
204 marked_bytes_on_page +=
205 ObjectView<>(header).Size() + sizeof(HeapObjectHeader);
206 }
207 if (marked_bytes_on_page == normal_page.marked_bytes()) continue;
208 ReportNormalPage(normal_page, marked_bytes_on_page);
209 for (const auto& header : normal_page) {
211 }
212 }
213 }
214 }
215}
216
218 size_t marked_bytes_on_page) const {
219 v8::base::OS::PrintError(
220 "\nNormal page in space %zu:\n"
221 "Marked bytes: expected %zu vs. verifier found %zu, difference %zd\n",
222 page.space().index(), page.marked_bytes(), marked_bytes_on_page,
223 page.marked_bytes() - marked_bytes_on_page);
224}
225
227 size_t marked_bytes_on_page) const {
228 v8::base::OS::PrintError(
229 "\nLarge page in space %zu:\n"
230 "Marked bytes: expected %zu vs. verifier found %zu, difference %zd\n",
231 page.space().index(), page.marked_bytes(), marked_bytes_on_page,
232 page.marked_bytes() - marked_bytes_on_page);
233}
234
236 const HeapObjectHeader& header) const {
237 const char* name =
238 header.IsFree()
239 ? "free space"
240 : header
241 .GetName(
243 .value;
244 v8::base::OS::PrintError("- %s at %p, size %zu, %s\n", name,
245 header.ObjectStart(), header.ObjectSize(),
246 header.IsMarked() ? "marked" : "unmarked");
247}
248
249namespace {
250
251class VerificationVisitor final : public cppgc::Visitor {
252 public:
253 explicit VerificationVisitor(VerificationState& state)
254 : cppgc::Visitor(VisitorFactory::CreateKey()), state_(state) {}
255
256 void Visit(const void*, TraceDescriptor desc) final {
257 state_.VerifyMarked(desc.base_object_payload);
258 }
259
260 void VisitWeak(const void*, TraceDescriptor desc, WeakCallback,
261 const void*) final {
262 // Weak objects should have been cleared at this point. As a consequence,
263 // all objects found through weak references have to point to live objects
264 // at this point.
265 state_.VerifyMarked(desc.base_object_payload);
266 }
267
268 void VisitWeakContainer(const void* object, TraceDescriptor,
269 TraceDescriptor weak_desc, WeakCallback,
270 const void*) final {
271 if (!object) return;
272
273 // Contents of weak containers are found themselves through page iteration
274 // and are treated strongly, similar to how they are treated strongly when
275 // found through stack scanning. The verification here only makes sure that
276 // the container itself is properly marked.
277 state_.VerifyMarked(weak_desc.base_object_payload);
278 }
279
280 private:
281 VerificationState& state_;
282};
283
284} // namespace
285
287 CollectionType collection_type)
288 : MarkingVerifierBase(heap_base, collection_type, state_,
289 std::make_unique<VerificationVisitor>(state_)) {}
290
291} // namespace internal
292} // namespace cppgc
static CagedHeap & Instance()
Definition caged-heap.cc:93
void(ConservativeTracingVisitor *, const HeapObjectHeader &) TraceConservativelyCallback
Definition visitor.h:63
virtual void TraceConservativelyIfNeeded(const void *)
Definition visitor.cc:96
virtual heap::base::Stack * stack()
Definition heap-base.h:174
static HeapObjectHeader & FromObject(void *address)
V8_EXPORT_PRIVATE HeapObjectName GetName() const
static LargePage * From(BasePage *page)
Definition heap-page.h:275
void VisitInConstructionConservatively(HeapObjectHeader &, TraceConservativelyCallback) final
MarkingVerifierBase(const MarkingVerifierBase &)=delete
std::unordered_set< const HeapObjectHeader * > * in_construction_objects_
void ReportNormalPage(const NormalPage &, size_t) const
void Run(StackState, std::optional< size_t >)
std::unique_ptr< cppgc::Visitor > visitor_
std::unordered_set< const HeapObjectHeader * > in_construction_objects_heap_
std::unordered_set< const HeapObjectHeader * > in_construction_objects_stack_
void ReportHeapObjectHeader(const HeapObjectHeader &) const
void ReportLargePage(const LargePage &, size_t) const
bool VisitHeapObjectHeader(HeapObjectHeader &)
MarkingVerifier(HeapBase &, CollectionType)
static NormalPage * From(BasePage *page)
Definition heap-page.h:205
V8_INLINE size_t Size() const
Definition object-view.h:54
void VerifyMarked(const void *) const
const HeapObjectHeader * parent_
void SetCurrentParent(const HeapObjectHeader *header)
void IteratePointersUntilMarker(StackVisitor *visitor) const
Definition stack.cc:161
CppHeap::CollectionType collection_type_
Definition cpp-heap.cc:219
enum v8::internal::@1270::DeoptimizableCodeIterator::@67 state_
TNode< Object > callback
EmbedderStackState
Definition common.h:15
void(*)(const LivenessBroker &, const void *) WeakCallback
Definition visitor.h:37
STL namespace.
#define FATAL(...)
Definition logging.h:47
#define CHECK(condition)
Definition logging.h:124
#define CHECK_LE(lhs, rhs)
#define CHECK_NE(lhs, rhs)
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
RootVisitor * visitor_
#define V8_UNLIKELY(condition)
Definition v8config.h:660