1 /*
2 * Copyright (c) 2025 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 "verification.h"
17
18 #include "ark_collector/ark_collector.h"
19 #include "allocator/region_desc.h"
20 #include "allocator/region_space.h"
21 #include "common/mark_work_stack.h"
22 #include "common/type_def.h"
23 #include "common_components/log/log.h"
24 #include "common_interfaces/objects/base_object.h"
25 #include "common_interfaces/objects/base_state_word.h"
26 #include "common_interfaces/objects/ref_field.h"
27 #include "macros.h"
28 #include "mutator/mutator_manager.h"
29 #include "securec.h"
30 #include "thread/mutator_base.h"
31 #include <iomanip>
32 #include <sstream>
33 #include <unordered_set>
34
35 /*
36 * Heap Verify:
37 * Checks heap invariants after each GC mark, copy and fix phase. During the check, the world is stopped.
38 * Enabled by default for debug mode. Controlled by gn option `ets_runtime_enable_heap_verify`.
39 *
40 * RB DFX:
41 * Force to use STW GC. Force to use read barrier out of GC.
42 * After GC is finished, set the lowerst bit(WEAK_TAG) of RefField which is not root or doesn't point to pinned objects.
43 * The read barrier is responsible to remove the WEAK_TAG for properly deferencing the object.
44 * Disabled by defualt. Controlled by gn-option `ets_runtime_enable_rb_dfx`.
45 *
46 * Example:
47 * standalone:
48 * python ark.py x64.release --gn-args="ets_runtime_enable_heap_verify=true ets_runtime_enable_rb_dfx=true"
49 * openharmony:
50 * ./build_system.sh --gn-args="ets_runtime_enable_heap_verify=true ets_runtime_enable_rb_dfx=true" ...
51 */
52
53 namespace common {
54 void VisitRoots(const RefFieldVisitor& visitorFunc);
55 void VisitWeakRoots(const WeakRefFieldVisitor& visitorFunc);
56
57 #define CONTEXT " at " << __FILE__ << ":" << __LINE__ << std::endl
58
HexDump(const void * address,size_t length)59 std::string HexDump(const void* address, size_t length)
60 {
61 static constexpr size_t hexDigitsPerByte = 2;
62 static constexpr size_t wordSize = sizeof(uint64_t);
63 static constexpr size_t addressWidth = sizeof(void*) * hexDigitsPerByte;
64
65 std::ostringstream oss;
66 oss << std::hex << std::setfill('0');
67
68 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(address);
69
70 for (size_t i = 0; i < length; i += wordSize) {
71 // Print address
72 oss << "0x" << std::setw(addressWidth) << reinterpret_cast<uintptr_t>(ptr + i) << ": ";
73
74 // Print content
75 uint64_t word = 0;
76 size_t bytesToRead = std::min(wordSize, length - i);
77 memcpy_s(&word, sizeof(uint64_t), ptr + i, bytesToRead);
78
79 oss << "0x" << std::setw(wordSize * hexDigitsPerByte) << word << std::endl;
80 }
81
82 return oss.str();
83 }
84
GetObjectInfo(const BaseObject * obj)85 std::string GetObjectInfo(const BaseObject* obj)
86 {
87 constexpr size_t defaultInfoLength = 64;
88
89 std::ostringstream s;
90 s << std::hex << std::endl << ">>> address: 0x" << obj << std::endl;
91
92 s << "> Raw memory:" << std::endl;
93 if (obj == nullptr) {
94 s << "Skip: nullptr(Ref of nullptr might be a root, or Ref is iterated in region)" << std::endl;
95 } else {
96 s << std::hex << HexDump((void*) obj, defaultInfoLength);
97 }
98
99 s << "> Region Info:" << std::endl;
100 if (!Heap::IsHeapAddress(obj)) {
101 s << "Skip: Object is not in heap range" << std::endl;
102 } else {
103 auto region = RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(obj));
104 s << std::hex << "Type: 0x" << (int) region->GetRegionType() << ", "
105 << "Base: 0x" << region->GetRegionBase() << ", "
106 << "Start: 0x" << region->GetRegionStart() << ", "
107 << "End: 0x" << region->GetRegionEnd() << ", "
108 << "AllocPtr: 0x" << region->GetRegionAllocPtr() << ", "
109 << "MarkingLine: 0x" << region->GetMarkingLine() << ", "
110 << "CopyLine: 0x" << region->GetCopyLine() << std::endl;
111 }
112
113 return s.str();
114 }
115
GetRefInfo(const RefField<> & ref)116 std::string GetRefInfo(const RefField<>& ref)
117 {
118 std::ostringstream s;
119 s << std::hex << std::endl << ">>> Ref value: 0x" << ref.GetFieldValue();
120 if (Heap::IsTaggedObject(ref.GetFieldValue())) {
121 s << GetObjectInfo(ref.GetTargetObject()) << std::endl;
122 } else {
123 s << "> Raw memory:" << std::endl
124 << "Skip: primitive" << std::endl;
125 }
126 return s.str();
127 }
128
IsValidRef(const BaseObject * obj,const RefField<> & ref)129 void IsValidRef(const BaseObject* obj, const RefField<>& ref)
130 {
131 // Maybe we need to check ref later
132 // ...
133
134 CHECKF(Heap::IsTaggedObject(ref.GetFieldValue()))
135 << CONTEXT
136 << "Object: " << GetObjectInfo(obj) << std::endl
137 << "Ref: " << GetRefInfo(ref) << std::endl;
138
139 // check referenee
140 auto refObj = ref.GetTargetObject();
141
142 CHECKF(Heap::IsHeapAddress(refObj))
143 << CONTEXT << std::hex
144 << "Object address: 0x" << reinterpret_cast<MAddress>(refObj) << ","
145 << "Heap range: [0x" << Heap::heapStartAddr_ << ", 0x" << Heap::heapCurrentEnd_ << "]";
146
147 auto region = RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(refObj));
148 CHECKF(region->GetRegionType() != RegionDesc::RegionType::GARBAGE_REGION)
149 << CONTEXT
150 << "Object: " << GetObjectInfo(obj) << std::endl
151 << "Ref: " << GetRefInfo(ref) << std::endl;
152 CHECKF(region->GetRegionType() != RegionDesc::RegionType::FREE_REGION)
153 << CONTEXT
154 << "Object: " << GetObjectInfo(obj) << std::endl
155 << "Ref: " << GetRefInfo(ref) << std::endl;
156
157 CHECKF(!refObj->IsForwarding() && !refObj->IsForwarded())
158 << CONTEXT
159 << "Object: " << GetObjectInfo(obj) << std::endl
160 << "Ref: " << GetRefInfo(ref) << std::endl;
161
162 CHECKF(refObj->IsValidObject() != 0)
163 << CONTEXT
164 << "Object: " << GetObjectInfo(obj) << std::endl
165 << "Ref: " << GetRefInfo(ref) << std::endl;
166
167 CHECKF(refObj->GetSize() != 0)
168 << CONTEXT
169 << "Object: " << GetObjectInfo(obj) << std::endl
170 << "Ref: " << GetRefInfo(ref) << std::endl;
171 }
172
173 class VerifyVisitor {
174 public:
VerifyRef(const BaseObject * obj,RefField<> & ref)175 void VerifyRef(const BaseObject* obj, RefField<>& ref)
176 {
177 VerifyRefImpl(obj, ref);
178 count_++;
179 }
180
VerifyRef(const BaseObject * obj,const RefField<> & ref)181 void VerifyRef(const BaseObject* obj, const RefField<>& ref)
182 {
183 VerifyRefImpl(obj, ref);
184 count_++;
185 }
186
VerifyRefCount() const187 size_t VerifyRefCount() const
188 {
189 return count_;
190 }
191
192 protected:
VerifyRefImpl(const BaseObject * obj,RefField<> & ref)193 virtual void VerifyRefImpl(const BaseObject* obj, RefField<>& ref)
194 {
195 VerifyRefImpl(obj, static_cast<const RefField<>&>(ref));
196 }
197
VerifyRefImpl(const BaseObject * obj,const RefField<> & ref)198 virtual void VerifyRefImpl(const BaseObject* obj, const RefField<>& ref)
199 {
200 UNREACHABLE();
201 }
202
203 private:
204 size_t count_ = 0;
205 };
206
207 template <bool IsSTWRootVerify = true>
208 class AfterMarkVisitor : public VerifyVisitor {
209 public:
VerifyRefImpl(const BaseObject * obj,const RefField<> & ref)210 void VerifyRefImpl(const BaseObject* obj, const RefField<>& ref) override
211 {
212 IsValidRef(obj, ref);
213
214 // check remarked objects, so they must be in one of the states below
215 auto refObj = ref.GetTargetObject();
216 RegionDesc *region = RegionDesc::GetRegionDescAt(reinterpret_cast<HeapAddress>(refObj));
217
218 // if obj is nullptr, this means it is a root object
219 // We expect root objects to be already forwarded: assert(!region->isFromRegion())
220 if (obj == nullptr) {
221 if constexpr (IsSTWRootVerify) {
222 CHECKF(!region->IsFromRegion())
223 << CONTEXT << "Object: " << GetObjectInfo(obj) << std::endl
224 << "Ref: " << GetRefInfo(ref) << std::endl;
225
226 return;
227 } else {
228 CHECKF(!region->IsInToSpace())
229 << CONTEXT << "Object: " << GetObjectInfo(obj) << std::endl
230 << "Ref: " << GetRefInfo(ref) << std::endl;
231
232 return;
233 }
234 }
235
236 if (Heap::GetHeap().GetGCReason() == GC_REASON_YOUNG) {
237 CHECKF(RegionSpace::IsResurrectedObject(refObj) ||
238 RegionSpace::IsMarkedObject(refObj) ||
239 RegionSpace::IsNewObjectSinceMarking(refObj) ||
240 !RegionSpace::IsYoungSpaceObject(refObj))
241 << CONTEXT << "Object: " << GetObjectInfo(obj) << std::endl
242 << "Ref: " << GetRefInfo(ref) << std::endl;
243 } else {
244 CHECKF(RegionSpace::IsResurrectedObject(refObj) ||
245 RegionSpace::IsMarkedObject(refObj) ||
246 RegionSpace::IsNewObjectSinceMarking(refObj))
247 << CONTEXT << "Object: " << GetObjectInfo(obj) << std::endl
248 << "Ref: " << GetRefInfo(ref) << std::endl;
249 }
250 }
251 };
252
253 class AfterForwardVisitor : public VerifyVisitor {
254 public:
VerifyRefImpl(const BaseObject * obj,const RefField<> & ref)255 void VerifyRefImpl(const BaseObject* obj, const RefField<>& ref) override
256 {
257 // check objects in from-space, only alive objects are forwarded
258 auto refObj = ref.GetTargetObject();
259 if (RegionSpace::IsMarkedObject(refObj) || RegionSpace::IsResurrectedObject(refObj)) {
260 CHECKF(refObj->IsForwarded())
261 << CONTEXT
262 << "Object: " << GetObjectInfo(obj) << std::endl
263 << "Ref: " << GetRefInfo(ref) << std::endl;
264
265 auto toObj = refObj->GetForwardingPointer();
266 IsValidRef(obj, RefField<>(toObj));
267 }
268 }
269 };
270
271 class AfterFixVisitor : public VerifyVisitor {
272 public:
VerifyRefImpl(const BaseObject * obj,const RefField<> & ref)273 void VerifyRefImpl(const BaseObject* obj, const RefField<>& ref) override
274 {
275 IsValidRef(obj, ref);
276
277 auto refRegion = RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(ref.GetTargetObject()));
278 CHECKF(refRegion->GetRegionType() != RegionDesc::RegionType::FROM_REGION)
279 << CONTEXT
280 << "Object: " << GetObjectInfo(obj) << std::endl
281 << "Ref: " << GetRefInfo(ref) << std::endl;
282 }
283 };
284
285 class ReadBarrierVisitor : public VerifyVisitor {
286 protected:
287 constexpr static MAddress TAG_RB_DFX_SHIFT = 47;
288 constexpr static MAddress TAG_RB_DFX = 0x1ULL << TAG_RB_DFX_SHIFT;
289 };
290
291 class ReadBarrierSetter : public ReadBarrierVisitor {
292 public:
VerifyRefImpl(const BaseObject * obj,RefField<> & ref)293 void VerifyRefImpl(const BaseObject* obj, RefField<>& ref) override
294 {
295 if (obj == nullptr) {
296 // skip roots
297 return;
298 }
299
300 auto regionType =
301 RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(ref.GetTargetObject()))->GetRegionType();
302 if (regionType == RegionDesc::RegionType::RECENT_PINNED_REGION ||
303 regionType == RegionDesc::RegionType::FULL_PINNED_REGION ||
304 regionType == RegionDesc::RegionType::FIXED_PINNED_REGION ||
305 regionType == RegionDesc::RegionType::FULL_FIXED_PINNED_REGION) {
306 // Read barrier for pinned objects might be optimized out, so don't set dfx tag
307 return;
308 }
309
310 auto newRefValue = ref.GetFieldValue() | TAG_RB_DFX;
311 ref.SetFieldValue(newRefValue);
312 }
313 };
314
315 class ReadBarrierUnsetter : public ReadBarrierVisitor {
316 public:
VerifyRefImpl(const BaseObject * obj,RefField<> & ref)317 void VerifyRefImpl(const BaseObject* obj, RefField<>& ref) override
318 {
319 auto newRefValue = ref.GetFieldValue() & (~TAG_RB_DFX);
320 ref.SetFieldValue(newRefValue);
321 }
322
GetTargetObject(const RefField<> & ref)323 static BaseObject* GetTargetObject(const RefField<>& ref)
324 {
325 // Get the target object from the reference field, ignoring the read barrier tag
326 BaseObject* targetObj = ref.GetTargetObject();
327 return reinterpret_cast<BaseObject*>(reinterpret_cast<MAddress>(targetObj) & (~TAG_RB_DFX));
328 }
329 };
330
331 class VerifyIterator {
332 public:
VerifyIterator(RegionSpace & space)333 explicit VerifyIterator(RegionSpace& space) : space_(space) {}
334
IterateFromSpace(VerifyVisitor & visitor)335 void IterateFromSpace(VerifyVisitor& visitor)
336 {
337 IterateRegionList(space_.GetFromSpace().GetFromRegionList(), visitor);
338 }
339
IterateRoot(VerifyVisitor & visitor)340 void IterateRoot(VerifyVisitor& visitor)
341 {
342 MarkStack<BaseObject*> roots;
343
344 RefFieldVisitor refVisitor = [&](RefField<>& ref) { visitor.VerifyRef(nullptr, ref); };
345 VisitRoots(refVisitor);
346 }
347
IterateWeakRoot(VerifyVisitor & visitor)348 void IterateWeakRoot(VerifyVisitor& visitor)
349 {
350 MarkStack<BaseObject*> roots;
351
352 WeakRefFieldVisitor refVisitor = [&](RefField<>& ref) {
353 visitor.VerifyRef(nullptr, ref);
354 return true;
355 };
356 VisitWeakRoots(refVisitor);
357 }
358
359 // By default, IterateRemarked uses the VisitRoots method to traverse GC roots
360 template <void (*VisitRoot)(const RefFieldVisitor &) = VisitRoots>
IterateRemarked(VerifyVisitor & visitor,std::unordered_set<BaseObject * > & markSet,bool forRBDFX=false)361 void IterateRemarked(VerifyVisitor &visitor, std::unordered_set<BaseObject *> &markSet, bool forRBDFX = false)
362 {
363 MarkStack<BaseObject*> markStack;
364 BaseObject* obj = nullptr;
365
366 auto markFunc = [this, &visitor, &markStack, &markSet, &obj, &forRBDFX](RefField<>& field) {
367 if (!Heap::IsTaggedObject(reinterpret_cast<MAddress>(field.GetFieldValue()))) {
368 return;
369 }
370
371 BaseObject* refObj = nullptr;
372 if (forRBDFX) {
373 refObj = ReadBarrierUnsetter::GetTargetObject(field);
374 } else {
375 refObj = field.GetTargetObject();
376 }
377 // If it is forwarded, its toVersion must have been traversed during
378 // EnumRoot, so it must have been marked. There is no need for me to
379 // check it, nor to push it into the mark stack.
380 if (refObj->IsForwarded()) {
381 auto toObj = refObj->GetForwardingPointer();
382 bool find = markSet.find(toObj) != markSet.end();
383 CHECKF(find) << "not found to version obj in markSet";
384 return;
385 }
386
387 visitor.VerifyRef(obj, field);
388
389 if (markSet.find(refObj) != markSet.end()) {
390 return;
391 }
392 markSet.insert(refObj);
393 markStack.push_back(refObj);
394 };
395
396 VisitRoot(markFunc);
397 while (!markStack.empty()) {
398 obj = markStack.back();
399 markStack.pop_back();
400
401 obj->ForEachRefField(markFunc);
402 }
403 }
404
405 private:
IterateRegionList(RegionList & list,VerifyVisitor & visitor)406 void IterateRegionList(RegionList& list, VerifyVisitor& visitor)
407 {
408 list.VisitAllRegions([&](RegionDesc* region) {
409 region->VisitAllObjects([&](BaseObject* obj) { visitor.VerifyRef(nullptr, RefField<>(obj)); });
410 });
411 }
412
EnumStrongRoots(const std::function<void (RefField<> &)> & markFunc)413 void EnumStrongRoots(const std::function<void(RefField<>&)>& markFunc)
414 {
415 VisitRoots(markFunc);
416 }
417
Marking(MarkStack<BaseObject * > & markStack)418 void Marking(MarkStack<BaseObject*>& markStack) {}
419
420 RegionSpace& space_;
421 };
422
VerifyAfterMarkInternal(RegionSpace & space)423 void WVerify::VerifyAfterMarkInternal(RegionSpace& space)
424 {
425 CHECKF(Heap::GetHeap().GetGCPhase() == GCPhase::GC_PHASE_POST_MARK)
426 << CONTEXT << "Mark verification should be called after PostMarking()";
427
428 auto iter = VerifyIterator(space);
429 auto verifySTWRoots = AfterMarkVisitor();
430 std::unordered_set<BaseObject*> markSet;
431 iter.IterateRemarked<VisitSTWRoots>(verifySTWRoots, markSet);
432 auto verifyConcurrentRoots = AfterMarkVisitor<false>();
433 iter.IterateRemarked<VisitConcurrentRoots>(verifyConcurrentRoots, markSet);
434
435 LOG_COMMON(DEBUG) << "[WVerify]: VerifyAfterMark (STWRoots) verified ref count: "
436 << verifySTWRoots.VerifyRefCount();
437 LOG_COMMON(DEBUG) << "[WVerify]: VerifyAfterMark (ConcurrentRoots) verified ref count: "
438 << verifyConcurrentRoots.VerifyRefCount();
439 }
440
VerifyAfterMark(ArkCollector & collector)441 void WVerify::VerifyAfterMark(ArkCollector& collector)
442 {
443 #if !defined(ENABLE_CMC_VERIFY) && defined(NDEBUG)
444 return;
445 #endif
446 RegionSpace& space = reinterpret_cast<RegionSpace&>(collector.GetAllocator());
447 if (!MutatorManager::Instance().WorldStopped()) {
448 STWParam stwParam{"WGC-verify-aftermark"};
449 ScopedStopTheWorld stw(stwParam);
450 VerifyAfterMarkInternal(space);
451 } else {
452 VerifyAfterMarkInternal(space);
453 }
454 }
455
VerifyAfterForwardInternal(RegionSpace & space)456 void WVerify::VerifyAfterForwardInternal(RegionSpace& space)
457 {
458 CHECKF(Heap::GetHeap().GetGCPhase() == GCPhase::GC_PHASE_COPY)
459 << CONTEXT << "Forward verification should be called after ForwardFromSpace()";
460
461 auto iter = VerifyIterator(space);
462 auto visitor = AfterForwardVisitor();
463 iter.IterateFromSpace(visitor);
464
465 LOG_COMMON(DEBUG) << "[WVerify]: VerifyAfterForward verified ref count: " << visitor.VerifyRefCount();
466 }
467
VerifyAfterForward(ArkCollector & collector)468 void WVerify::VerifyAfterForward(ArkCollector& collector)
469 {
470 #if !defined(ENABLE_CMC_VERIFY) && defined(NDEBUG)
471 return;
472 #endif
473 RegionSpace& space = reinterpret_cast<RegionSpace&>(collector.GetAllocator());
474 if (!MutatorManager::Instance().WorldStopped()) {
475 STWParam stwParam{"WGC-verify-aftermark"};
476 ScopedStopTheWorld stw(stwParam);
477 VerifyAfterForwardInternal(space);
478 } else {
479 VerifyAfterForwardInternal(space);
480 }
481 }
482
VerifyAfterFixInternal(RegionSpace & space)483 void WVerify::VerifyAfterFixInternal(RegionSpace& space)
484 {
485 CHECKF(Heap::GetHeap().GetGCPhase() == GCPhase::GC_PHASE_FIX)
486 << CONTEXT << "Fix verification should be called after Fix()";
487
488 auto iter = VerifyIterator(space);
489 auto visitor = AfterFixVisitor();
490
491 std::unordered_set<BaseObject*> markSet;
492 iter.IterateRemarked(visitor, markSet);
493
494 LOG_COMMON(DEBUG) << "[WVerify]: VerifyAfterFix verified ref count: " << visitor.VerifyRefCount();
495 }
496
VerifyAfterFix(ArkCollector & collector)497 void WVerify::VerifyAfterFix(ArkCollector& collector)
498 {
499 #if !defined(ENABLE_CMC_VERIFY) && defined(NDEBUG)
500 return;
501 #endif
502 RegionSpace& space = reinterpret_cast<RegionSpace&>(collector.GetAllocator());
503 if (!MutatorManager::Instance().WorldStopped()) {
504 STWParam stwParam{"WGC-verify-aftermark"};
505 ScopedStopTheWorld stw(stwParam);
506 VerifyAfterFixInternal(space);
507 } else {
508 VerifyAfterFixInternal(space);
509 }
510 }
511
EnableReadBarrierDFXInternal(RegionSpace & space)512 void WVerify::EnableReadBarrierDFXInternal(RegionSpace& space)
513 {
514 auto iter = VerifyIterator(space);
515 auto setter = ReadBarrierSetter();
516 auto unsetter = ReadBarrierUnsetter();
517
518 std::unordered_set<BaseObject*> markSet;
519 iter.IterateRemarked(setter, markSet, true);
520 // some slots of heap object are also roots, so we need to unset them
521 iter.IterateRoot(unsetter);
522 }
523
EnableReadBarrierDFX(ArkCollector & collector)524 void WVerify::EnableReadBarrierDFX(ArkCollector& collector)
525 {
526 #if !defined(ENABLE_CMC_RB_DFX)
527 return;
528 #endif
529 RegionSpace& space = reinterpret_cast<RegionSpace&>(collector.GetAllocator());
530 if (!MutatorManager::Instance().WorldStopped()) {
531 STWParam stwParam{"WGC-verify-enable-rb-dfx"};
532 ScopedStopTheWorld stw(stwParam);
533 EnableReadBarrierDFXInternal(space);
534 } else {
535 EnableReadBarrierDFXInternal(space);
536 }
537 }
538
DisableReadBarrierDFXInternal(RegionSpace & space)539 void WVerify::DisableReadBarrierDFXInternal(RegionSpace& space)
540 {
541 auto iter = VerifyIterator(space);
542 auto unsetter = ReadBarrierUnsetter();
543
544 std::unordered_set<BaseObject*> markSet;
545 iter.IterateRemarked(unsetter, markSet, true);
546 }
547
DisableReadBarrierDFX(ArkCollector & collector)548 void WVerify::DisableReadBarrierDFX(ArkCollector& collector)
549 {
550 #if !defined(ENABLE_CMC_RB_DFX)
551 return;
552 #endif
553 RegionSpace& space = reinterpret_cast<RegionSpace&>(collector.GetAllocator());
554 if (!MutatorManager::Instance().WorldStopped()) {
555 STWParam stwParam{"WGC-verify-disable-rb-dfx"};
556 ScopedStopTheWorld stw(stwParam);
557 DisableReadBarrierDFXInternal(space);
558 } else {
559 DisableReadBarrierDFXInternal(space);
560 }
561 }
562
563 } // namespace common
564