mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-07 16:13:49 +00:00
Enhance heap verify
Signed-off-by: chentianyu <chentianyu31@huawei.com> Change-Id: I23890f3531c417e3adda5da0a2b7b231dacd2cf6
This commit is contained in:
parent
5212133062
commit
93babd1795
@ -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
|
||||
|
@ -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],
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user