v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
heap-verifier.cc
Go to the documentation of this file.
1// Copyright 2022 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 "include/v8-locker.h"
10#include "src/base/logging.h"
13#include "src/common/globals.h"
21#include "src/heap/heap.h"
26#include "src/heap/new-spaces.h"
31#include "src/heap/safepoint.h"
33#include "src/objects/code.h"
37
38#ifdef VERIFY_HEAP
39namespace v8 {
40namespace internal {
41
42namespace {
43thread_local Tagged<HeapObject> pending_layout_change_object =
45} // namespace
46
47// Verify that all objects are Smis.
48class VerifySmisVisitor final : public RootVisitor {
49 public:
50 void VisitRootPointers(Root root, const char* description,
51 FullObjectSlot start, FullObjectSlot end) final {
52 for (FullObjectSlot current = start; current < end; ++current) {
53 CHECK(IsSmi(*current));
54 }
55 }
56};
57
58// Visitor class to verify interior pointers in spaces that do not contain
59// or care about inter-generational references. All heap object pointers have to
60// point into the heap to a location that has a map pointer at its first word.
61// Caveat: Heap::Contains is an approximation because it can return true for
62// objects in a heap space but above the allocation pointer.
63class VerifyPointersVisitor : public HeapVisitor<VerifyPointersVisitor>,
64 public RootVisitor {
65 public:
66 V8_INLINE explicit VerifyPointersVisitor(Heap* heap)
67 : HeapVisitor(heap), heap_(heap) {}
68
69 void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
70 ObjectSlot end) override;
71 void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
72 MaybeObjectSlot end) override;
73 void VisitInstructionStreamPointer(Tagged<Code> host,
74 InstructionStreamSlot slot) override;
75 void VisitCodeTarget(Tagged<InstructionStream> host,
76 RelocInfo* rinfo) override;
77 void VisitEmbeddedPointer(Tagged<InstructionStream> host,
78 RelocInfo* rinfo) override;
79
80 void VisitRootPointers(Root root, const char* description,
81 FullObjectSlot start, FullObjectSlot end) override;
82 void VisitRootPointers(Root root, const char* description,
83 OffHeapObjectSlot start,
84 OffHeapObjectSlot end) override;
85 void VisitMapPointer(Tagged<HeapObject> host) override;
86
87 protected:
88 V8_INLINE void VerifyHeapObjectImpl(Tagged<HeapObject> heap_object);
89 V8_INLINE void VerifyCodeObjectImpl(Tagged<HeapObject> heap_object);
90
91 template <typename TSlot>
92 V8_INLINE void VerifyPointersImpl(TSlot start, TSlot end);
93
94 virtual void VerifyPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
95 MaybeObjectSlot end);
96
97 Heap* heap_;
98};
99
100void VerifyPointersVisitor::VisitPointers(Tagged<HeapObject> host,
102 VerifyPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
103}
104
105void VerifyPointersVisitor::VisitPointers(Tagged<HeapObject> host,
108 VerifyPointers(host, start, end);
109}
110
111void VerifyPointersVisitor::VisitInstructionStreamPointer(
113 Tagged<Object> maybe_code = slot.load(code_cage_base());
115 // The slot might contain smi during Code creation.
116 if (maybe_code.GetHeapObject(&code)) {
117 VerifyCodeObjectImpl(code);
118 } else {
119 CHECK(IsSmi(maybe_code));
120 }
121}
122
123void VerifyPointersVisitor::VisitRootPointers(Root root,
124 const char* description,
125 FullObjectSlot start,
126 FullObjectSlot end) {
127 VerifyPointersImpl(start, end);
128}
129
130void VerifyPointersVisitor::VisitRootPointers(Root root,
131 const char* description,
134 VerifyPointersImpl(start, end);
135}
136
137void VerifyPointersVisitor::VisitMapPointer(Tagged<HeapObject> host) {
138 VerifyHeapObjectImpl(host->map(cage_base()));
139}
140
141void VerifyPointersVisitor::VerifyHeapObjectImpl(
142 Tagged<HeapObject> heap_object) {
143 CHECK(IsValidHeapObject(heap_, heap_object));
144 CHECK(IsMap(heap_object->map(cage_base())));
145 // Heap::InToPage() is not available with sticky mark-bits.
147 !v8_flags.sticky_mark_bits && HeapLayout::InYoungGeneration(heap_object),
148 Heap::InToPage(heap_object));
149}
150
151void VerifyPointersVisitor::VerifyCodeObjectImpl(
152 Tagged<HeapObject> heap_object) {
153 CHECK(IsValidCodeObject(heap_, heap_object));
154 CHECK(IsMap(heap_object->map(cage_base())));
155 CHECK(heap_object->map(cage_base())->instance_type() ==
156 INSTRUCTION_STREAM_TYPE);
157}
158
159template <typename TSlot>
160void VerifyPointersVisitor::VerifyPointersImpl(TSlot start, TSlot end) {
161 for (TSlot slot = start; slot < end; ++slot) {
162 typename TSlot::TObject object = slot.load(cage_base());
163#ifdef V8_ENABLE_DIRECT_HANDLE
164 if (object.ptr() == kTaggedNullAddress) continue;
165#endif
166 Tagged<HeapObject> heap_object;
167 if (object.GetHeapObject(&heap_object)) {
168 VerifyHeapObjectImpl(heap_object);
169 } else {
170 CHECK(IsSmi(object) || object.IsCleared() ||
171 MapWord::IsPacked(object.ptr()));
172 }
173 }
174}
175
176void VerifyPointersVisitor::VerifyPointers(Tagged<HeapObject> host,
179 // If this CHECK fires then you probably added a pointer field
180 // to one of objects in DATA_ONLY_VISITOR_ID_LIST. You can fix
181 // this by moving that object to POINTER_VISITOR_ID_LIST.
183 Map::ObjectFieldsFrom(host->map(cage_base())->visitor_id()));
184 VerifyPointersImpl(start, end);
185}
186
187void VerifyPointersVisitor::VisitCodeTarget(Tagged<InstructionStream> host,
188 RelocInfo* rinfo) {
190 InstructionStream::FromTargetAddress(rinfo->target_address());
191 VerifyHeapObjectImpl(target);
192}
193
194void VerifyPointersVisitor::VisitEmbeddedPointer(Tagged<InstructionStream> host,
195 RelocInfo* rinfo) {
196 VerifyHeapObjectImpl(rinfo->target_object(cage_base()));
197}
198
199class VerifyReadOnlyPointersVisitor : public VerifyPointersVisitor {
200 public:
201 explicit VerifyReadOnlyPointersVisitor(Heap* heap)
202 : VerifyPointersVisitor(heap) {}
203
204 protected:
205 void VerifyPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
206 MaybeObjectSlot end) override {
207 if (!host.is_null()) {
208 CHECK(ReadOnlyHeap::Contains(host->map()));
209 }
210 VerifyPointersVisitor::VerifyPointers(host, start, end);
211
212 for (MaybeObjectSlot current = start; current < end; ++current) {
213 Tagged<HeapObject> heap_object;
214 if ((*current).GetHeapObject(&heap_object)) {
215 CHECK(ReadOnlyHeap::Contains(heap_object));
216 }
217 }
218 }
219};
220
221class VerifySharedHeapObjectVisitor : public VerifyPointersVisitor {
222 public:
223 explicit VerifySharedHeapObjectVisitor(Heap* heap)
224 : VerifyPointersVisitor(heap),
225 shared_space_(heap->shared_space()),
226 shared_trusted_space_(heap->shared_trusted_space()),
227 shared_lo_space_(heap->shared_lo_space()),
228 shared_trusted_lo_space_(heap->shared_trusted_lo_space()) {
229 CHECK_NOT_NULL(shared_space_);
230 CHECK_NOT_NULL(shared_lo_space_);
231 }
232
233 protected:
234 void VerifyPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
235 MaybeObjectSlot end) override {
236 if (!host.is_null()) {
237 Tagged<Map> map = host->map();
238 CHECK(ReadOnlyHeap::Contains(map) || shared_space_->Contains(map));
239 }
240 VerifyPointersVisitor::VerifyPointers(host, start, end);
241
242 for (MaybeObjectSlot current = start; current < end; ++current) {
243 Tagged<HeapObject> heap_object;
244 if ((*current).GetHeapObject(&heap_object)) {
245 MemoryChunk* chunk = MemoryChunk::FromHeapObject(heap_object);
246 CHECK(chunk->InReadOnlySpace() || chunk->InWritableSharedSpace());
247 CHECK(ReadOnlyHeap::Contains(heap_object) ||
248 shared_space_->Contains(heap_object) ||
249 shared_lo_space_->Contains(heap_object) ||
250 shared_trusted_space_->Contains(heap_object) ||
251 shared_trusted_lo_space_->Contains(heap_object));
252 }
253 }
254 }
255
256 private:
257 SharedSpace* shared_space_;
258 SharedTrustedSpace* shared_trusted_space_;
259 SharedLargeObjectSpace* shared_lo_space_;
260 SharedTrustedLargeObjectSpace* shared_trusted_lo_space_;
261};
262
263class HeapVerification final : public SpaceVerificationVisitor {
264 public:
265 explicit HeapVerification(Heap* heap)
266 : heap_(heap), isolate_(heap->isolate()), cage_base_(isolate_) {}
267
268 void Verify();
269 void VerifyReadOnlyHeap();
270 void VerifySharedHeap(Isolate* initiator);
271
272 private:
273 void VerifySpace(BaseSpace* space);
274
275 void VerifyPage(const MemoryChunkMetadata* chunk) final;
276 void VerifyPageDone(const MemoryChunkMetadata* chunk) final;
277
278 void VerifyObject(Tagged<HeapObject> object) final;
279 void VerifyObjectMap(Tagged<HeapObject> object);
280 void VerifyOutgoingPointers(Tagged<HeapObject> object);
281 // Verifies OLD_TO_NEW, OLD_TO_NEW_BACKGROUND and OLD_TO_SHARED remembered
282 // sets for this object.
283 void VerifyRememberedSetFor(Tagged<HeapObject> object);
284
285 ReadOnlySpace* read_only_space() const { return heap_->read_only_space(); }
286 NewSpace* new_space() const { return heap_->new_space(); }
287 OldSpace* old_space() const { return heap_->old_space(); }
288 SharedSpace* shared_space() const { return heap_->shared_space(); }
289 CodeSpace* code_space() const { return heap_->code_space(); }
290 LargeObjectSpace* lo_space() const { return heap_->lo_space(); }
291 SharedLargeObjectSpace* shared_lo_space() const {
292 return heap_->shared_lo_space();
293 }
294 CodeLargeObjectSpace* code_lo_space() const { return heap_->code_lo_space(); }
295 NewLargeObjectSpace* new_lo_space() const { return heap_->new_lo_space(); }
296 TrustedSpace* trusted_space() const { return heap_->trusted_space(); }
297 SharedTrustedSpace* shared_trusted_space() const {
298 return heap_->shared_trusted_space();
299 }
300 TrustedLargeObjectSpace* trusted_lo_space() const {
301 return heap_->trusted_lo_space();
302 }
303 SharedTrustedLargeObjectSpace* shared_trusted_lo_space() const {
304 return heap_->shared_trusted_lo_space();
305 }
306
307 Isolate* isolate() const { return isolate_; }
308 Heap* heap() const { return heap_; }
309
310 AllocationSpace current_space_identity() const {
311 return *current_space_identity_;
312 }
313
314 Heap* const heap_;
315 Isolate* const isolate_;
316 const PtrComprCageBase cage_base_;
317 std::optional<AllocationSpace> current_space_identity_;
318 std::optional<const MemoryChunkMetadata*> current_chunk_;
319};
320
321void HeapVerification::Verify() {
322 CHECK(heap()->HasBeenSetUp());
323 AllowGarbageCollection allow_gc;
324 SafepointKind safepoint_kind = isolate()->is_shared_space_isolate()
327 SafepointScope safepoint_scope(isolate(), safepoint_kind);
328 HandleScope scope(isolate());
329
330 heap()->MakeHeapIterable();
331 heap()->FreeLinearAllocationAreas();
332
333 // TODO(v8:13257): Currently we don't iterate through the stack conservatively
334 // when verifying the heap.
335 VerifyPointersVisitor visitor(heap());
336 heap()->IterateRoots(&visitor,
337 base::EnumSet<SkipRoot>{SkipRoot::kConservativeStack});
338
339 if (!isolate()->context().is_null() &&
340 !isolate()->raw_native_context().is_null()) {
341 Tagged<Object> normalized_map_cache =
342 isolate()->raw_native_context()->normalized_map_cache();
343
344 if (IsNormalizedMapCache(normalized_map_cache)) {
345 Cast<NormalizedMapCache>(normalized_map_cache)
346 ->NormalizedMapCacheVerify(isolate());
347 }
348 }
349
350#if V8_ENABLE_WEBASSEMBLY
351 // wasm_canonical_rtts holds weak references to maps or (strong) undefined.
352 Tagged<WeakFixedArray> canonical_rtts = heap()->wasm_canonical_rtts();
353 for (int i = 0, e = canonical_rtts->length(); i < e; ++i) {
354 Tagged<MaybeObject> maybe_rtt = canonical_rtts->get(i);
355 if (maybe_rtt.IsCleared()) continue;
356 CHECK(maybe_rtt.IsWeak());
357 CHECK(IsMap(maybe_rtt.GetHeapObjectAssumeWeak()));
358 }
359
360 // js_to_wasm_wrappers holds weak references to code or cleared values.
361 Tagged<WeakFixedArray> wrappers = heap()->js_to_wasm_wrappers();
362 for (int i = 0, e = wrappers->length(); i < e; ++i) {
363 Tagged<MaybeObject> maybe_wrapper = wrappers->get(i);
364 if (maybe_wrapper.IsCleared()) continue;
365 CHECK(maybe_wrapper.IsWeak());
366 CHECK(IsCodeWrapper(maybe_wrapper.GetHeapObjectAssumeWeak()));
367 }
368#endif // V8_ENABLE_WEBASSEMBLY
369
370 // The heap verifier can't deal with partially deserialized objects, so
371 // disable it if a deserializer is active.
372 // TODO(leszeks): Enable verification during deserialization, e.g. by only
373 // blocklisting objects that are in a partially deserialized state.
374 if (isolate()->has_active_deserializer()) return;
375
376 VerifySmisVisitor smis_visitor;
377 heap()->IterateSmiRoots(&smis_visitor);
378
379 VerifySpace(new_space());
380
381 VerifySpace(old_space());
382 VerifySpace(shared_space());
383 VerifySpace(code_space());
384
385 VerifySpace(lo_space());
386 VerifySpace(new_lo_space());
387 VerifySpace(shared_lo_space());
388 VerifySpace(code_lo_space());
389
390 VerifySpace(trusted_space());
391 VerifySpace(shared_trusted_space());
392 VerifySpace(trusted_lo_space());
393 VerifySpace(shared_trusted_lo_space());
394
396
397#if DEBUG
398 heap()->VerifyCommittedPhysicalMemory();
399#endif // DEBUG
400}
401
402void HeapVerification::VerifySpace(BaseSpace* space) {
403 if (!space) return;
404 current_space_identity_ = space->identity();
405 space->Verify(isolate(), this);
406 current_space_identity_.reset();
407}
408
409void HeapVerification::VerifyPage(const MemoryChunkMetadata* chunk_metadata) {
410 const MemoryChunk* chunk = chunk_metadata->Chunk();
411
412 CHECK(!current_chunk_.has_value());
413 CHECK(!chunk->IsFlagSet(MemoryChunk::PAGE_NEW_OLD_PROMOTION));
414 CHECK(!chunk->IsFlagSet(MemoryChunk::FROM_PAGE));
415 CHECK(!chunk->IsFlagSet(MemoryChunk::WILL_BE_PROMOTED));
416 CHECK(!chunk->IsQuarantined());
417 if (chunk->InReadOnlySpace()) {
418 CHECK_NULL(chunk_metadata->owner());
419 } else {
420 CHECK_EQ(chunk_metadata->heap(), heap());
421 CHECK_EQ(chunk_metadata->owner()->identity(), current_space_identity());
422 }
423 current_chunk_ = chunk_metadata;
424}
425
426void HeapVerification::VerifyPageDone(const MemoryChunkMetadata* chunk) {
427 CHECK_EQ(chunk, *current_chunk_);
428 current_chunk_.reset();
429}
430
431void HeapVerification::VerifyObject(Tagged<HeapObject> object) {
432 CHECK_EQ(MemoryChunkMetadata::FromHeapObject(object), *current_chunk_);
433
434 // Verify object map.
435 VerifyObjectMap(object);
436
437 // The object itself should look OK.
438 Object::ObjectVerify(object, isolate_);
439
440 // Verify outgoing references.
441 VerifyOutgoingPointers(object);
442
443 // Verify remembered set.
445 // Minor incremental marking "steals" the remembered sets from pages.
446 VerifyRememberedSetFor(object);
447 }
448}
449
450void HeapVerification::VerifyOutgoingPointers(Tagged<HeapObject> object) {
451 switch (current_space_identity()) {
452 case RO_SPACE: {
453 VerifyReadOnlyPointersVisitor visitor(heap());
454 visitor.Visit(object);
455 break;
456 }
457
458 case SHARED_SPACE:
460 case SHARED_LO_SPACE:
462 VerifySharedHeapObjectVisitor visitor(heap());
463 visitor.Visit(object);
464 break;
465 }
466
467 case NEW_SPACE:
468 case OLD_SPACE:
469 case TRUSTED_SPACE:
470 case CODE_SPACE:
471 case LO_SPACE:
472 case NEW_LO_SPACE:
473 case CODE_LO_SPACE:
474 case TRUSTED_LO_SPACE: {
475 VerifyPointersVisitor visitor(heap());
476 visitor.Visit(object);
477 break;
478 }
479 }
480}
481
482void HeapVerification::VerifyObjectMap(Tagged<HeapObject> object) {
483 // The first word should be a map, and we expect all map pointers to be
484 // in map space or read-only space.
485 MapWord map_word = object->map_word(cage_base_, kRelaxedLoad);
486 CHECK(!map_word.IsForwardingAddress());
487 Tagged<Map> map = map_word.ToMap();
488 CHECK(IsMap(map, cage_base_));
489 CHECK(ReadOnlyHeap::Contains(map) || old_space()->Contains(map) ||
490 (shared_space() && shared_space()->Contains(map)));
491
492 if (HeapLayout::InYoungGeneration(object)) {
493 // The object should not be code or a map.
494 CHECK(!IsMap(object, cage_base_));
495 CHECK(!IsAbstractCode(object, cage_base_));
496 } else if (current_space_identity() == RO_SPACE) {
497 CHECK(!IsExternalString(object));
498 CHECK(!IsJSArrayBuffer(object));
499 }
500}
501
502void HeapVerification::VerifyReadOnlyHeap() {
503 CHECK(!read_only_space()->writable());
504 VerifySpace(read_only_space());
505}
506
507class SlotVerifyingVisitor : public HeapVisitor<SlotVerifyingVisitor> {
508 public:
509 SlotVerifyingVisitor(Isolate* isolate, std::set<Address>* untyped,
510 std::set<std::pair<SlotType, Address>>* typed,
511 std::set<Address>* protected_pointer)
512 : HeapVisitor(isolate),
513 untyped_(untyped),
514 typed_(typed),
515 protected_(protected_pointer) {}
516
517 virtual bool ShouldHaveBeenRecorded(Tagged<HeapObject> host,
518 Tagged<MaybeObject> target) = 0;
519
520 void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
521 ObjectSlot end) override {
522#ifdef DEBUG
523 for (ObjectSlot slot = start; slot < end; ++slot) {
524 Tagged<Object> obj = slot.load(cage_base());
525 CHECK(!MapWord::IsPacked(obj.ptr()) || !HasWeakHeapObjectTag(obj));
526 }
527#endif // DEBUG
528 VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
529 }
530
531 void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
532 MaybeObjectSlot end) final {
533 for (MaybeObjectSlot slot = start; slot < end; ++slot) {
534 if (ShouldHaveBeenRecorded(host, slot.load(cage_base()))) {
535 CHECK_GT(untyped_->count(slot.address()), 0);
536 }
537 }
538 }
539
540 void VisitInstructionStreamPointer(Tagged<Code> host,
541 InstructionStreamSlot slot) override {
542 if (ShouldHaveBeenRecorded(host, slot.load(code_cage_base()))) {
543 CHECK_GT(untyped_->count(slot.address()), 0);
544 }
545 }
546
547 void VisitCodeTarget(Tagged<InstructionStream> host,
548 RelocInfo* rinfo) override {
549 Tagged<Object> target =
550 InstructionStream::FromTargetAddress(rinfo->target_address());
551 if (ShouldHaveBeenRecorded(host, target)) {
552 CHECK(InTypedSet(SlotType::kCodeEntry, rinfo->pc()) ||
553 (rinfo->IsInConstantPool() &&
554 InTypedSet(SlotType::kConstPoolCodeEntry,
555 rinfo->constant_pool_entry_address())));
556 }
557 }
558
559 void VisitEmbeddedPointer(Tagged<InstructionStream> host,
560 RelocInfo* rinfo) override {
561 Tagged<Object> target = rinfo->target_object(cage_base());
562 if (ShouldHaveBeenRecorded(host, target)) {
563 CHECK(InTypedSet(SlotType::kEmbeddedObjectFull, rinfo->pc()) ||
564 InTypedSet(SlotType::kEmbeddedObjectCompressed, rinfo->pc()) ||
565 (rinfo->IsInConstantPool() &&
566 InTypedSet(SlotType::kConstPoolEmbeddedObjectCompressed,
567 rinfo->constant_pool_entry_address())) ||
568 (rinfo->IsInConstantPool() &&
569 InTypedSet(SlotType::kConstPoolEmbeddedObjectFull,
570 rinfo->constant_pool_entry_address())));
571 }
572 }
573
574 void VisitProtectedPointer(Tagged<TrustedObject> host,
575 ProtectedPointerSlot slot) override {
576 if (ShouldHaveBeenRecorded(host, slot.load())) {
577 CHECK_NOT_NULL(protected_);
578 CHECK_GT(protected_->count(slot.address()), 0);
579 }
580 }
581
582 void VisitProtectedPointer(Tagged<TrustedObject> host,
583 ProtectedMaybeObjectSlot slot) override {
584 if (ShouldHaveBeenRecorded(host, slot.load())) {
585 CHECK_NOT_NULL(protected_);
586 CHECK_GT(protected_->count(slot.address()), 0);
587 }
588 }
589
590 void VisitMapPointer(Tagged<HeapObject> object) override {
591 VisitPointers(object, object->map_slot(), object->map_slot() + 1);
592 }
593
594 protected:
595 bool InUntypedSet(ObjectSlot slot) {
596 return untyped_->count(slot.address()) > 0;
597 }
598
599 private:
600 bool InTypedSet(SlotType type, Address slot) {
601 return typed_->count(std::make_pair(type, slot)) > 0;
602 }
603 std::set<Address>* untyped_;
604 std::set<std::pair<SlotType, Address>>* typed_;
605 std::set<Address>* protected_;
606};
607
608class OldToNewSlotVerifyingVisitor : public SlotVerifyingVisitor {
609 public:
610 OldToNewSlotVerifyingVisitor(
611 Isolate* isolate, std::set<Address>* untyped,
612 std::set<std::pair<SlotType, Address>>* typed,
613 EphemeronRememberedSet::TableMap* ephemeron_remembered_set)
614 : SlotVerifyingVisitor(isolate, untyped, typed, nullptr),
615 ephemeron_remembered_set_(ephemeron_remembered_set) {}
616
617 bool ShouldHaveBeenRecorded(Tagged<HeapObject> host,
618 Tagged<MaybeObject> target) override {
619 // Heap::InToPage() is not available with sticky mark-bits.
620 CHECK_IMPLIES(!v8_flags.sticky_mark_bits && target.IsStrongOrWeak() &&
621 HeapLayout::InYoungGeneration(target),
622 Heap::InToPage(target));
623 return target.IsStrongOrWeak() && HeapLayout::InYoungGeneration(target) &&
624 !HeapLayout::InYoungGeneration(host);
625 }
626
627 void VisitEphemeron(Tagged<HeapObject> host, int index, ObjectSlot key,
628 ObjectSlot target) override {
629 VisitPointer(host, target);
630 if (v8_flags.minor_ms) return;
631 // Keys are handled separately and should never appear in this set.
632 CHECK(!InUntypedSet(key));
633 Tagged<Object> k = *key;
634 if (!HeapLayout::InYoungGeneration(host) &&
635 HeapLayout::InYoungGeneration(k)) {
636 Tagged<EphemeronHashTable> table = i::Cast<EphemeronHashTable>(host);
637 auto it = ephemeron_remembered_set_->find(table);
638 CHECK(it != ephemeron_remembered_set_->end());
639 int slot_index =
640 EphemeronHashTable::SlotToIndex(table.address(), key.address());
641 InternalIndex entry = EphemeronHashTable::IndexToEntry(slot_index);
642 CHECK(it->second.find(entry.as_int()) != it->second.end());
643 }
644 }
645
646 private:
647 EphemeronRememberedSet::TableMap* ephemeron_remembered_set_;
648};
649
650class OldToSharedSlotVerifyingVisitor : public SlotVerifyingVisitor {
651 public:
652 OldToSharedSlotVerifyingVisitor(Isolate* isolate, std::set<Address>* untyped,
653 std::set<std::pair<SlotType, Address>>* typed,
654 std::set<Address>* protected_pointer)
655 : SlotVerifyingVisitor(isolate, untyped, typed, protected_pointer) {}
656
657 bool ShouldHaveBeenRecorded(Tagged<HeapObject> host,
658 Tagged<MaybeObject> target) override {
659 Tagged<HeapObject> target_heap_object;
660 return target.GetHeapObject(&target_heap_object) &&
661 HeapLayout::InWritableSharedSpace(target_heap_object) &&
662 !(v8_flags.black_allocated_pages &&
663 HeapLayout::InBlackAllocatedPage(target_heap_object)) &&
664 !HeapLayout::InYoungGeneration(host) &&
665 !HeapLayout::InWritableSharedSpace(host);
666 }
667};
668
669template <RememberedSetType direction>
670void CollectSlots(MutablePageMetadata* chunk, Address start, Address end,
671 std::set<Address>* untyped,
672 std::set<std::pair<SlotType, Address>>* typed) {
674 chunk,
675 [start, end, untyped](MaybeObjectSlot slot) {
676 if (start <= slot.address() && slot.address() < end) {
677 untyped->insert(slot.address());
678 }
679 return KEEP_SLOT;
680 },
683 chunk, [=](SlotType type, Address slot) {
684 if (start <= slot && slot < end) {
685 typed->insert(std::make_pair(type, slot));
686 }
687 return KEEP_SLOT;
688 });
689}
690
691// Helper class for collecting slot addresses.
692class SlotCollectingVisitor final : public HeapVisitor<SlotCollectingVisitor> {
693 public:
694 explicit SlotCollectingVisitor(Isolate* isolate) : HeapVisitor(isolate) {}
695
696 static constexpr bool ShouldUseUncheckedCast() { return true; }
697
698 void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
699 ObjectSlot end) override {
700 VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
701 }
702 void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
703 MaybeObjectSlot end) final {
704 for (MaybeObjectSlot p = start; p < end; ++p) {
705 slots_.push_back(p);
706 }
707 }
708
709 void VisitInstructionStreamPointer(Tagged<Code> host,
710 InstructionStreamSlot slot) override {
712#ifdef V8_EXTERNAL_CODE_SPACE
713 code_slots_.push_back(slot);
714#endif
715 }
716
717 void VisitCodeTarget(Tagged<InstructionStream> host, RelocInfo* rinfo) final {
718 UNREACHABLE();
719 }
720
721 void VisitEmbeddedPointer(Tagged<InstructionStream> host,
722 RelocInfo* rinfo) override {
723 UNREACHABLE();
724 }
725
726 void VisitMapPointer(Tagged<HeapObject> object) override {
727 slots_.push_back(MaybeObjectSlot(object->map_slot()));
728 }
729
730 int number_of_slots() { return static_cast<int>(slots_.size()); }
731
732 MaybeObjectSlot slot(int i) { return slots_[i]; }
733#ifdef V8_EXTERNAL_CODE_SPACE
734 InstructionStreamSlot code_slot(int i) { return code_slots_[i]; }
735 int number_of_code_slots() { return static_cast<int>(code_slots_.size()); }
736#endif
737
738 private:
739 std::vector<MaybeObjectSlot> slots_;
740#ifdef V8_EXTERNAL_CODE_SPACE
741 std::vector<InstructionStreamSlot> code_slots_;
742#endif
743};
744
745void HeapVerification::VerifyRememberedSetFor(Tagged<HeapObject> object) {
746 if (current_space_identity() == RO_SPACE ||
747 v8_flags.verify_heap_skip_remembered_set) {
748 return;
749 }
750
751 MutablePageMetadata* chunk = MutablePageMetadata::FromHeapObject(object);
752
753 Address start = object.address();
754 Address end = start + object->Size(cage_base_);
755
756 std::set<Address> old_to_new;
757 std::set<std::pair<SlotType, Address>> typed_old_to_new;
758 CollectSlots<OLD_TO_NEW>(chunk, start, end, &old_to_new, &typed_old_to_new);
759 CollectSlots<OLD_TO_NEW_BACKGROUND>(chunk, start, end, &old_to_new,
760 &typed_old_to_new);
761
762 OldToNewSlotVerifyingVisitor old_to_new_visitor(
763 isolate(), &old_to_new, &typed_old_to_new,
764 heap()->ephemeron_remembered_set()->tables());
765 old_to_new_visitor.Visit(object);
766
767 std::set<Address> old_to_shared;
768 std::set<std::pair<SlotType, Address>> typed_old_to_shared;
769 CollectSlots<OLD_TO_SHARED>(chunk, start, end, &old_to_shared,
770 &typed_old_to_shared);
771 std::set<Address> trusted_to_shared_trusted;
772 CollectSlots<TRUSTED_TO_SHARED_TRUSTED>(chunk, start, end,
773 &trusted_to_shared_trusted, nullptr);
774 OldToSharedSlotVerifyingVisitor old_to_shared_visitor(
775 isolate(), &old_to_shared, &typed_old_to_shared,
776 &trusted_to_shared_trusted);
777 old_to_shared_visitor.Visit(object);
778
779 if (!MemoryChunk::FromHeapObject(object)->IsTrusted()) {
780 CHECK_NULL(chunk->slot_set<TRUSTED_TO_TRUSTED>());
781 CHECK_NULL(chunk->slot_set<TRUSTED_TO_SHARED_TRUSTED>());
782 }
783
785 CHECK_NULL(chunk->slot_set<OLD_TO_SHARED>());
786 CHECK_NULL(chunk->typed_slot_set<OLD_TO_SHARED>());
787
788 CHECK_NULL(chunk->slot_set<TRUSTED_TO_SHARED_TRUSTED>());
789 CHECK_NULL(chunk->typed_slot_set<TRUSTED_TO_SHARED_TRUSTED>());
790
791 CHECK_NULL(chunk->slot_set<OLD_TO_NEW>());
792 CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW>());
793
794 CHECK_NULL(chunk->slot_set<OLD_TO_NEW_BACKGROUND>());
795 CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW_BACKGROUND>());
796 }
797
798 if (!v8_flags.sticky_mark_bits && HeapLayout::InYoungGeneration(object)) {
799 CHECK_NULL(chunk->slot_set<OLD_TO_NEW>());
800 CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW>());
801
802 CHECK_NULL(chunk->slot_set<OLD_TO_NEW_BACKGROUND>());
803 CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW_BACKGROUND>());
804
805 CHECK_NULL(chunk->slot_set<OLD_TO_OLD>());
806 CHECK_NULL(chunk->typed_slot_set<OLD_TO_OLD>());
807
808 CHECK_NULL(chunk->slot_set<OLD_TO_SHARED>());
809 CHECK_NULL(chunk->typed_slot_set<OLD_TO_SHARED>());
810 }
811
812 // TODO(v8:11797): Add old to old slot set verification once all weak objects
813 // have their own instance types and slots are recorded for all weak fields.
814}
815
816// static
817void HeapVerifier::VerifyHeap(Heap* heap) {
818 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"), "V8.HeapVerification");
819 HeapVerification verifier(heap);
820 verifier.Verify();
821}
822
823// static
825 HeapVerification verifier(heap);
826 verifier.VerifyReadOnlyHeap();
827}
828
829// static
831 Heap* heap, Tagged<HeapObject> object) {
833 // Out of objects in the shared heap, only strings can change layout.
834 CHECK(IsString(object));
835 // Shared strings only change layout under GC, never concurrently.
836 if (IsShared(object)) {
837 Isolate* isolate = heap->isolate();
838 Isolate* shared_space_isolate = isolate->is_shared_space_isolate()
839 ? isolate
840 : isolate->shared_space_isolate();
841 shared_space_isolate->global_safepoint()->AssertActive();
842 }
843 // Non-shared strings in the shared heap are allowed to change layout
844 // outside of GC like strings in non-shared heaps.
845 }
846}
847
848// static
849void HeapVerifier::SetPendingLayoutChangeObject(Heap* heap,
850 Tagged<HeapObject> object) {
852 CHECK(pending_layout_change_object.is_null());
853 pending_layout_change_object = object;
854}
855
856// static
858 Tagged<HeapObject> object,
859 Tagged<Map> new_map) {
860 // Object layout changes are currently not supported on background threads.
861 CHECK_NULL(LocalHeap::Current());
862
863 if (!v8_flags.verify_heap) return;
864
866
867 PtrComprCageBase cage_base(heap->isolate());
868
869 // Check that Heap::NotifyObjectLayoutChange was called for object transitions
870 // that are not safe for concurrent marking.
871 // If you see this check triggering for a freshly allocated object,
872 // use object->set_map_after_allocation() to initialize its map.
873 if (pending_layout_change_object.is_null()) {
874 VerifySafeMapTransition(heap, object, new_map);
875 } else {
876 CHECK_EQ(pending_layout_change_object, object);
877 pending_layout_change_object = HeapObject();
878 }
879}
880
881// static
883 Tagged<HeapObject> object,
884 Tagged<Map> new_map) {
885 PtrComprCageBase cage_base(heap->isolate());
886
887 if (IsJSObject(object, cage_base)) {
888 // Without double unboxing all in-object fields of a JSObject are tagged.
889 return;
890 }
891
892 if (IsString(object, cage_base) &&
893 (new_map == ReadOnlyRoots(heap).thin_two_byte_string_map() ||
894 new_map == ReadOnlyRoots(heap).thin_one_byte_string_map())) {
895 // When transitioning a string to ThinString,
896 // Heap::NotifyObjectLayoutChange doesn't need to be invoked because only
897 // tagged fields are introduced.
898 return;
899 }
900
901 if (v8_flags.shared_string_table && IsString(object, cage_base) &&
902 InstanceTypeChecker::IsInternalizedString(new_map->instance_type())) {
903 // In-place internalization does not change a string's fields.
904 //
905 // When sharing the string table, the setting and re-setting of maps below
906 // can race when there are parallel internalization operations, causing
907 // CHECKs to fail.
908 return;
909 }
910
911 // Check that the set of slots before and after the transition match.
912 SlotCollectingVisitor old_visitor(heap->isolate());
913 old_visitor.Visit(object);
914 // Collect slots in object for new map.
915 SlotCollectingVisitor new_visitor(heap->isolate());
916 new_visitor.Visit(new_map, object);
917 CHECK_EQ(new_visitor.number_of_slots(), old_visitor.number_of_slots());
918 for (int i = 0; i < new_visitor.number_of_slots(); i++) {
919 CHECK_EQ(new_visitor.slot(i), old_visitor.slot(i));
920 }
921#ifdef V8_EXTERNAL_CODE_SPACE
922 CHECK_EQ(new_visitor.number_of_code_slots(),
923 old_visitor.number_of_code_slots());
924 for (int i = 0; i < new_visitor.number_of_code_slots(); i++) {
925 CHECK_EQ(new_visitor.code_slot(i), old_visitor.code_slot(i));
926 }
927#endif // V8_EXTERNAL_CODE_SPACE
928}
929
930} // namespace internal
931} // namespace v8
932#endif // VERIFY_HEAP
Isolate * isolate_
static V8_INLINE bool InYoungGeneration(Tagged< Object > object)
static V8_INLINE bool InWritableSharedSpace(Tagged< HeapObject > object)
static void VerifyObjectLayoutChange(Heap *heap, Tagged< HeapObject > object, Tagged< Map > new_map)
static void VerifyReadOnlyHeap(Heap *heap)
static void VerifyHeap(Heap *heap)
static void VerifyObjectLayoutChangeIsAllowed(Heap *heap, Tagged< HeapObject > object)
static void VerifySafeMapTransition(Heap *heap, Tagged< HeapObject > object, Tagged< Map > new_map)
IncrementalMarking * incremental_marking() const
Definition heap.h:1062
static Tagged< InstructionStream > FromTargetAddress(Address address)
bool is_shared_space_isolate() const
Definition isolate.h:2292
Tagged< NativeContext > raw_native_context()
Definition isolate-inl.h:53
StringTable * string_table() const
Definition isolate.h:781
static constexpr bool IsPacked(Address)
Definition objects.h:846
static constexpr ObjectFields ObjectFieldsFrom(VisitorId visitor_id)
Definition map.h:919
static V8_INLINE MemoryChunkMetadata * FromHeapObject(Tagged< HeapObject > o)
static V8_INLINE MemoryChunk * FromHeapObject(Tagged< HeapObject > object)
static V8_INLINE MutablePageMetadata * FromHeapObject(Tagged< HeapObject > o)
static V8_EXPORT_PRIVATE bool Contains(Address address)
static int IterateTyped(MutablePageMetadata *chunk, Callback callback)
static int Iterate(MutablePageMetadata *chunk, Callback callback, SlotSet::EmptyBucketMode mode)
void VerifyIfOwnedBy(Isolate *isolate)
Handle< Code > code
#define V8_EXTERNAL_CODE_SPACE_BOOL
Definition globals.h:255
int start
int end
LineAndColumn current
Isolate * isolate
TNode< Context > context
V8_INLINE constexpr bool IsExternalString(InstanceType instance_type)
V8_INLINE constexpr bool IsInternalizedString(InstanceType instance_type)
V8_INLINE constexpr bool IsAbstractCode(InstanceType instance_type)
V8_WARN_UNUSED_RESULT bool IsValidCodeObject(Heap *heap, Tagged< HeapObject > object)
constexpr Address kTaggedNullAddress
Definition handles.h:53
SlotTraits::TObjectSlot ObjectSlot
Definition globals.h:1243
Tagged(T object) -> Tagged< T >
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
kInterpreterTrampolineOffset Tagged< HeapObject >
SlotTraits::TInstructionStreamSlot InstructionStreamSlot
Definition globals.h:1265
PerThreadAssertScopeDebugOnly< true, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > AllowGarbageCollection
kMemory0SizeOffset Address kNewAllocationLimitAddressOffset Address kOldAllocationLimitAddressOffset uint8_t kGlobalsStartOffset kJumpTableStartOffset std::atomic< uint32_t > kTieringBudgetArrayOffset kDataSegmentStartsOffset kElementSegmentsOffset kInstanceObjectOffset kMemoryObjectsOffset kTaggedGlobalsBufferOffset tables
bool IsShared(Tagged< Object > obj)
@ SHARED_TRUSTED_LO_SPACE
Definition globals.h:1319
@ SHARED_TRUSTED_SPACE
Definition globals.h:1314
V8_EXPORT_PRIVATE FlagValues v8_flags
SlotTraits::TOffHeapObjectSlot OffHeapObjectSlot
Definition globals.h:1258
SlotTraits::TMaybeObjectSlot MaybeObjectSlot
Definition globals.h:1248
V8_WARN_UNUSED_RESULT bool IsValidHeapObject(Heap *heap, Tagged< HeapObject > object)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr RelaxedLoadTag kRelaxedLoad
Definition globals.h:2909
#define UNREACHABLE()
Definition logging.h:67
#define CHECK_IMPLIES(lhs, rhs)
#define CHECK(condition)
Definition logging.h:124
#define CHECK_GT(lhs, rhs)
#define CHECK_NULL(val)
#define CHECK_NOT_NULL(val)
#define CHECK_EQ(lhs, rhs)
EphemeronRememberedSet * ephemeron_remembered_set_
Definition sweeper.cc:572
#define TRACE_EVENT0(category_group, name)
#define TRACE_DISABLED_BY_DEFAULT(name)
Heap * heap_
#define V8_INLINE
Definition v8config.h:500
std::unique_ptr< ValueMirror > key