/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/mem/verification.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/mem/slots.h" #include "ecmascript/mem/visitor.h" #include "ecmascript/mem/concurrent_sweeper.h" namespace panda::ecmascript { void LogErrorForObjSlot(const Heap *heap, const char *headerInfo, TaggedObject *obj, ObjectSlot slot, TaggedObject *value) { TaggedObject *slotValue = slot.GetTaggedObject(); Region *region = Region::ObjectAddressToRange(obj); Region *valueRegion = Region::ObjectAddressToRange(value); Region *slotRegion = Region::ObjectAddressToRange(slotValue); LOG_GC(FATAL) << headerInfo << ": gctype=" << heap->GetGCType() << ", obj address=" << obj << ", obj region=" << region << ", obj space type=" << region->GetSpaceTypeName() << ", obj type=" << JSHClass::DumpJSType(obj->GetClass()->GetObjectType()) << ", slot address=" << reinterpret_cast<void*>(slot.SlotAddress()) << ", slot value=" << slotValue << ", slot value region=" << slotRegion << ", slot value space type=" << slotRegion->GetSpaceTypeName() << ", slot value type=" << JSHClass::DumpJSType(slotValue->GetClass()->GetObjectType()) << ", value address=" << value << ", value region=" << valueRegion << ", value space type=" << valueRegion->GetSpaceTypeName() << ", value type=" << JSHClass::DumpJSType(value->GetClass()->GetObjectType()) << ", obj mark bit=" << region->Test(obj) << ", obj slot olwToNew bit=" << region->TestOldToNew(slot.SlotAddress()) << ", obj slot value mark bit=" << slotRegion->Test(slotValue) << ", value mark bit=" << valueRegion->Test(value); UNREACHABLE(); } void LogErrorForObj(const Heap *heap, const char *headerInfo, TaggedObject *obj) { Region *region = Region::ObjectAddressToRange(obj); LOG_GC(FATAL) << headerInfo << ": gctype=" << heap->GetGCType() << ", obj address=" << obj << ", obj value=" << ObjectSlot(ToUintPtr(obj)).GetTaggedObject() << ", obj region=" << region << ", obj space type=" << region->GetSpaceTypeName() << ", obj type=" << JSHClass::DumpJSType(obj->GetClass()->GetObjectType()) << ", obj mark bit=" << region->Test(obj); UNREACHABLE(); } // Only used for verify InactiveSemiSpace void VerifyObjectVisitor::VerifyInactiveSemiSpaceMarkedObject(const Heap *heap, void *addr) { TaggedObject *object = reinterpret_cast<TaggedObject*>(addr); Region *objectRegion = Region::ObjectAddressToRange(object); if (!objectRegion->InInactiveSemiSpace()) { LogErrorForObj(heap, "Verify InactiveSemiSpaceMarkedObject: Object is not in InactiveSemiSpace.", object); } else { MarkWord word(object); if (!word.IsForwardingAddress()) { LogErrorForObj(heap, "Verify InactiveSemiSpaceMarkedObject: not forwarding address.", object); } else { ObjectSlot slot(ToUintPtr(object)); TaggedObject *value = word.ToForwardingAddress(); Region *valueRegion = Region::ObjectAddressToRange(value); if (valueRegion->InInactiveSemiSpace()) { LogErrorForObjSlot(heap, "Verify InactiveSemiSpaceMarkedObject: forwarding address, " "but InactiveSemiSpace(FromSpace) Object.", object, slot, value); } } } } // Verify the object body void VerifyObjectVisitor::VisitAllObjects(TaggedObject *obj) { auto jsHclass = obj->GetClass(); objXRay_.VisitObjectBody<VisitType::OLD_GC_VISIT>( obj, jsHclass, [this](TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) { if (area == VisitObjectArea::IN_OBJECT) { auto hclass = root->GetClass(); ASSERT(!hclass->IsAllTaggedProp()); int index = 0; for (ObjectSlot slot = start; slot < end; slot++) { auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); auto attr = layout->GetAttr(index++); if (attr.IsTaggedRep()) { VerifyObjectSlotLegal(slot, root); } } return; } for (ObjectSlot slot = start; slot < end; slot++) { VerifyObjectSlotLegal(slot, root); } }); } void VerifyObjectVisitor::VerifyObjectSlotLegal(ObjectSlot slot, TaggedObject *object) const { JSTaggedValue value(slot.GetTaggedType()); if (value.IsWeak()) { if (ToUintPtr(value.GetTaggedWeakRef()) < INVALID_THRESHOLD) { LogErrorForObjSlot(heap_, "Heap verify detected an invalid value.", object, slot, value.GetTaggedWeakRef()); } if (!heap_->IsAlive(value.GetTaggedWeakRef())) { LogErrorForObjSlot(heap_, "Heap verify detected a dead weak object.", object, slot, value.GetTaggedWeakRef()); ++(*failCount_); } } else if (value.IsHeapObject()) { if (ToUintPtr(value.GetTaggedObject()) < INVALID_THRESHOLD) { LogErrorForObjSlot(heap_, "Heap verify detected an invalid value.", object, slot, value.GetTaggedObject()); } if (!heap_->IsAlive(value.GetTaggedObject())) { LogErrorForObjSlot(heap_, "Heap verify detected a dead object.", object, slot, value.GetTaggedObject()); ++(*failCount_); } switch (verifyKind_) { case VerifyKind::VERIFY_PRE_GC: case VerifyKind::VERIFY_POST_GC: break; case VerifyKind::VERIFY_CONCURRENT_MARK_YOUNG: VerifyMarkYoung(object, slot, value.GetTaggedObject()); break; case VerifyKind::VERIFY_EVACUATE_YOUNG: VerifyEvacuateYoung(object, slot, value.GetTaggedObject()); break; case VerifyKind::VERIFY_CONCURRENT_MARK_FULL: VerifyMarkFull(object, slot, value.GetTaggedObject()); break; case VerifyKind::VERIFY_EVACUATE_OLD: VerifyEvacuateOld(object, slot, value.GetTaggedObject()); break; case VerifyKind::VERIFY_EVACUATE_FULL: VerifyEvacuateFull(object, slot, value.GetTaggedObject()); break; default: LOG_GC(FATAL) << "unknown verify kind:" << static_cast<size_t>(verifyKind_); UNREACHABLE(); } } } void VerifyObjectVisitor::VerifyMarkYoung(TaggedObject *object, ObjectSlot slot, TaggedObject *value) const { Region *objectRegion = Region::ObjectAddressToRange(object); Region *valueRegion = Region::ObjectAddressToRange(value); if (!objectRegion->InYoungSpace() && valueRegion->InYoungSpace()) { if (!objectRegion->TestOldToNew(slot.SlotAddress())) { LogErrorForObjSlot(heap_, "Verify MarkYoung: Old object, slot miss old_to_new bit.", object, slot, value); } else if (!valueRegion->Test(value)) { LogErrorForObjSlot(heap_, "Verify MarkYoung: Old object, slot has old_to_new bit, miss gc_mark bit.", object, slot, value); } } if (objectRegion->Test(object)) { if (!objectRegion->InYoungSpace() && !objectRegion->InAppSpawnSpace() && !objectRegion->InReadOnlySpace()) { LogErrorForObj(heap_, "Verify MarkYoung: Marked object, NOT in Young/AppSpawn/ReadOnly Space", object); } if (valueRegion->InYoungSpace() && !valueRegion->Test(value)) { LogErrorForObjSlot(heap_, "Verify MarkYoung: Marked object, slot in YoungSpace, miss gc_mark bit.", object, slot, value); } if (valueRegion->Test(value) && !(valueRegion->InYoungSpace() || valueRegion->InAppSpawnSpace() || valueRegion->InReadOnlySpace())) { LogErrorForObjSlot(heap_, "Verify MarkYoung: Marked object, slot marked, but NOT in " "Young/AppSpawn/ReadOnly Space.", object, slot, value); } } } void VerifyObjectVisitor::VerifyEvacuateYoung(TaggedObject *object, ObjectSlot slot, TaggedObject *value) const { Region *objectRegion = Region::ObjectAddressToRange(object); Region *valueRegion = Region::ObjectAddressToRange(value); if (!objectRegion->InYoungSpace()) { if (objectRegion->TestOldToNew(slot.SlotAddress())) { if (!valueRegion->InActiveSemiSpace()) { LogErrorForObjSlot(heap_, "Verify EvacuateYoung: Old object, slot old_to_new bit = 1, " "but NOT ActiveSpace(ToSpace) object.", object, slot, value); } } else { if (valueRegion->InYoungSpace()) { LogErrorForObjSlot(heap_, "Verify EvacuateYoung: Old object, slot old_to_new bit = 0, " "but YoungSpace object.", object, slot, value); } } } if (objectRegion->InActiveSemiSpace()) { if (valueRegion->InInactiveSemiSpace()) { LogErrorForObjSlot(heap_, "Verify EvacuateYoung: ActiveSpace object, slot in InactiveSpace(FromSpace).", object, slot, value); } } } void VerifyObjectVisitor::VerifyMarkFull(TaggedObject *object, ObjectSlot slot, TaggedObject *value) const { Region *objectRegion = Region::ObjectAddressToRange(object); Region *valueRegion = Region::ObjectAddressToRange(value); if (!objectRegion->InYoungSpace() && valueRegion->InYoungSpace()) { if (!objectRegion->TestOldToNew(slot.SlotAddress())) { LogErrorForObjSlot(heap_, "Verify MarkFull: Old object, slot miss old_to_new bit.", object, slot, value); } } if (objectRegion->Test(object)) { if (!valueRegion->Test(value)) { LogErrorForObjSlot(heap_, "Verify MarkFull: Marked object, slot miss gc_mark bit.", object, slot, value); } } } void VerifyObjectVisitor::VerifyEvacuateOld([[maybe_unused]]TaggedObject *root, [[maybe_unused]]ObjectSlot slot, [[maybe_unused]]TaggedObject *value) const { VerifyEvacuateYoung(root, slot, value); } void VerifyObjectVisitor::VerifyEvacuateFull([[maybe_unused]]TaggedObject *root, [[maybe_unused]]ObjectSlot slot, [[maybe_unused]]TaggedObject *value) const { VerifyEvacuateYoung(root, slot, value); } void VerifyObjectVisitor::operator()(TaggedObject *obj, JSTaggedValue value) { ObjectSlot slot(reinterpret_cast<uintptr_t>(obj)); if (!value.IsHeapObject()) { LOG_GC(DEBUG) << "Heap object(" << slot.SlotAddress() << ") old to new rset fail: value is " << slot.GetTaggedType(); return; } TaggedObject *object = value.GetRawTaggedObject(); auto region = Region::ObjectAddressToRange(object); if (!region->InYoungSpace()) { LOG_GC(ERROR) << "Heap object(" << slot.GetTaggedType() << ") old to new rset fail: value(" << slot.GetTaggedObject() << "/" << JSHClass::DumpJSType(slot.GetTaggedObject()->GetClass()->GetObjectType()) << ")" << " in " << region->GetSpaceTypeName(); ++(*failCount_); } } void Verification::VerifyAll() const { [[maybe_unused]] VerifyScope verifyScope(heap_); heap_->GetSweeper()->EnsureAllTaskFinished(); size_t result = VerifyRoot(); result += VerifyHeap(); if (result > 0) { LOG_GC(FATAL) << "Verify (type=" << static_cast<uint8_t>(verifyKind_) << ") corrupted and " << result << " corruptions"; } } size_t Verification::VerifyRoot() const { size_t failCount = 0; RootVisitor visitor = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot slot) { VerifyObjectSlot(slot, &failCount); }; RootRangeVisitor rangeVisitor = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { for (ObjectSlot slot = start; slot < end; slot++) { VerifyObjectSlot(slot, &failCount); } }; RootBaseAndDerivedVisitor derivedVisitor = []([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base, [[maybe_unused]] ObjectSlot derived, [[maybe_unused]] uintptr_t baseOldObject) { }; objXRay_.VisitVMRoots(visitor, rangeVisitor, derivedVisitor); if (failCount > 0) { LOG_GC(ERROR) << "VerifyRoot detects deadObject count is " << failCount; } return failCount; } size_t Verification::VerifyHeap() const { size_t failCount = heap_->VerifyHeapObjects(verifyKind_); if (failCount > 0) { LOG_GC(ERROR) << "VerifyHeap detects deadObject count is " << failCount; } return failCount; } size_t Verification::VerifyOldToNewRSet() const { size_t failCount = heap_->VerifyOldToNewRSet(verifyKind_); if (failCount > 0) { LOG_GC(ERROR) << "VerifyOldToNewRSet detects non new space count is " << failCount; } return failCount; } void Verification::VerifyObjectSlot(const ObjectSlot &slot, size_t *failCount) const { JSTaggedValue value(slot.GetTaggedType()); if (value.IsWeak()) { VerifyObjectVisitor(heap_, failCount, verifyKind_)(value.GetTaggedWeakRef()); } else if (value.IsHeapObject()) { VerifyObjectVisitor(heap_, failCount, verifyKind_)(value.GetTaggedObject()); } } } // namespace panda::ecmascript