1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/mem/verification.h"
17
18 #include "ecmascript/js_tagged_value-inl.h"
19 #include "ecmascript/mem/slots.h"
20 #include "ecmascript/mem/visitor.h"
21 #include "ecmascript/mem/concurrent_sweeper.h"
22
23 namespace panda::ecmascript {
LogErrorForObjSlot(const Heap * heap,const char * headerInfo,TaggedObject * obj,ObjectSlot slot,TaggedObject * value)24 void LogErrorForObjSlot(const Heap *heap, const char *headerInfo, TaggedObject *obj, ObjectSlot slot,
25 TaggedObject *value)
26 {
27 TaggedObject *slotValue = slot.GetTaggedObject();
28 Region *region = Region::ObjectAddressToRange(obj);
29 Region *valueRegion = Region::ObjectAddressToRange(value);
30 Region *slotRegion = Region::ObjectAddressToRange(slotValue);
31 LOG_GC(FATAL) << headerInfo
32 << ": gctype=" << heap->GetGCType()
33 << ", obj address=" << obj
34 << ", obj region=" << region
35 << ", obj space type=" << region->GetSpaceTypeName()
36 << ", obj type=" << JSHClass::DumpJSType(obj->GetClass()->GetObjectType())
37 << ", slot address=" << reinterpret_cast<void*>(slot.SlotAddress())
38 << ", slot value=" << slotValue
39 << ", slot value region=" << slotRegion
40 << ", slot value space type=" << slotRegion->GetSpaceTypeName()
41 << ", slot value type=" << JSHClass::DumpJSType(slotValue->GetClass()->GetObjectType())
42 << ", value address=" << value
43 << ", value region=" << valueRegion
44 << ", value space type=" << valueRegion->GetSpaceTypeName()
45 << ", value type=" << JSHClass::DumpJSType(value->GetClass()->GetObjectType())
46 << ", obj mark bit=" << region->Test(obj)
47 << ", obj slot olwToNew bit=" << region->TestOldToNew(slot.SlotAddress())
48 << ", obj slot value mark bit=" << slotRegion->Test(slotValue)
49 << ", value mark bit=" << valueRegion->Test(value);
50 UNREACHABLE();
51 }
52
LogErrorForObj(const Heap * heap,const char * headerInfo,TaggedObject * obj)53 void LogErrorForObj(const Heap *heap, const char *headerInfo, TaggedObject *obj)
54 {
55 Region *region = Region::ObjectAddressToRange(obj);
56 LOG_GC(FATAL) << headerInfo
57 << ": gctype=" << heap->GetGCType()
58 << ", obj address=" << obj
59 << ", obj value=" << ObjectSlot(ToUintPtr(obj)).GetTaggedObject()
60 << ", obj region=" << region
61 << ", obj space type=" << region->GetSpaceTypeName()
62 << ", obj type=" << JSHClass::DumpJSType(obj->GetClass()->GetObjectType())
63 << ", obj mark bit=" << region->Test(obj);
64 UNREACHABLE();
65 }
66
67 // Only used for verify InactiveSemiSpace
VerifyInactiveSemiSpaceMarkedObject(const Heap * heap,void * addr)68 void VerifyObjectVisitor::VerifyInactiveSemiSpaceMarkedObject(const Heap *heap, void *addr)
69 {
70 TaggedObject *object = reinterpret_cast<TaggedObject*>(addr);
71 Region *objectRegion = Region::ObjectAddressToRange(object);
72 if (!objectRegion->InInactiveSemiSpace()) {
73 LogErrorForObj(heap, "Verify InactiveSemiSpaceMarkedObject: Object is not in InactiveSemiSpace.", object);
74 } else {
75 MarkWord word(object);
76 if (!word.IsForwardingAddress()) {
77 LogErrorForObj(heap, "Verify InactiveSemiSpaceMarkedObject: not forwarding address.", object);
78 } else {
79 ObjectSlot slot(ToUintPtr(object));
80 TaggedObject *value = word.ToForwardingAddress();
81 Region *valueRegion = Region::ObjectAddressToRange(value);
82 if (valueRegion->InInactiveSemiSpace()) {
83 LogErrorForObjSlot(heap, "Verify InactiveSemiSpaceMarkedObject: forwarding address, "
84 "but InactiveSemiSpace(FromSpace) Object.", object, slot, value);
85 }
86 }
87 }
88 }
89
90 // Verify the object body
VisitAllObjects(TaggedObject * obj)91 void VerifyObjectVisitor::VisitAllObjects(TaggedObject *obj)
92 {
93 auto jsHclass = obj->GetClass();
94 objXRay_.VisitObjectBody<VisitType::OLD_GC_VISIT>(
95 obj, jsHclass, [this](TaggedObject *root, ObjectSlot start, ObjectSlot end,
96 VisitObjectArea area) {
97 if (area == VisitObjectArea::IN_OBJECT) {
98 auto hclass = root->GetClass();
99 ASSERT(!hclass->IsAllTaggedProp());
100 int index = 0;
101 for (ObjectSlot slot = start; slot < end; slot++) {
102 auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
103 auto attr = layout->GetAttr(index++);
104 if (attr.IsTaggedRep()) {
105 VerifyObjectSlotLegal(slot, root);
106 }
107 }
108 return;
109 }
110 for (ObjectSlot slot = start; slot < end; slot++) {
111 VerifyObjectSlotLegal(slot, root);
112 }
113 });
114 }
115
VerifyObjectSlotLegal(ObjectSlot slot,TaggedObject * object) const116 void VerifyObjectVisitor::VerifyObjectSlotLegal(ObjectSlot slot, TaggedObject *object) const
117 {
118 JSTaggedValue value(slot.GetTaggedType());
119 if (value.IsWeak()) {
120 if (ToUintPtr(value.GetTaggedWeakRef()) < INVALID_THRESHOLD) {
121 LogErrorForObjSlot(heap_, "Heap verify detected an invalid value.",
122 object, slot, value.GetTaggedWeakRef());
123 }
124 if (!heap_->IsAlive(value.GetTaggedWeakRef())) {
125 LogErrorForObjSlot(heap_, "Heap verify detected a dead weak object.",
126 object, slot, value.GetTaggedWeakRef());
127 ++(*failCount_);
128 }
129 } else if (value.IsHeapObject()) {
130 if (ToUintPtr(value.GetTaggedObject()) < INVALID_THRESHOLD) {
131 LogErrorForObjSlot(heap_, "Heap verify detected an invalid value.",
132 object, slot, value.GetTaggedObject());
133 }
134 if (!heap_->IsAlive(value.GetTaggedObject())) {
135 LogErrorForObjSlot(heap_, "Heap verify detected a dead object.",
136 object, slot, value.GetTaggedObject());
137 ++(*failCount_);
138 }
139 switch (verifyKind_) {
140 case VerifyKind::VERIFY_PRE_GC:
141 case VerifyKind::VERIFY_POST_GC:
142 break;
143 case VerifyKind::VERIFY_CONCURRENT_MARK_YOUNG:
144 VerifyMarkYoung(object, slot, value.GetTaggedObject());
145 break;
146 case VerifyKind::VERIFY_EVACUATE_YOUNG:
147 VerifyEvacuateYoung(object, slot, value.GetTaggedObject());
148 break;
149 case VerifyKind::VERIFY_CONCURRENT_MARK_FULL:
150 VerifyMarkFull(object, slot, value.GetTaggedObject());
151 break;
152 case VerifyKind::VERIFY_EVACUATE_OLD:
153 VerifyEvacuateOld(object, slot, value.GetTaggedObject());
154 break;
155 case VerifyKind::VERIFY_EVACUATE_FULL:
156 VerifyEvacuateFull(object, slot, value.GetTaggedObject());
157 break;
158 default:
159 LOG_GC(FATAL) << "unknown verify kind:" << static_cast<size_t>(verifyKind_);
160 UNREACHABLE();
161 }
162 }
163 }
164
VerifyMarkYoung(TaggedObject * object,ObjectSlot slot,TaggedObject * value) const165 void VerifyObjectVisitor::VerifyMarkYoung(TaggedObject *object, ObjectSlot slot, TaggedObject *value) const
166 {
167 Region *objectRegion = Region::ObjectAddressToRange(object);
168 Region *valueRegion = Region::ObjectAddressToRange(value);
169 if (!objectRegion->InYoungSpace() && valueRegion->InYoungSpace()) {
170 if (!objectRegion->TestOldToNew(slot.SlotAddress())) {
171 LogErrorForObjSlot(heap_, "Verify MarkYoung: Old object, slot miss old_to_new bit.", object, slot, value);
172 } else if (!valueRegion->Test(value)) {
173 LogErrorForObjSlot(heap_, "Verify MarkYoung: Old object, slot has old_to_new bit, miss gc_mark bit.",
174 object, slot, value);
175 }
176 }
177 if (objectRegion->Test(object)) {
178 if (!objectRegion->InYoungSpace() && !objectRegion->InAppSpawnSpace() && !objectRegion->InReadOnlySpace()) {
179 LogErrorForObj(heap_, "Verify MarkYoung: Marked object, NOT in Young/AppSpawn/ReadOnly Space", object);
180 }
181 if (valueRegion->InYoungSpace() && !valueRegion->Test(value)) {
182 LogErrorForObjSlot(heap_, "Verify MarkYoung: Marked object, slot in YoungSpace, miss gc_mark bit.",
183 object, slot, value);
184 }
185 if (valueRegion->Test(value) && !(valueRegion->InYoungSpace() || valueRegion->InAppSpawnSpace() ||
186 valueRegion->InReadOnlySpace())) {
187 LogErrorForObjSlot(heap_, "Verify MarkYoung: Marked object, slot marked, but NOT in "
188 "Young/AppSpawn/ReadOnly Space.", object, slot, value);
189 }
190 }
191 }
192
VerifyEvacuateYoung(TaggedObject * object,ObjectSlot slot,TaggedObject * value) const193 void VerifyObjectVisitor::VerifyEvacuateYoung(TaggedObject *object, ObjectSlot slot, TaggedObject *value) const
194 {
195 Region *objectRegion = Region::ObjectAddressToRange(object);
196 Region *valueRegion = Region::ObjectAddressToRange(value);
197 if (!objectRegion->InYoungSpace()) {
198 if (objectRegion->TestOldToNew(slot.SlotAddress())) {
199 if (!valueRegion->InActiveSemiSpace()) {
200 LogErrorForObjSlot(heap_, "Verify EvacuateYoung: Old object, slot old_to_new bit = 1, "
201 "but NOT ActiveSpace(ToSpace) object.", object, slot, value);
202 }
203 } else {
204 if (valueRegion->InYoungSpace()) {
205 LogErrorForObjSlot(heap_, "Verify EvacuateYoung: Old object, slot old_to_new bit = 0, "
206 "but YoungSpace object.", object, slot, value);
207 }
208 }
209 }
210 if (objectRegion->InActiveSemiSpace()) {
211 if (valueRegion->InInactiveSemiSpace()) {
212 LogErrorForObjSlot(heap_, "Verify EvacuateYoung: ActiveSpace object, slot in InactiveSpace(FromSpace).",
213 object, slot, value);
214 }
215 }
216 }
217
VerifyMarkFull(TaggedObject * object,ObjectSlot slot,TaggedObject * value) const218 void VerifyObjectVisitor::VerifyMarkFull(TaggedObject *object, ObjectSlot slot, TaggedObject *value) const
219 {
220 Region *objectRegion = Region::ObjectAddressToRange(object);
221 Region *valueRegion = Region::ObjectAddressToRange(value);
222 if (!objectRegion->InYoungSpace() && valueRegion->InYoungSpace()) {
223 if (!objectRegion->TestOldToNew(slot.SlotAddress())) {
224 LogErrorForObjSlot(heap_, "Verify MarkFull: Old object, slot miss old_to_new bit.", object, slot, value);
225 }
226 }
227 if (objectRegion->Test(object)) {
228 if (!valueRegion->Test(value)) {
229 LogErrorForObjSlot(heap_, "Verify MarkFull: Marked object, slot miss gc_mark bit.", object, slot, value);
230 }
231 }
232 }
233
VerifyEvacuateOld(TaggedObject * root,ObjectSlot slot,TaggedObject * value) const234 void VerifyObjectVisitor::VerifyEvacuateOld([[maybe_unused]]TaggedObject *root,
235 [[maybe_unused]]ObjectSlot slot,
236 [[maybe_unused]]TaggedObject *value) const
237 {
238 VerifyEvacuateYoung(root, slot, value);
239 }
240
VerifyEvacuateFull(TaggedObject * root,ObjectSlot slot,TaggedObject * value) const241 void VerifyObjectVisitor::VerifyEvacuateFull([[maybe_unused]]TaggedObject *root,
242 [[maybe_unused]]ObjectSlot slot,
243 [[maybe_unused]]TaggedObject *value) const
244 {
245 VerifyEvacuateYoung(root, slot, value);
246 }
247
operator ()(TaggedObject * obj,JSTaggedValue value)248 void VerifyObjectVisitor::operator()(TaggedObject *obj, JSTaggedValue value)
249 {
250 ObjectSlot slot(reinterpret_cast<uintptr_t>(obj));
251 if (!value.IsHeapObject()) {
252 LOG_GC(DEBUG) << "Heap object(" << slot.SlotAddress() << ") old to new rset fail: value is "
253 << slot.GetTaggedType();
254 return;
255 }
256
257 TaggedObject *object = value.GetRawTaggedObject();
258 auto region = Region::ObjectAddressToRange(object);
259 if (!region->InYoungSpace()) {
260 LOG_GC(ERROR) << "Heap object(" << slot.GetTaggedType() << ") old to new rset fail: value("
261 << slot.GetTaggedObject() << "/"
262 << JSHClass::DumpJSType(slot.GetTaggedObject()->GetClass()->GetObjectType())
263 << ")" << " in " << region->GetSpaceTypeName();
264 ++(*failCount_);
265 }
266 }
267
VerifyAll() const268 void Verification::VerifyAll() const
269 {
270 [[maybe_unused]] VerifyScope verifyScope(heap_);
271 heap_->GetSweeper()->EnsureAllTaskFinished();
272 size_t result = VerifyRoot();
273 result += VerifyHeap();
274 if (result > 0) {
275 LOG_GC(FATAL) << "Verify (type=" << static_cast<uint8_t>(verifyKind_)
276 << ") corrupted and " << result << " corruptions";
277 }
278 }
279
VerifyRoot() const280 size_t Verification::VerifyRoot() const
281 {
282 size_t failCount = 0;
283 RootVisitor visitor = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot slot) {
284 VerifyObjectSlot(slot, &failCount);
285 };
286 RootRangeVisitor rangeVisitor = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) {
287 for (ObjectSlot slot = start; slot < end; slot++) {
288 VerifyObjectSlot(slot, &failCount);
289 }
290 };
291 RootBaseAndDerivedVisitor derivedVisitor =
292 []([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base, [[maybe_unused]] ObjectSlot derived,
293 [[maybe_unused]] uintptr_t baseOldObject) {
294 };
295 objXRay_.VisitVMRoots(visitor, rangeVisitor, derivedVisitor);
296 if (failCount > 0) {
297 LOG_GC(ERROR) << "VerifyRoot detects deadObject count is " << failCount;
298 }
299
300 return failCount;
301 }
302
VerifyHeap() const303 size_t Verification::VerifyHeap() const
304 {
305 size_t failCount = heap_->VerifyHeapObjects(verifyKind_);
306 if (failCount > 0) {
307 LOG_GC(ERROR) << "VerifyHeap detects deadObject count is " << failCount;
308 }
309 return failCount;
310 }
311
VerifyOldToNewRSet() const312 size_t Verification::VerifyOldToNewRSet() const
313 {
314 size_t failCount = heap_->VerifyOldToNewRSet(verifyKind_);
315 if (failCount > 0) {
316 LOG_GC(ERROR) << "VerifyOldToNewRSet detects non new space count is " << failCount;
317 }
318 return failCount;
319 }
320
VerifyObjectSlot(const ObjectSlot & slot,size_t * failCount) const321 void Verification::VerifyObjectSlot(const ObjectSlot &slot, size_t *failCount) const
322 {
323 JSTaggedValue value(slot.GetTaggedType());
324 if (value.IsWeak()) {
325 VerifyObjectVisitor(heap_, failCount, verifyKind_)(value.GetTaggedWeakRef());
326 } else if (value.IsHeapObject()) {
327 VerifyObjectVisitor(heap_, failCount, verifyKind_)(value.GetTaggedObject());
328 }
329 }
330 } // namespace panda::ecmascript
331