%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/heap-verifier.cc |
// Copyright 2022 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/heap-verifier.h"
#include "include/v8-locker.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/reloc-info.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/heap/array-buffer-sweeper.h"
#include "src/heap/basic-memory-chunk.h"
#include "src/heap/combined-heap.h"
#include "src/heap/ephemeron-remembered-set.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/heap/heap.h"
#include "src/heap/large-spaces.h"
#include "src/heap/memory-chunk-layout.h"
#include "src/heap/new-spaces.h"
#include "src/heap/objects-visiting-inl.h"
#include "src/heap/paged-spaces.h"
#include "src/heap/read-only-heap.h"
#include "src/heap/read-only-spaces.h"
#include "src/heap/remembered-set.h"
#include "src/heap/safepoint.h"
#include "src/objects/code-inl.h"
#include "src/objects/code.h"
#include "src/objects/maybe-object.h"
#include "src/objects/slots-inl.h"
#include "src/objects/string-table.h"
#ifdef VERIFY_HEAP
namespace v8 {
namespace internal {
namespace {
thread_local Tagged<HeapObject> pending_layout_change_object =
Tagged<HeapObject>();
} // namespace
// Verify that all objects are Smis.
class VerifySmisVisitor final : public RootVisitor {
public:
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override {
for (FullObjectSlot current = start; current < end; ++current) {
CHECK(IsSmi(*current));
}
}
};
// Visitor class to verify interior pointers in spaces that do not contain
// or care about inter-generational references. All heap object pointers have to
// point into the heap to a location that has a map pointer at its first word.
// Caveat: Heap::Contains is an approximation because it can return true for
// objects in a heap space but above the allocation pointer.
class VerifyPointersVisitor : public ObjectVisitorWithCageBases,
public RootVisitor {
public:
V8_INLINE explicit VerifyPointersVisitor(Heap* heap)
: ObjectVisitorWithCageBases(heap), heap_(heap) {}
void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
ObjectSlot end) override;
void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) override;
void VisitInstructionStreamPointer(Tagged<Code> host,
InstructionStreamSlot slot) override;
void VisitCodeTarget(Tagged<InstructionStream> host,
RelocInfo* rinfo) override;
void VisitEmbeddedPointer(Tagged<InstructionStream> host,
RelocInfo* rinfo) override;
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
void VisitRootPointers(Root root, const char* description,
OffHeapObjectSlot start,
OffHeapObjectSlot end) override;
void VisitMapPointer(Tagged<HeapObject> host) override;
protected:
V8_INLINE void VerifyHeapObjectImpl(Tagged<HeapObject> heap_object);
V8_INLINE void VerifyCodeObjectImpl(Tagged<HeapObject> heap_object);
template <typename TSlot>
V8_INLINE void VerifyPointersImpl(TSlot start, TSlot end);
virtual void VerifyPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end);
Heap* heap_;
};
void VerifyPointersVisitor::VisitPointers(Tagged<HeapObject> host,
ObjectSlot start, ObjectSlot end) {
VerifyPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
}
void VerifyPointersVisitor::VisitPointers(Tagged<HeapObject> host,
MaybeObjectSlot start,
MaybeObjectSlot end) {
VerifyPointers(host, start, end);
}
void VerifyPointersVisitor::VisitInstructionStreamPointer(
Tagged<Code> host, InstructionStreamSlot slot) {
Tagged<Object> maybe_code = slot.load(code_cage_base());
Tagged<HeapObject> code;
// The slot might contain smi during Code creation.
if (maybe_code.GetHeapObject(&code)) {
VerifyCodeObjectImpl(code);
} else {
CHECK(IsSmi(maybe_code));
}
}
void VerifyPointersVisitor::VisitRootPointers(Root root,
const char* description,
FullObjectSlot start,
FullObjectSlot end) {
VerifyPointersImpl(start, end);
}
void VerifyPointersVisitor::VisitRootPointers(Root root,
const char* description,
OffHeapObjectSlot start,
OffHeapObjectSlot end) {
VerifyPointersImpl(start, end);
}
void VerifyPointersVisitor::VisitMapPointer(Tagged<HeapObject> host) {
VerifyHeapObjectImpl(host->map(cage_base()));
}
void VerifyPointersVisitor::VerifyHeapObjectImpl(
Tagged<HeapObject> heap_object) {
CHECK(IsValidHeapObject(heap_, heap_object));
CHECK(IsMap(heap_object->map(cage_base())));
}
void VerifyPointersVisitor::VerifyCodeObjectImpl(
Tagged<HeapObject> heap_object) {
CHECK(IsValidCodeObject(heap_, heap_object));
CHECK(IsMap(heap_object->map(cage_base())));
CHECK(heap_object->map(cage_base())->instance_type() ==
INSTRUCTION_STREAM_TYPE);
}
template <typename TSlot>
void VerifyPointersVisitor::VerifyPointersImpl(TSlot start, TSlot end) {
for (TSlot slot = start; slot < end; ++slot) {
typename TSlot::TObject object = slot.load(cage_base());
Tagged<HeapObject> heap_object;
if (object.GetHeapObject(&heap_object)) {
VerifyHeapObjectImpl(heap_object);
} else {
CHECK(IsSmi(object) || object.IsCleared() ||
MapWord::IsPacked(object.ptr()));
}
}
}
void VerifyPointersVisitor::VerifyPointers(Tagged<HeapObject> host,
MaybeObjectSlot start,
MaybeObjectSlot end) {
// If this DCHECK fires then you probably added a pointer field
// to one of objects in DATA_ONLY_VISITOR_ID_LIST. You can fix
// this by moving that object to POINTER_VISITOR_ID_LIST.
DCHECK_EQ(ObjectFields::kMaybePointers,
Map::ObjectFieldsFrom(host->map(cage_base())->visitor_id()));
VerifyPointersImpl(start, end);
}
void VerifyPointersVisitor::VisitCodeTarget(Tagged<InstructionStream> host,
RelocInfo* rinfo) {
Tagged<InstructionStream> target =
InstructionStream::FromTargetAddress(rinfo->target_address());
VerifyHeapObjectImpl(target);
}
void VerifyPointersVisitor::VisitEmbeddedPointer(Tagged<InstructionStream> host,
RelocInfo* rinfo) {
VerifyHeapObjectImpl(rinfo->target_object(cage_base()));
}
class VerifyReadOnlyPointersVisitor : public VerifyPointersVisitor {
public:
explicit VerifyReadOnlyPointersVisitor(Heap* heap)
: VerifyPointersVisitor(heap) {}
protected:
void VerifyPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) override {
if (!host.is_null()) {
CHECK(ReadOnlyHeap::Contains(host->map()));
}
VerifyPointersVisitor::VerifyPointers(host, start, end);
for (MaybeObjectSlot current = start; current < end; ++current) {
Tagged<HeapObject> heap_object;
if ((*current).GetHeapObject(&heap_object)) {
CHECK(ReadOnlyHeap::Contains(heap_object));
}
}
}
};
class VerifySharedHeapObjectVisitor : public VerifyPointersVisitor {
public:
explicit VerifySharedHeapObjectVisitor(Heap* heap)
: VerifyPointersVisitor(heap),
shared_space_(heap->shared_space()),
shared_lo_space_(heap->shared_lo_space()) {
DCHECK_NOT_NULL(shared_space_);
DCHECK_NOT_NULL(shared_lo_space_);
}
protected:
void VerifyPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) override {
if (!host.is_null()) {
Tagged<Map> map = host->map();
CHECK(ReadOnlyHeap::Contains(map) || shared_space_->Contains(map));
}
VerifyPointersVisitor::VerifyPointers(host, start, end);
for (MaybeObjectSlot current = start; current < end; ++current) {
Tagged<HeapObject> heap_object;
if ((*current).GetHeapObject(&heap_object)) {
CHECK(ReadOnlyHeap::Contains(heap_object) ||
shared_space_->Contains(heap_object) ||
shared_lo_space_->Contains(heap_object));
}
}
}
private:
SharedSpace* shared_space_;
SharedLargeObjectSpace* shared_lo_space_;
};
class HeapVerification final : public SpaceVerificationVisitor {
public:
explicit HeapVerification(Heap* heap)
: heap_(heap), isolate_(heap->isolate()), cage_base_(isolate_) {}
void Verify();
void VerifyReadOnlyHeap();
void VerifySharedHeap(Isolate* initiator);
private:
void VerifySpace(BaseSpace* space);
void VerifyPage(const BasicMemoryChunk* chunk) final;
void VerifyPageDone(const BasicMemoryChunk* chunk) final;
void VerifyObject(Tagged<HeapObject> object) final;
void VerifyObjectMap(Tagged<HeapObject> object);
void VerifyOutgoingPointers(Tagged<HeapObject> object);
// Verifies OLD_TO_NEW, OLD_TO_NEW_BACKGROUND and OLD_TO_SHARED remembered
// sets for this object.
void VerifyRememberedSetFor(Tagged<HeapObject> object);
ReadOnlySpace* read_only_space() const { return heap_->read_only_space(); }
NewSpace* new_space() const { return heap_->new_space(); }
OldSpace* old_space() const { return heap_->old_space(); }
SharedSpace* shared_space() const { return heap_->shared_space(); }
CodeSpace* code_space() const { return heap_->code_space(); }
LargeObjectSpace* lo_space() const { return heap_->lo_space(); }
SharedLargeObjectSpace* shared_lo_space() const {
return heap_->shared_lo_space();
}
CodeLargeObjectSpace* code_lo_space() const { return heap_->code_lo_space(); }
NewLargeObjectSpace* new_lo_space() const { return heap_->new_lo_space(); }
TrustedSpace* trusted_space() const { return heap_->trusted_space(); }
TrustedLargeObjectSpace* trusted_lo_space() const {
return heap_->trusted_lo_space();
}
Isolate* isolate() const { return isolate_; }
Heap* heap() const { return heap_; }
AllocationSpace current_space_identity() const {
return *current_space_identity_;
}
Heap* const heap_;
Isolate* const isolate_;
const PtrComprCageBase cage_base_;
base::Optional<AllocationSpace> current_space_identity_;
base::Optional<const BasicMemoryChunk*> current_chunk_;
};
void HeapVerification::Verify() {
CHECK(heap()->HasBeenSetUp());
AllowGarbageCollection allow_gc;
SafepointKind safepoint_kind = isolate()->is_shared_space_isolate()
? SafepointKind::kGlobal
: SafepointKind::kIsolate;
SafepointScope safepoint_scope(isolate(), safepoint_kind);
HandleScope scope(isolate());
heap()->MakeHeapIterable();
// TODO(v8:13257): Currently we don't iterate through the stack conservatively
// when verifying the heap.
VerifyPointersVisitor visitor(heap());
heap()->IterateRoots(&visitor,
base::EnumSet<SkipRoot>{SkipRoot::kConservativeStack});
if (!isolate()->context().is_null() &&
!isolate()->raw_native_context().is_null()) {
Tagged<Object> normalized_map_cache =
isolate()->raw_native_context()->normalized_map_cache();
if (IsNormalizedMapCache(normalized_map_cache)) {
NormalizedMapCache::cast(normalized_map_cache)
->NormalizedMapCacheVerify(isolate());
}
}
// The heap verifier can't deal with partially deserialized objects, so
// disable it if a deserializer is active.
// TODO(leszeks): Enable verification during deserialization, e.g. by only
// blocklisting objects that are in a partially deserialized state.
if (isolate()->has_active_deserializer()) return;
VerifySmisVisitor smis_visitor;
heap()->IterateSmiRoots(&smis_visitor);
VerifySpace(new_space());
VerifySpace(old_space());
VerifySpace(shared_space());
VerifySpace(code_space());
VerifySpace(lo_space());
VerifySpace(new_lo_space());
VerifySpace(shared_lo_space());
VerifySpace(code_lo_space());
VerifySpace(trusted_space());
VerifySpace(trusted_lo_space());
isolate()->string_table()->VerifyIfOwnedBy(isolate());
#if DEBUG
heap()->VerifyCommittedPhysicalMemory();
#endif // DEBUG
}
void HeapVerification::VerifySpace(BaseSpace* space) {
if (!space) return;
current_space_identity_ = space->identity();
space->Verify(isolate(), this);
current_space_identity_.reset();
}
void HeapVerification::VerifyPage(const BasicMemoryChunk* chunk) {
CHECK(!current_chunk_.has_value());
CHECK(!chunk->IsFlagSet(Page::PAGE_NEW_OLD_PROMOTION));
if (V8_SHARED_RO_HEAP_BOOL && chunk->InReadOnlySpace()) {
CHECK_NULL(chunk->owner());
} else {
CHECK_EQ(chunk->heap(), heap());
CHECK_EQ(chunk->owner()->identity(), current_space_identity());
}
current_chunk_ = chunk;
}
void HeapVerification::VerifyPageDone(const BasicMemoryChunk* chunk) {
CHECK_EQ(chunk, *current_chunk_);
current_chunk_.reset();
}
void HeapVerification::VerifyObject(Tagged<HeapObject> object) {
CHECK_EQ(BasicMemoryChunk::FromHeapObject(object), *current_chunk_);
// Verify object map.
VerifyObjectMap(object);
// The object itself should look OK.
Object::ObjectVerify(object, isolate_);
// Verify outgoing references.
VerifyOutgoingPointers(object);
// Verify remembered set.
if (!heap_->incremental_marking()->IsMinorMarking()) {
// Minor incremental marking "steals" the remembered sets from pages.
VerifyRememberedSetFor(object);
}
}
void HeapVerification::VerifyOutgoingPointers(Tagged<HeapObject> object) {
switch (current_space_identity()) {
case RO_SPACE: {
VerifyReadOnlyPointersVisitor visitor(heap());
object->Iterate(cage_base_, &visitor);
break;
}
case SHARED_SPACE:
case SHARED_LO_SPACE: {
VerifySharedHeapObjectVisitor visitor(heap());
object->Iterate(cage_base_, &visitor);
break;
}
default: {
VerifyPointersVisitor visitor(heap());
object->Iterate(cage_base_, &visitor);
break;
}
}
}
void HeapVerification::VerifyObjectMap(Tagged<HeapObject> object) {
// The first word should be a map, and we expect all map pointers to be
// in map space or read-only space.
Tagged<Map> map = object->map(cage_base_);
CHECK(IsMap(map, cage_base_));
CHECK(ReadOnlyHeap::Contains(map) || old_space()->Contains(map) ||
(shared_space() && shared_space()->Contains(map)));
if (Heap::InYoungGeneration(object)) {
// The object should not be code or a map.
CHECK(!IsMap(object, cage_base_));
CHECK(!IsAbstractCode(object, cage_base_));
} else if (current_space_identity() == RO_SPACE) {
CHECK(!IsExternalString(object));
CHECK(!IsJSArrayBuffer(object));
}
}
void HeapVerification::VerifyReadOnlyHeap() {
CHECK(!read_only_space()->writable());
VerifySpace(read_only_space());
}
class SlotVerifyingVisitor : public ObjectVisitorWithCageBases {
public:
SlotVerifyingVisitor(Isolate* isolate, std::set<Address>* untyped,
std::set<std::pair<SlotType, Address>>* typed)
: ObjectVisitorWithCageBases(isolate), untyped_(untyped), typed_(typed) {}
virtual bool ShouldHaveBeenRecorded(Tagged<HeapObject> host,
MaybeObject target) = 0;
void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
ObjectSlot end) override {
#ifdef DEBUG
for (ObjectSlot slot = start; slot < end; ++slot) {
Tagged<Object> obj = slot.load(cage_base());
CHECK(!MapWord::IsPacked(obj.ptr()) || !HasWeakHeapObjectTag(obj));
}
#endif // DEBUG
VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
}
void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) final {
for (MaybeObjectSlot slot = start; slot < end; ++slot) {
if (ShouldHaveBeenRecorded(host, slot.load(cage_base()))) {
CHECK_GT(untyped_->count(slot.address()), 0);
}
}
}
void VisitInstructionStreamPointer(Tagged<Code> host,
InstructionStreamSlot slot) override {
if (ShouldHaveBeenRecorded(
host, MaybeObject::FromObject(slot.load(code_cage_base())))) {
CHECK_GT(untyped_->count(slot.address()), 0);
}
}
void VisitCodeTarget(Tagged<InstructionStream> host,
RelocInfo* rinfo) override {
Tagged<Object> target =
InstructionStream::FromTargetAddress(rinfo->target_address());
if (ShouldHaveBeenRecorded(host, MaybeObject::FromObject(target))) {
CHECK(InTypedSet(SlotType::kCodeEntry, rinfo->pc()) ||
(rinfo->IsInConstantPool() &&
InTypedSet(SlotType::kConstPoolCodeEntry,
rinfo->constant_pool_entry_address())));
}
}
void VisitEmbeddedPointer(Tagged<InstructionStream> host,
RelocInfo* rinfo) override {
Tagged<Object> target = rinfo->target_object(cage_base());
if (ShouldHaveBeenRecorded(host, MaybeObject::FromObject(target))) {
CHECK(InTypedSet(SlotType::kEmbeddedObjectFull, rinfo->pc()) ||
InTypedSet(SlotType::kEmbeddedObjectCompressed, rinfo->pc()) ||
(rinfo->IsInConstantPool() &&
InTypedSet(SlotType::kConstPoolEmbeddedObjectCompressed,
rinfo->constant_pool_entry_address())) ||
(rinfo->IsInConstantPool() &&
InTypedSet(SlotType::kConstPoolEmbeddedObjectFull,
rinfo->constant_pool_entry_address())));
}
}
protected:
bool InUntypedSet(ObjectSlot slot) {
return untyped_->count(slot.address()) > 0;
}
private:
bool InTypedSet(SlotType type, Address slot) {
return typed_->count(std::make_pair(type, slot)) > 0;
}
std::set<Address>* untyped_;
std::set<std::pair<SlotType, Address>>* typed_;
};
class OldToNewSlotVerifyingVisitor : public SlotVerifyingVisitor {
public:
OldToNewSlotVerifyingVisitor(
Isolate* isolate, std::set<Address>* untyped,
std::set<std::pair<SlotType, Address>>* typed,
EphemeronRememberedSet::TableMap* ephemeron_remembered_set)
: SlotVerifyingVisitor(isolate, untyped, typed),
ephemeron_remembered_set_(ephemeron_remembered_set) {}
bool ShouldHaveBeenRecorded(Tagged<HeapObject> host,
MaybeObject target) override {
DCHECK_IMPLIES(target->IsStrongOrWeak() && Heap::InYoungGeneration(target),
Heap::InToPage(target));
return target->IsStrongOrWeak() && Heap::InYoungGeneration(target) &&
!Heap::InYoungGeneration(host);
}
void VisitEphemeron(Tagged<HeapObject> host, int index, ObjectSlot key,
ObjectSlot target) override {
VisitPointer(host, target);
if (v8_flags.minor_ms) return;
// Keys are handled separately and should never appear in this set.
CHECK(!InUntypedSet(key));
Tagged<Object> k = *key;
if (!ObjectInYoungGeneration(host) && ObjectInYoungGeneration(k)) {
Tagged<EphemeronHashTable> table = EphemeronHashTable::cast(host);
auto it = ephemeron_remembered_set_->find(table);
CHECK(it != ephemeron_remembered_set_->end());
int slot_index =
EphemeronHashTable::SlotToIndex(table.address(), key.address());
InternalIndex entry = EphemeronHashTable::IndexToEntry(slot_index);
CHECK(it->second.find(entry.as_int()) != it->second.end());
}
}
private:
EphemeronRememberedSet::TableMap* ephemeron_remembered_set_;
};
class OldToSharedSlotVerifyingVisitor : public SlotVerifyingVisitor {
public:
OldToSharedSlotVerifyingVisitor(Isolate* isolate, std::set<Address>* untyped,
std::set<std::pair<SlotType, Address>>* typed)
: SlotVerifyingVisitor(isolate, untyped, typed) {}
bool ShouldHaveBeenRecorded(Tagged<HeapObject> host,
MaybeObject target) override {
return target->IsStrongOrWeak() && Heap::InWritableSharedSpace(target) &&
!Heap::InYoungGeneration(host) && !host.InWritableSharedSpace();
}
};
template <RememberedSetType direction>
void CollectSlots(MemoryChunk* chunk, Address start, Address end,
std::set<Address>* untyped,
std::set<std::pair<SlotType, Address>>* typed) {
RememberedSet<direction>::Iterate(
chunk,
[start, end, untyped](MaybeObjectSlot slot) {
if (start <= slot.address() && slot.address() < end) {
untyped->insert(slot.address());
}
return KEEP_SLOT;
},
SlotSet::FREE_EMPTY_BUCKETS);
RememberedSet<direction>::IterateTyped(
chunk, [=](SlotType type, Address slot) {
if (start <= slot && slot < end) {
typed->insert(std::make_pair(type, slot));
}
return KEEP_SLOT;
});
}
// Helper class for collecting slot addresses.
class SlotCollectingVisitor final : public ObjectVisitor {
public:
void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
ObjectSlot end) override {
VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
}
void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) final {
for (MaybeObjectSlot p = start; p < end; ++p) {
slots_.push_back(p);
}
}
void VisitInstructionStreamPointer(Tagged<Code> host,
InstructionStreamSlot slot) override {
CHECK(V8_EXTERNAL_CODE_SPACE_BOOL);
#ifdef V8_EXTERNAL_CODE_SPACE
code_slots_.push_back(slot);
#endif
}
void VisitCodeTarget(Tagged<InstructionStream> host, RelocInfo* rinfo) final {
UNREACHABLE();
}
void VisitEmbeddedPointer(Tagged<InstructionStream> host,
RelocInfo* rinfo) override {
UNREACHABLE();
}
void VisitMapPointer(Tagged<HeapObject> object) override {
} // do nothing by default
int number_of_slots() { return static_cast<int>(slots_.size()); }
MaybeObjectSlot slot(int i) { return slots_[i]; }
#ifdef V8_EXTERNAL_CODE_SPACE
InstructionStreamSlot code_slot(int i) { return code_slots_[i]; }
int number_of_code_slots() { return static_cast<int>(code_slots_.size()); }
#endif
private:
std::vector<MaybeObjectSlot> slots_;
#ifdef V8_EXTERNAL_CODE_SPACE
std::vector<InstructionStreamSlot> code_slots_;
#endif
};
void HeapVerification::VerifyRememberedSetFor(Tagged<HeapObject> object) {
if (current_space_identity() == RO_SPACE ||
v8_flags.verify_heap_skip_remembered_set) {
return;
}
MemoryChunk* chunk = MemoryChunk::FromHeapObject(object);
Address start = object.address();
Address end = start + object->Size(cage_base_);
std::set<Address> old_to_new;
std::set<std::pair<SlotType, Address>> typed_old_to_new;
CollectSlots<OLD_TO_NEW>(chunk, start, end, &old_to_new, &typed_old_to_new);
CollectSlots<OLD_TO_NEW_BACKGROUND>(chunk, start, end, &old_to_new,
&typed_old_to_new);
OldToNewSlotVerifyingVisitor old_to_new_visitor(
isolate(), &old_to_new, &typed_old_to_new,
heap()->ephemeron_remembered_set()->tables());
object->IterateBody(cage_base_, &old_to_new_visitor);
std::set<Address> old_to_shared;
std::set<std::pair<SlotType, Address>> typed_old_to_shared;
CollectSlots<OLD_TO_SHARED>(chunk, start, end, &old_to_shared,
&typed_old_to_shared);
OldToSharedSlotVerifyingVisitor old_to_shared_visitor(
isolate(), &old_to_shared, &typed_old_to_shared);
object->IterateBody(cage_base_, &old_to_shared_visitor);
if (object.InWritableSharedSpace()) {
CHECK_NULL(chunk->slot_set<OLD_TO_SHARED>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_SHARED>());
CHECK_NULL(chunk->slot_set<OLD_TO_NEW>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW>());
CHECK_NULL(chunk->slot_set<OLD_TO_NEW_BACKGROUND>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW_BACKGROUND>());
}
if (Heap::InYoungGeneration(object)) {
CHECK_NULL(chunk->slot_set<OLD_TO_NEW>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW>());
CHECK_NULL(chunk->slot_set<OLD_TO_NEW_BACKGROUND>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_NEW_BACKGROUND>());
CHECK_NULL(chunk->slot_set<OLD_TO_OLD>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_OLD>());
CHECK_NULL(chunk->slot_set<OLD_TO_SHARED>());
CHECK_NULL(chunk->typed_slot_set<OLD_TO_SHARED>());
}
// TODO(v8:11797): Add old to old slot set verification once all weak objects
// have their own instance types and slots are recorded for all weak fields.
}
// static
void HeapVerifier::VerifyHeap(Heap* heap) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"), "V8.HeapVerification");
HeapVerification verifier(heap);
verifier.Verify();
}
// static
void HeapVerifier::VerifyReadOnlyHeap(Heap* heap) {
HeapVerification verifier(heap);
verifier.VerifyReadOnlyHeap();
}
// static
void HeapVerifier::VerifyObjectLayoutChangeIsAllowed(
Heap* heap, Tagged<HeapObject> object) {
if (object.InWritableSharedSpace()) {
// Out of objects in the shared heap, only strings can change layout.
DCHECK(IsString(object));
// Shared strings only change layout under GC, never concurrently.
if (IsShared(object)) {
Isolate* isolate = heap->isolate();
Isolate* shared_space_isolate = isolate->is_shared_space_isolate()
? isolate
: isolate->shared_space_isolate();
shared_space_isolate->global_safepoint()->AssertActive();
}
// Non-shared strings in the shared heap are allowed to change layout
// outside of GC like strings in non-shared heaps.
}
}
// static
void HeapVerifier::SetPendingLayoutChangeObject(Heap* heap,
Tagged<HeapObject> object) {
VerifyObjectLayoutChangeIsAllowed(heap, object);
DCHECK(pending_layout_change_object.is_null());
pending_layout_change_object = object;
}
// static
void HeapVerifier::VerifyObjectLayoutChange(Heap* heap,
Tagged<HeapObject> object,
Tagged<Map> new_map) {
// Object layout changes are currently not supported on background threads.
DCHECK_NULL(LocalHeap::Current());
if (!v8_flags.verify_heap) return;
VerifyObjectLayoutChangeIsAllowed(heap, object);
PtrComprCageBase cage_base(heap->isolate());
// Check that Heap::NotifyObjectLayoutChange was called for object transitions
// that are not safe for concurrent marking.
// If you see this check triggering for a freshly allocated object,
// use object->set_map_after_allocation() to initialize its map.
if (pending_layout_change_object.is_null()) {
VerifySafeMapTransition(heap, object, new_map);
} else {
DCHECK_EQ(pending_layout_change_object, object);
pending_layout_change_object = HeapObject();
}
}
// static
void HeapVerifier::VerifySafeMapTransition(Heap* heap,
Tagged<HeapObject> object,
Tagged<Map> new_map) {
PtrComprCageBase cage_base(heap->isolate());
if (IsJSObject(object, cage_base)) {
// Without double unboxing all in-object fields of a JSObject are tagged.
return;
}
if (IsString(object, cage_base) &&
(new_map == ReadOnlyRoots(heap).thin_two_byte_string_map() ||
new_map == ReadOnlyRoots(heap).thin_one_byte_string_map())) {
// When transitioning a string to ThinString,
// Heap::NotifyObjectLayoutChange doesn't need to be invoked because only
// tagged fields are introduced.
return;
}
if (v8_flags.shared_string_table && IsString(object, cage_base) &&
InstanceTypeChecker::IsInternalizedString(new_map->instance_type())) {
// In-place internalization does not change a string's fields.
//
// When sharing the string table, the setting and re-setting of maps below
// can race when there are parallel internalization operations, causing
// DCHECKs to fail.
return;
}
// Check that the set of slots before and after the transition match.
SlotCollectingVisitor old_visitor;
object->IterateFast(cage_base, &old_visitor);
MapWord old_map_word = object->map_word(cage_base, kRelaxedLoad);
// Temporarily set the new map to iterate new slots.
object->set_map_word(new_map, kRelaxedStore);
SlotCollectingVisitor new_visitor;
object->IterateFast(cage_base, &new_visitor);
// Restore the old map.
object->set_map_word(old_map_word.ToMap(), kRelaxedStore);
DCHECK_EQ(new_visitor.number_of_slots(), old_visitor.number_of_slots());
for (int i = 0; i < new_visitor.number_of_slots(); i++) {
DCHECK_EQ(new_visitor.slot(i), old_visitor.slot(i));
}
#ifdef V8_EXTERNAL_CODE_SPACE
DCHECK_EQ(new_visitor.number_of_code_slots(),
old_visitor.number_of_code_slots());
for (int i = 0; i < new_visitor.number_of_code_slots(); i++) {
DCHECK_EQ(new_visitor.code_slot(i), old_visitor.code_slot(i));
}
#endif // V8_EXTERNAL_CODE_SPACE
}
} // namespace internal
} // namespace v8
#endif // VERIFY_HEAP