Enhance heap verify

Signed-off-by: chentianyu <chentianyu31@huawei.com>
Change-Id: I23890f3531c417e3adda5da0a2b7b231dacd2cf6
This commit is contained in:
chentianyu 2024-02-08 13:12:59 +08:00
parent 5212133062
commit 93babd1795
27 changed files with 469 additions and 141 deletions

View File

@ -61,7 +61,7 @@ namespace panda::ecmascript {
#define ECMASCRIPT_SWITCH_GC_MODE_TO_FULL_GC 1
#define ECMASCRIPT_ENABLE_CAST_CHECK 1
#define ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 1
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 1
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 1 // use jsoption instead
#define ECMASCRIPT_ENABLE_BARRIER_CHECK 1
#define ECMASCRIPT_ENABLE_NAPI_SPECIAL_CHECK 1
#elif defined(ECMASCRIPT_ENABLE_DFX_CONFIG)
@ -70,7 +70,7 @@ namespace panda::ecmascript {
#define ECMASCRIPT_SWITCH_GC_MODE_TO_FULL_GC 0
#define ECMASCRIPT_ENABLE_CAST_CHECK 0
#define ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 0
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 1
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 1 // use jsoption instead
#define ECMASCRIPT_ENABLE_BARRIER_CHECK 0
#define ECMASCRIPT_ENABLE_NAPI_SPECIAL_CHECK 1
#else
@ -79,7 +79,7 @@ namespace panda::ecmascript {
#define ECMASCRIPT_SWITCH_GC_MODE_TO_FULL_GC 0
#define ECMASCRIPT_ENABLE_CAST_CHECK 0
#define ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 0
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 0
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 0 // use jsoption instead
#define ECMASCRIPT_ENABLE_BARRIER_CHECK 0
#define ECMASCRIPT_ENABLE_NAPI_SPECIAL_CHECK 0
#endif

View File

@ -860,9 +860,11 @@ void EcmaContext::ShrinkHandleStorage(int prevIndex)
int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
#if ECMASCRIPT_ENABLE_ZAP_MEM
uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
LOG_FULL(FATAL) << "memset_s failed";
UNREACHABLE();
if (currentHandleStorageIndex_ != -1) {
if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
LOG_FULL(FATAL) << "memset_s failed";
UNREACHABLE();
}
}
for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
if (memset_s(handleStorageNodes_[i],

View File

@ -47,7 +47,8 @@ enum ArkProperties {
ENABLE_GC_TRACER = 1 << 15,
CPU_PROFILER_COLD_START_WORKER_THREAD = 1 << 16,
CPU_PROFILER_ANY_TIME_MAIN_THREAD = 1 << 17,
CPU_PROFILER_ANY_TIME_WORKER_THREAD = 1 << 18
CPU_PROFILER_ANY_TIME_WORKER_THREAD = 1 << 18,
ENABLE_HEAP_VERIFY = 1 << 19
};
// asm interpreter control parsed option
@ -483,6 +484,11 @@ public:
return (static_cast<uint32_t>(arkProperties_) & ArkProperties::ENABLE_SNAPSHOT_DESERIALIZE) != 0;
}
bool EnableHeapVerify() const
{
return (static_cast<uint32_t>(arkProperties_) & ArkProperties::ENABLE_HEAP_VERIFY) != 0;
}
void DisableReportModuleResolvingFailure()
{
reportModuleResolvingFailure_ = false;

View File

@ -22,7 +22,7 @@ void Barriers::Update(const JSThread *thread, uintptr_t slotAddr, Region *object
Region *valueRegion, WriteBarrierType writeType)
{
auto heap = thread->GetEcmaVM()->GetHeap();
if (heap->IsFullMark()) {
if (heap->IsConcurrentFullMark()) {
if (valueRegion->InCollectSet() && !objectRegion->InYoungSpaceOrCSet()) {
objectRegion->AtomicInsertCrossRegionRSet(slotAddr);
}
@ -47,7 +47,7 @@ void Barriers::MarkAndPushForDeserialize(const JSThread *thread, TaggedObject *o
{
auto heap = thread->GetEcmaVM()->GetHeap();
Region *valueRegion = Region::ObjectAddressToRange(object);
if ((heap->IsFullMark() || valueRegion->InYoungSpace()) && valueRegion->AtomicMark(object)) {
if ((heap->IsConcurrentFullMark() || valueRegion->InYoungSpace()) && valueRegion->AtomicMark(object)) {
heap->GetWorkManager()->Push(MAIN_THREAD_INDEX, object, valueRegion);
}
}

View File

@ -86,7 +86,7 @@ void ConcurrentMarker::HandleMarkingFinished() // js-thread wait for sweep
{
LockHolder lock(waitMarkingFinishedMutex_);
if (notifyMarkingFinished_) {
heap_->CollectGarbage(heap_->IsFullMark() ? TriggerGCType::OLD_GC : TriggerGCType::YOUNG_GC,
heap_->CollectGarbage(heap_->IsConcurrentFullMark() ? TriggerGCType::OLD_GC : TriggerGCType::YOUNG_GC,
GCReason::ALLOCATION_LIMIT);
}
}
@ -114,7 +114,7 @@ void ConcurrentMarker::Reset(bool revertCSet)
region->ClearCrossRegionRSet();
region->ResetAliveObject();
};
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->EnumerateRegions(callback);
} else {
heap_->EnumerateNewSpaceRegions(callback);
@ -130,7 +130,7 @@ void ConcurrentMarker::InitializeMarking()
isConcurrentMarking_ = true;
thread_->SetMarkStatus(MarkStatus::MARKING);
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heapObjectSize_ = heap_->GetHeapObjectSize();
heap_->GetOldSpace()->SelectCSet();
heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
@ -145,7 +145,7 @@ void ConcurrentMarker::InitializeMarking()
heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize();
}
workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK);
if (!heap_->IsFullMark()) {
if (!heap_->IsConcurrentFullMark()) {
{
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::MarkOldToNew");
heap_->GetNonMovableMarker()->ProcessOldToNewNoMarkStack(MAIN_THREAD_INDEX);
@ -176,7 +176,7 @@ void ConcurrentMarker::FinishMarking(float spendTime)
waitMarkingFinishedCV_.Signal();
}
notifyMarkingFinished_ = true;
if (!heap_->IsFullMark()) {
if (!heap_->IsConcurrentFullMark()) {
heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize();
} else {
heapObjectSize_ = heap_->GetHeapObjectSize();

View File

@ -58,22 +58,6 @@ void GCStats::PrintGCStatistic()
InitializeRecordList();
}
const char *GCStats::GetGCTypeName()
{
switch (gcType_) {
case GCType::STW_YOUNG_GC:
return "STWYoungGC";
case GCType::PARTIAL_YOUNG_GC:
return "HPP YoungGC";
case GCType::PARTIAL_OLD_GC:
return "HPP OldGC";
case GCType::COMPRESS_GC:
return "CompressGC";
default:
return "UnknownType";
}
}
const char *GCStats::GCReasonToString()
{
switch (reason_) {
@ -552,7 +536,7 @@ void GCStats::RecordGCSpeed()
GCType GCStats::GetGCType(TriggerGCType gcType)
{
if (!heap_->GetJSThread()->IsReadyToMark()) {
return heap_->IsFullMark() ? GCType::PARTIAL_OLD_GC : GCType::PARTIAL_YOUNG_GC;
return heap_->IsConcurrentFullMark() ? GCType::PARTIAL_OLD_GC : GCType::PARTIAL_YOUNG_GC;
}
switch (gcType) {
case TriggerGCType::YOUNG_GC:

View File

@ -103,6 +103,22 @@ public:
return reason_;
}
const char *GetGCTypeName()
{
switch (gcType_) {
case GCType::STW_YOUNG_GC:
return "STWYoungGC";
case GCType::PARTIAL_YOUNG_GC:
return "HPP YoungGC";
case GCType::PARTIAL_OLD_GC:
return "HPP OldGC";
case GCType::COMPRESS_GC:
return "CompressGC";
default:
return "UnknownType";
}
}
const char *GCReasonToString();
double GetAvgSurvivalRate()
@ -151,7 +167,6 @@ private:
void PrintGCSummaryStatistic(GCType type = GCType::START);
GCType GetGCType(TriggerGCType gcType);
void InitializeRecordList();
const char *GetGCTypeName();
float GetConcurrrentMarkDuration();
int GetRecordDurationIndex(RecordDuration durationIdx)

View File

@ -344,6 +344,11 @@ void Heap::SwapNewSpace()
SemiSpace *newSpace = inactiveSemiSpace_;
inactiveSemiSpace_ = activeSemiSpace_;
activeSemiSpace_ = newSpace;
if (UNLIKELY(ShouldVerifyHeap())) {
inactiveSemiSpace_->EnumerateRegions([](Region *region) {
region->SetInactiveSemiSpace();
});
}
#ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING
activeSemiSpace_->SwapAllocationCounter(inactiveSemiSpace_);
#endif

View File

@ -87,6 +87,9 @@ void Heap::Initialize()
auto endAddress = activeSemiSpace_->GetAllocationEndAddress();
thread_->ReSetNewSpaceAllocationAddress(topAddress, endAddress);
inactiveSemiSpace_ = new SemiSpace(this, minSemiSpaceCapacity, maxSemiSpaceCapacity);
// whether should verify heap duration gc
shouldVerifyHeap_ = ecmaVm_->GetJSOptions().EnableHeapVerify();
// not set up from space
size_t readOnlySpaceCapacity = config.GetDefaultReadOnlySpaceSize();
@ -384,17 +387,11 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason)
#endif
CHECK_NO_GC
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
LOG_ECMA(DEBUG) << "Enable heap verify";
isVerifying_ = true;
// pre gc heap verify
sweeper_->EnsureAllTaskFinished();
auto failCount = Verification(this).VerifyAll();
if (failCount > 0) {
LOG_GC(FATAL) << "Before gc heap corrupted and " << failCount << " corruptions";
if (UNLIKELY(ShouldVerifyHeap())) {
// pre gc heap verify
LOG_ECMA(DEBUG) << "pre gc heap verify";
Verification(this, VerifyKind::VERIFY_PRE_GC).VerifyAll();
}
isVerifying_ = false;
#endif
#if ECMASCRIPT_SWITCH_GC_MODE_TO_FULL_GC
gcType = TriggerGCType::FULL_GC;
@ -491,11 +488,11 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason)
// Trigger full mark next time if the current survival rate is much less than half the average survival rates.
AdjustBySurvivalRate(originalNewSpaceSize);
memController_->StopCalculationAfterGC(gcType);
if (gcType == TriggerGCType::FULL_GC || IsFullMark()) {
if (gcType == TriggerGCType::FULL_GC || IsConcurrentFullMark()) {
// Only when the gc type is not semiGC and after the old space sweeping has been finished,
// the limits of old space and global space can be recomputed.
RecomputeLimits();
OPTIONAL_LOG(ecmaVm_, INFO) << " GC after: is full mark" << IsFullMark()
OPTIONAL_LOG(ecmaVm_, INFO) << " GC after: is full mark" << IsConcurrentFullMark()
<< " global object size " << GetHeapObjectSize()
<< " global committed size " << GetCommittedSize()
<< " global limit " << globalSpaceAllocLimit_;
@ -520,16 +517,11 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason)
// even lead to another GC, so this have to invoke after this GC process.
InvokeWeakNodeNativeFinalizeCallback();
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
// post gc heap verify
isVerifying_ = true;
sweeper_->EnsureAllTaskFinished();
auto failCount = Verification(this).VerifyAll();
if (failCount > 0) {
LOG_GC(FATAL) << "After gc heap corrupted and " << failCount << " corruptions";
if (UNLIKELY(ShouldVerifyHeap())) {
// verify post gc heap verify
LOG_ECMA(DEBUG) << "post gc heap verify";
Verification(this, VerifyKind::VERIFY_POST_GC).VerifyAll();
}
isVerifying_ = false;
#endif
JSFinalizationRegistry::CheckAndCall(thread_);
#if defined(ECMASCRIPT_SUPPORT_TRACING)
auto tracing = GetEcmaVM()->GetTracing();
@ -603,52 +595,63 @@ void Heap::AdjustBySurvivalRate(size_t originalNewSpaceSize)
}
}
size_t Heap::VerifyHeapObjects() const
size_t Heap::VerifyHeapObjects(VerifyKind verifyKind) const
{
size_t failCount = 0;
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
activeSemiSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
if (verifyKind == VerifyKind::VERIFY_EVACUATE_YOUNG ||
verifyKind == VerifyKind::VERIFY_EVACUATE_OLD ||
verifyKind == VerifyKind::VERIFY_EVACUATE_FULL) {
inactiveSemiSpace_->EnumerateRegions([this](Region *region) {
region->IterateAllMarkedBits(std::bind(&VerifyObjectVisitor::VerifyInactiveSemiSpaceMarkedObject,
this, std::placeholders::_1));
});
}
}
{
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
oldSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
appSpawnSpace_->IterateOverMarkedObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
nonMovableSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
hugeObjectSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
hugeMachineCodeSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
machineCodeSpace_->IterateOverObjects(verifier);
}
{
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
snapshotSpace_->IterateOverObjects(verifier);
}
return failCount;
}
size_t Heap::VerifyOldToNewRSet() const
size_t Heap::VerifyOldToNewRSet(VerifyKind verifyKind) const
{
size_t failCount = 0;
VerifyObjectVisitor verifier(this, &failCount);
VerifyObjectVisitor verifier(this, &failCount, verifyKind);
oldSpace_->IterateOldToNewOverObjects(verifier);
appSpawnSpace_->IterateOldToNewOverObjects(verifier);
nonMovableSpace_->IterateOldToNewOverObjects(verifier);
@ -897,7 +900,7 @@ void Heap::RecomputeLimits()
bool Heap::CheckAndTriggerOldGC(size_t size)
{
bool isFullMarking = IsFullMark() && GetJSThread()->IsMarking();
bool isFullMarking = IsConcurrentFullMark() && GetJSThread()->IsMarking();
bool isNativeSizeLargeTrigger = isFullMarking ? false : GlobalNativeSizeLargerThanLimit();
if (isFullMarking && oldSpace_->GetOvershootSize() == 0) {
oldSpace_->SetOvershootSize(GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize());
@ -942,7 +945,7 @@ bool Heap::CheckOngoingConcurrentMarking()
} else {
WaitRunningTaskFinished();
}
memController_->RecordAfterConcurrentMark(IsFullMark(), concurrentMarker_);
memController_->RecordAfterConcurrentMark(IsConcurrentFullMark(), concurrentMarker_);
return true;
}
return false;
@ -1193,7 +1196,7 @@ void Heap::PrepareRecordRegionsForReclaim()
void Heap::TriggerConcurrentMarking()
{
ASSERT(idleTask_ != IdleTaskType::INCREMENTAL_MARK);
if (idleTask_ == IdleTaskType::YOUNG_GC && IsFullMark()) {
if (idleTask_ == IdleTaskType::YOUNG_GC && IsConcurrentFullMark()) {
ClearIdleTask();
DisableNotifyIdle();
}
@ -1576,7 +1579,7 @@ void Heap::PrintHeapInfo(TriggerGCType gcType) const
<< ";OnHighSensitive:" << static_cast<int>(GetSensitiveStatus())
<< ";ConcurrentMark Status:" << static_cast<int>(thread_->GetMarkStatus());
OPTIONAL_LOG(ecmaVm_, INFO) << "Heap::CollectGarbage, gcType(" << gcType << "), Concurrent Mark("
<< concurrentMarker_->IsEnabled() << "), Full Mark(" << IsFullMark() << ")";
<< concurrentMarker_->IsEnabled() << "), Full Mark(" << IsConcurrentFullMark() << ")";
OPTIONAL_LOG(ecmaVm_, INFO) << "ActiveSemi(" << activeSemiSpace_->GetHeapObjectSize()
<< "/" << activeSemiSpace_->GetInitialCapacity() << "), NonMovable("
<< nonMovableSpace_->GetHeapObjectSize() << "/" << nonMovableSpace_->GetCommittedSize()

View File

@ -78,6 +78,16 @@ enum AppSensitiveStatus : uint8_t {
EXIT_HIGH_SENSITIVE,
};
enum class VerifyKind {
VERIFY_PRE_GC,
VERIFY_POST_GC,
VERIFY_CONCURRENT_MARK_YOUNG,
VERIFY_EVACUATE_YOUNG,
VERIFY_CONCURRENT_MARK_FULL,
VERIFY_EVACUATE_OLD,
VERIFY_EVACUATE_FULL
};
class Heap {
public:
explicit Heap(EcmaVM *ecmaVm);
@ -284,6 +294,12 @@ public:
return onSerializeEvent_;
}
// Whether should verify heap during gc.
bool ShouldVerifyHeap() const
{
return shouldVerifyHeap_;
}
/*
* For object allocations.
*/
@ -372,7 +388,7 @@ public:
markType_ = markType;
}
bool IsFullMark() const
bool IsConcurrentFullMark() const
{
return markType_ == MarkType::MARK_FULL;
}
@ -520,8 +536,8 @@ public:
bool IsAlive(TaggedObject *object) const;
bool ContainObject(TaggedObject *object) const;
size_t VerifyHeapObjects() const;
size_t VerifyOldToNewRSet() const;
size_t VerifyHeapObjects(VerifyKind verifyKind = VerifyKind::VERIFY_PRE_GC) const;
size_t VerifyOldToNewRSet(VerifyKind verifyKind = VerifyKind::VERIFY_PRE_GC) const;
void StatisticHeapObject(TriggerGCType gcType) const;
void StatisticHeapDetail() const;
void PrintHeapInfo(TriggerGCType gcType) const;
@ -540,12 +556,19 @@ public:
}
void AdjustSpaceSizeForAppSpawn();
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
// ONLY used for heap verification.
bool IsVerifying() const
{
return isVerifying_;
}
#endif
// ONLY used for heap verification.
void SetVerifying(bool verifying)
{
isVerifying_ = verifying;
}
static bool ShouldMoveToRoSpace(JSHClass *hclass, TaggedObject *object)
{
return hclass->IsString() && !Region::ObjectAddressToRange(object)->InHugeObjectSpace();
@ -622,6 +645,11 @@ public:
return oldGCRequested_;
}
TriggerGCType GetGCType() const
{
return gcType_;
}
void CheckNonMovableSpaceOOM();
std::tuple<uint64_t, uint8_t *, int, kungfu::CalleeRegAndOffsetVec> CalCallSiteInfo(uintptr_t retAddr) const;
@ -829,9 +857,9 @@ private:
double idleTaskFinishTime_ {0.0};
int32_t recursionDepth_ {0};
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
// ONLY used for heap verification.
bool shouldVerifyHeap_ {false};
bool isVerifying_ {false};
#endif
};
} // namespace panda::ecmascript

View File

@ -50,7 +50,7 @@ uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted)
object = allocator_.Allocate(size);
} else if (heap_->GetJSThread()->IsMarking() || !heap_->IsEmptyIdleTask()) {
// Temporary adjust semi space capacity
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
overShootSize_ = heap_->GetOldSpace()->GetMaximumCapacity() - heap_->GetOldSpace()->GetInitialCapacity();
} else {
size_t stepOverShootSize = heap_->GetEcmaVM()->GetEcmaParamConfiguration().GetSemiSpaceStepOvershootSize();
@ -198,6 +198,10 @@ bool SemiSpace::SwapRegion(Region *region, SemiSpace *fromSpace)
region->SetGCFlag(RegionGCFlags::IN_NEW_TO_NEW_SET);
if (UNLIKELY(heap_->ShouldVerifyHeap())) {
region->ResetInactiveSemiSpace();
}
regionList_.AddNodeToFront(region);
IncreaseCommitted(region->GetCapacity());
IncreaseObjectSize(region->GetSize());

View File

@ -139,7 +139,7 @@ void MemController::StopCalculationAfterGC(TriggerGCType gcType)
switch (gcType) {
case TriggerGCType::YOUNG_GC:
case TriggerGCType::OLD_GC: {
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
if (heap_->GetConcurrentMarker()->IsEnabled()) {
duration += heap_->GetConcurrentMarker()->GetDuration();
}

View File

@ -131,7 +131,7 @@ void ParallelEvacuator::UpdateWeakObjectSlot(TaggedObject *value, ObjectSlot &sl
return;
}
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
if (!objectRegion->Test(value)) {
slot.Clear();
}
@ -203,7 +203,7 @@ TaggedObject* ParallelEvacuator::UpdateAddressAfterEvacation(TaggedObject *oldAd
}
return nullptr;
}
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
if (objectRegion->GetMarkGCBitset() == nullptr || !objectRegion->Test(oldAddress)) {
return nullptr;
}

View File

@ -168,9 +168,10 @@ void ParallelEvacuator::EvacuateRegion(TlabAllocator *allocator, Region *region,
}
}
Barriers::SetPrimitive(header, 0, MarkWord::FromForwardingAddress(address));
#if ECMASCRIPT_ENABLE_HEAP_VERIFY
VerifyHeapObject(reinterpret_cast<TaggedObject *>(address));
#endif
if (UNLIKELY(heap_->ShouldVerifyHeap())) {
VerifyHeapObject(reinterpret_cast<TaggedObject *>(address));
}
if (actualPromoted) {
SetObjectFieldRSet(reinterpret_cast<TaggedObject *>(address), klass);
}
@ -202,7 +203,7 @@ void ParallelEvacuator::VerifyValue(TaggedObject *object, ObjectSlot slot)
return;
}
Region *objectRegion = Region::ObjectAddressToRange(value.GetTaggedObject());
if (!heap_->IsFullMark() && !objectRegion->InYoungSpace()) {
if (!heap_->IsConcurrentFullMark() && !objectRegion->InYoungSpace()) {
return;
}
if (!objectRegion->Test(value.GetTaggedObject()) && !objectRegion->InAppSpawnSpace()) {
@ -307,7 +308,7 @@ void ParallelEvacuator::UpdateWeakReference()
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::UpdateWeakReference");
UpdateRecordWeakReference();
auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable();
bool isFullMark = heap_->IsFullMark();
bool isFullMark = heap_->IsConcurrentFullMark();
WeakRootVisitor gcUpdateWeak = [isFullMark](TaggedObject *header) {
Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(header));
if (!objectRegion) {

View File

@ -72,7 +72,7 @@ inline void NonMovableMarker::MarkObject(uint32_t threadId, TaggedObject *object
{
Region *objectRegion = Region::ObjectAddressToRange(object);
if (!heap_->IsFullMark() && !objectRegion->InYoungSpace()) {
if (!heap_->IsConcurrentFullMark() && !objectRegion->InYoungSpace()) {
return;
}

View File

@ -66,7 +66,7 @@ void Marker::ProcessSnapshotRSetNoMarkStack(uint32_t threadId)
void NonMovableMarker::ProcessMarkStack(uint32_t threadId)
{
TRACE_GC(GCStats::Scope::ScopeId::ProcessMarkStack, heap_->GetEcmaVM()->GetEcmaGCStats());
bool isFullMark = heap_->IsFullMark();
bool isFullMark = heap_->IsConcurrentFullMark();
auto cb = [&](ObjectSlot s, Region *rootRegion, bool needBarrier) {
MarkValue(threadId, s, rootRegion, needBarrier);
};
@ -99,7 +99,7 @@ void NonMovableMarker::ProcessMarkStack(uint32_t threadId)
void NonMovableMarker::ProcessIncrementalMarkStack(uint32_t threadId, uint32_t markStepSize)
{
TRACE_GC(GCStats::Scope::ScopeId::ProcessMarkStack, heap_->GetEcmaVM()->GetEcmaGCStats());
bool isFullMark = heap_->IsFullMark();
bool isFullMark = heap_->IsConcurrentFullMark();
uint32_t visitAddrNum = 0;
auto cb = [&](ObjectSlot s, Region *rootRegion, bool needBarrier) {
MarkValue(threadId, s, rootRegion, needBarrier);

View File

@ -30,6 +30,7 @@
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/runtime_call_id.h"
#include "ecmascript/mem/verification.h"
namespace panda::ecmascript {
PartialGC::PartialGC(Heap *heap) : heap_(heap), workManager_(heap->GetWorkManager()) {}
@ -37,7 +38,7 @@ PartialGC::PartialGC(Heap *heap) : heap_(heap), workManager_(heap->GetWorkManage
void PartialGC::RunPhases()
{
GCStats *gcStats = heap_->GetEcmaVM()->GetEcmaGCStats();
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::RunPhases" + std::to_string(heap_->IsFullMark())
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::RunPhases" + std::to_string(heap_->IsConcurrentFullMark())
+ ";Reason" + std::to_string(static_cast<int>(gcStats->GetGCReason()))
+ ";Sensitive" + std::to_string(static_cast<int>(heap_->GetSensitiveStatus()))
+ ";Startup" + std::to_string(heap_->onStartUpEvent())
@ -52,13 +53,25 @@ void PartialGC::RunPhases()
LOG_GC(DEBUG) << "markingInProgress_" << markingInProgress_;
Initialize();
Mark();
if (UNLIKELY(heap_->ShouldVerifyHeap())) {
// verify mark
LOG_ECMA(DEBUG) << "start verify mark";
Verification(heap_, heap_->IsConcurrentFullMark() ?
VerifyKind::VERIFY_CONCURRENT_MARK_FULL : VerifyKind::VERIFY_CONCURRENT_MARK_YOUNG).VerifyAll();
}
Sweep();
Evacuate();
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->GetSweeper()->PostTask();
}
if (UNLIKELY(heap_->ShouldVerifyHeap())) {
// verify evacuate and sweep
LOG_ECMA(DEBUG) << "start verify evacuate and sweep";
Verification(heap_, heap_->IsConcurrentFullMark() ?
VerifyKind::VERIFY_EVACUATE_OLD : VerifyKind::VERIFY_EVACUATE_YOUNG).VerifyAll();
}
Finish();
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->NotifyHeapAliveSizeAfterGC(heap_->GetHeapObjectSize());
}
}
@ -70,7 +83,7 @@ void PartialGC::Initialize()
if (!markingInProgress_ && !heap_->GetIncrementalMarker()->IsTriggeredIncrementalMark()) {
LOG_GC(DEBUG) << "No ongoing Concurrent marking. Initializing...";
heap_->Prepare();
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->GetOldSpace()->SelectCSet();
heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
current->ClearMarkGCBitset();
@ -102,7 +115,7 @@ void PartialGC::Finish()
} else {
workManager_->Finish();
}
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->GetSweeper()->TryFillSweptRegion();
}
}
@ -116,7 +129,7 @@ void PartialGC::Mark()
return;
}
heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX);
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->GetNonMovableMarker()->ProcessMarkStack(MAIN_THREAD_INDEX);
} else {
{
@ -132,7 +145,7 @@ void PartialGC::Sweep()
{
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PartialGC::Sweep");
ProcessNativeDelete();
if (heap_->IsFullMark()) {
if (heap_->IsConcurrentFullMark()) {
heap_->GetOldSpace()->EnumerateRegions([](Region *current) {
current->SetRegionAliveSize();
});
@ -147,7 +160,7 @@ void PartialGC::ProcessNativeDelete()
TRACE_GC(GCStats::Scope::ScopeId::ClearNativeObject, heap_->GetEcmaVM()->GetEcmaGCStats());
WeakRootVisitor gcUpdateWeak = [this](TaggedObject *header) {
Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(header));
if (!objectRegion->InYoungSpaceOrCSet() && !heap_->IsFullMark()) {
if (!objectRegion->InYoungSpaceOrCSet() && !heap_->IsConcurrentFullMark()) {
return header;
}
if (!objectRegion->Test(header)) {

View File

@ -102,6 +102,18 @@ inline bool Region::Test(void *addr) const
return packedData_.markGCBitset_->TestBit((addrPtr & DEFAULT_REGION_MASK) >> TAGGED_TYPE_SIZE_LOG);
}
// ONLY used for heap verification.
inline bool Region::TestOldToNew(uintptr_t addr)
{
ASSERT(InRange(addr));
// Only used for heap verification, so donot need to use lock
auto set = packedData_.oldToNewSet_;
if (set == nullptr) {
return false;
}
return set->TestBit(ToUintPtr(this), addr);
}
template <typename Visitor>
inline void Region::IterateAllMarkedBits(Visitor visitor) const
{

View File

@ -66,6 +66,8 @@ enum RegionGCFlags {
// INVALID_VALUE in ZAP_MEM.
HAS_BEEN_SWEPT = 1 << 11,
NEED_RELOCATE = 1 << 12,
// ONLY used for heap verification.
IN_INACTIVE_SEMI_SPACE = 1 << 13,
};
static inline std::string ToSpaceTypeName(uint8_t space)
@ -191,6 +193,8 @@ public:
bool AtomicMark(void *address);
void ClearMark(void *address);
bool Test(void *addr) const;
// ONLY used for heap verification.
bool TestOldToNew(uintptr_t addr);
template <typename Visitor>
void IterateAllMarkedBits(Visitor visitor) const;
void ClearMarkGCBitset();
@ -345,6 +349,30 @@ public:
return IsGCFlagSet(RegionGCFlags::NEED_RELOCATE);
}
// ONLY used for heap verification.
bool InInactiveSemiSpace() const
{
return IsGCFlagSet(RegionGCFlags::IN_INACTIVE_SEMI_SPACE);
}
// ONLY used for heap verification.
bool InActiveSemiSpace() const
{
return InYoungSpace() && !InInactiveSemiSpace();
}
// ONLY used for heap verification.
void SetInactiveSemiSpace()
{
SetGCFlag(RegionGCFlags::IN_INACTIVE_SEMI_SPACE);
}
// ONLY used for heap verification.
void ResetInactiveSemiSpace()
{
ClearGCFlag(RegionGCFlags::IN_INACTIVE_SEMI_SPACE);
}
void SetSwept()
{
SetGCFlag(RegionGCFlags::HAS_BEEN_SWEPT);

View File

@ -69,6 +69,11 @@ public:
(start - begin) >> TAGGED_TYPE_SIZE_LOG, (end - begin) >> TAGGED_TYPE_SIZE_LOG);
}
bool TestBit(uintptr_t begin, uintptr_t addr) const
{
return GCBitsetData()->TestBit((addr - begin) >> TAGGED_TYPE_SIZE_LOG);
}
template <typename Visitor>
void IterateAllMarkedBits(uintptr_t begin, Visitor visitor)
{

View File

@ -20,23 +20,23 @@
#include "ecmascript/mem/mem_common.h"
#ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING
#define CHECK_OBJECT_AND_INC_OBJ_SIZE(size) \
if (object != 0) { \
IncreaseLiveObjectSize(size); \
if (!heap_->IsFullMark() || heap_->GetJSThread()->IsReadyToMark()) { \
Region::ObjectAddressToRange(object)->IncreaseAliveObject(size); \
} \
InvokeAllocationInspector(object, size, size); \
return object; \
#define CHECK_OBJECT_AND_INC_OBJ_SIZE(size) \
if (object != 0) { \
IncreaseLiveObjectSize(size); \
if (!heap_->IsConcurrentFullMark() || heap_->GetJSThread()->IsReadyToMark()) { \
Region::ObjectAddressToRange(object)->IncreaseAliveObject(size); \
} \
InvokeAllocationInspector(object, size, size); \
return object; \
}
#else
#define CHECK_OBJECT_AND_INC_OBJ_SIZE(size) \
if (object != 0) { \
IncreaseLiveObjectSize(size); \
if (!heap_->IsFullMark() || heap_->GetJSThread()->IsReadyToMark()) { \
Region::ObjectAddressToRange(object)->IncreaseAliveObject(size); \
} \
return object; \
#define CHECK_OBJECT_AND_INC_OBJ_SIZE(size) \
if (object != 0) { \
IncreaseLiveObjectSize(size); \
if (!heap_->IsConcurrentFullMark() || heap_->GetJSThread()->IsReadyToMark()) { \
Region::ObjectAddressToRange(object)->IncreaseAliveObject(size); \
} \
return object; \
}
#endif

View File

@ -18,8 +18,75 @@
#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)
{
@ -35,43 +102,149 @@ void VerifyObjectVisitor::VisitAllObjects(TaggedObject *obj)
auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
auto attr = layout->GetAttr(index++);
if (attr.IsTaggedRep()) {
VisitObject(slot, root);
VerifyObjectSlotLegal(slot, root);
}
}
return;
}
for (ObjectSlot slot = start; slot < end; slot++) {
VisitObject(slot, root);
VerifyObjectSlotLegal(slot, root);
}
});
}
void VerifyObjectVisitor::VisitObject(ObjectSlot slot, TaggedObject *root)
void VerifyObjectVisitor::VerifyObjectSlotLegal(ObjectSlot slot, TaggedObject *object) const
{
JSTaggedValue value(slot.GetTaggedType());
if (value.IsWeak()) {
if (ToUintPtr(value.GetTaggedWeakRef()) < INVALID_THRESHOLD) {
LOG_GC(ERROR) << "Heap verify detected an invalid value at " << value.GetTaggedWeakRef()
<< " at object:" << slot.SlotAddress() << ", root:" << root;
LogErrorForObjSlot(heap_, "Heap verify detected an invalid value.",
object, slot, value.GetTaggedWeakRef());
}
if (!heap_->IsAlive(value.GetTaggedWeakRef())) {
LOG_GC(ERROR) << "Heap verify detected a dead weak object at " << value.GetTaggedWeakRef()
<< " at object:" << slot.SlotAddress();
LogErrorForObjSlot(heap_, "Heap verify detected a dead weak object.",
object, slot, value.GetTaggedWeakRef());
++(*failCount_);
}
} else if (value.IsHeapObject()) {
if (ToUintPtr(value.GetTaggedObject()) < INVALID_THRESHOLD) {
LOG_GC(ERROR) << "Heap verify detected an invalid value at " << value.GetTaggedObject()
<< " at object:" << slot.SlotAddress() << ", root:" << root;
LogErrorForObjSlot(heap_, "Heap verify detected an invalid value.",
object, slot, value.GetTaggedObject());
}
if (!heap_->IsAlive(value.GetTaggedObject())) {
LOG_GC(ERROR) << "Heap verify detected a dead object at " << value.GetTaggedObject()
<< " at object:" << slot.SlotAddress();
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));
@ -92,6 +265,18 @@ void VerifyObjectVisitor::operator()(TaggedObject *obj, JSTaggedValue value)
}
}
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;
@ -117,7 +302,7 @@ size_t Verification::VerifyRoot() const
size_t Verification::VerifyHeap() const
{
size_t failCount = heap_->VerifyHeapObjects();
size_t failCount = heap_->VerifyHeapObjects(verifyKind_);
if (failCount > 0) {
LOG_GC(ERROR) << "VerifyHeap detects deadObject count is " << failCount;
}
@ -126,7 +311,7 @@ size_t Verification::VerifyHeap() const
size_t Verification::VerifyOldToNewRSet() const
{
size_t failCount = heap_->VerifyOldToNewRSet();
size_t failCount = heap_->VerifyOldToNewRSet(verifyKind_);
if (failCount > 0) {
LOG_GC(ERROR) << "VerifyOldToNewRSet detects non new space count is " << failCount;
}
@ -137,9 +322,9 @@ void Verification::VerifyObjectSlot(const ObjectSlot &slot, size_t *failCount) c
{
JSTaggedValue value(slot.GetTaggedType());
if (value.IsWeak()) {
VerifyObjectVisitor(heap_, failCount)(value.GetTaggedWeakRef());
VerifyObjectVisitor(heap_, failCount, verifyKind_)(value.GetTaggedWeakRef());
} else if (value.IsHeapObject()) {
VerifyObjectVisitor(heap_, failCount)(value.GetTaggedObject());
VerifyObjectVisitor(heap_, failCount, verifyKind_)(value.GetTaggedObject());
}
}
} // namespace panda::ecmascript

View File

@ -27,12 +27,31 @@
namespace panda::ecmascript {
static constexpr uint32_t INVALID_THRESHOLD = 0x40000;
class VerifyScope {
public:
VerifyScope(Heap *heap) : heap_(heap)
{
heap_->SetVerifying(true);
}
~VerifyScope()
{
heap_->SetVerifying(false);
}
private:
Heap *heap_ {nullptr};
};
// Verify the object body
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions)
class VerifyObjectVisitor {
public:
VerifyObjectVisitor(const Heap *heap, size_t *failCount)
: heap_(heap), failCount_(failCount), objXRay_(heap->GetEcmaVM())
// Only used for verify InactiveSemiSpace
static void VerifyInactiveSemiSpaceMarkedObject(const Heap *heap, void *addr);
VerifyObjectVisitor(const Heap *heap, size_t *failCount,
VerifyKind verifyKind = VerifyKind::VERIFY_PRE_GC)
: heap_(heap), failCount_(failCount), objXRay_(heap->GetEcmaVM()), verifyKind_(verifyKind)
{
}
~VerifyObjectVisitor() = default;
@ -51,24 +70,26 @@ public:
private:
void VisitAllObjects(TaggedObject *obj);
void VisitObject(ObjectSlot slot, TaggedObject *root);
void VerifyObjectSlotLegal(ObjectSlot slot, TaggedObject *obj) const;
void VerifyMarkYoung(TaggedObject *obj, ObjectSlot slot, TaggedObject *value) const;
void VerifyEvacuateYoung(TaggedObject *obj, ObjectSlot slot, TaggedObject *value) const;
void VerifyMarkFull(TaggedObject *obj, ObjectSlot slot, TaggedObject *value) const;
void VerifyEvacuateOld(TaggedObject *obj, ObjectSlot slot, TaggedObject *value) const;
void VerifyEvacuateFull(TaggedObject *obj, ObjectSlot slot, TaggedObject *value) const;
const Heap* const heap_ {nullptr};
size_t* const failCount_ {nullptr};
ObjectXRay objXRay_;
VerifyKind verifyKind_;
};
class Verification {
public:
explicit Verification(const Heap *heap) : heap_(heap), objXRay_(heap->GetEcmaVM()) {}
explicit Verification(Heap *heap, VerifyKind verifyKind = VerifyKind::VERIFY_PRE_GC)
: heap_(heap), objXRay_(heap->GetEcmaVM()), verifyKind_(verifyKind) {}
~Verification() = default;
size_t VerifyAll() const
{
size_t result = VerifyRoot();
result += VerifyHeap();
return result;
}
void VerifyAll() const;
size_t VerifyRoot() const;
size_t VerifyHeap() const;
@ -79,8 +100,9 @@ private:
NO_COPY_SEMANTIC(Verification);
NO_MOVE_SEMANTIC(Verification);
const Heap *heap_ {nullptr};
Heap *heap_ {nullptr};
ObjectXRay objXRay_;
VerifyKind verifyKind_;
};
} // namespace panda::ecmascript

View File

@ -307,7 +307,7 @@ HWTEST_F_L0(DFXJSNApiTests, NotifyApplicationState)
const_cast<ecmascript::Heap *>(heap)->CollectGarbage(TriggerGCType::OLD_GC, GCReason::OTHER);
DFXJSNApi::NotifyApplicationState(vm_, true);
EXPECT_TRUE(concurrentMarker->IsDisabled());
EXPECT_TRUE(sweeper->IsRequestDisabled());
EXPECT_TRUE(sweeper->IsRequestDisabled() || sweeper->IsDisabled());
}
HWTEST_F_L0(DFXJSNApiTests, NotifyMemoryPressure)

View File

@ -1480,6 +1480,9 @@ FreeObject *ObjectFactory::FillFreeObject(uintptr_t address, size_t size, Remove
JSHClass::Cast(globalConst->GetFreeObjectWithTwoFieldClass().GetTaggedObject()));
object->SetAvailable(size);
object->SetNext(INVALID_OBJECT);
if (UNLIKELY(heap_->ShouldVerifyHeap())) {
FillFreeMemoryRange(address + FreeObject::SIZE, address + size);
}
} else if (size == FreeObject::NEXT_OFFSET) {
object = reinterpret_cast<FreeObject *>(address);
object->SetClassWithoutBarrier(
@ -4934,4 +4937,15 @@ JSHandle<JSHClass> ObjectFactory::CreateSFunctionClassWithoutProto(uint32_t size
functionClass->SetExtensible(false);
return functionClass;
}
void ObjectFactory::FillFreeMemoryRange(uintptr_t start, uintptr_t end)
{
ASSERT(start < end);
ASSERT(start % static_cast<uint8_t>(MemAlignment::MEM_ALIGN_OBJECT) == 0);
ASSERT(end % static_cast<uint8_t>(MemAlignment::MEM_ALIGN_OBJECT) == 0);
while (start < end) {
Barriers::SetPrimitive<JSTaggedType>(reinterpret_cast<void*>(start), 0, FREE_MEMMORY_ADDRESS_ZAM_VALUE);
start += sizeof(JSTaggedType);
}
}
} // namespace panda::ecmascript

View File

@ -194,6 +194,7 @@ enum class GrowMode { KEEP, GROW };
class ObjectFactory {
public:
static constexpr JSTaggedType FREE_MEMMORY_ADDRESS_ZAM_VALUE = 0xDEADFACE;
ObjectFactory(JSThread *thread, Heap *heap);
~ObjectFactory() = default;
JSHandle<Method> NewMethodForNativeFunction(const void *func, FunctionKind kind = FunctionKind::NORMAL_FUNCTION,
@ -683,6 +684,9 @@ public:
JSHandle<JSTaggedValue> CreateJSObjectWithNamedProperties(size_t propertyCount, const char **keys,
const Local<JSValueRef> *values);
// Fill the given free memory range with special zam value.
void FillFreeMemoryRange(uintptr_t start, uintptr_t end);
private:
friend class GlobalEnv;
friend class GlobalEnvConstants;

View File

@ -1391,10 +1391,7 @@ HWTEST_F_L0(JSObjectTest, CreateObjectFromProperties)
JSHandle<JSObject> newObj = JSObject::CreateObjectFromProperties(thread, properties);
Heap *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetSweeper()->EnsureAllTaskFinished();
auto failCount = Verification(heap).VerifyAll();
if (failCount > 0) {
LOG_GC(FATAL) << "Heap occur dead object";
}
Verification(heap).VerifyAll();
EXPECT_TRUE(newObj->GetClass()->IsDictionaryMode());
}
} // namespace panda::test