1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/mem/incremental_marker.h"
17
18 #include <ctime>
19 #include <regex>
20 #include <sys/time.h>
21
22 #include "ecmascript/mem/allocator-inl.h"
23 #include "ecmascript/mem/clock_scope.h"
24 #include "ecmascript/mem/concurrent_marker.h"
25 #include "ecmascript/mem/heap-inl.h"
26 #include "ecmascript/mem/mark_stack.h"
27 #include "ecmascript/mem/mark_word.h"
28 #include "ecmascript/mem/parallel_marker-inl.h"
29 #include "ecmascript/mem/space-inl.h"
30 #include "ecmascript/mem/verification.h"
31 #include "ecmascript/mem/visitor.h"
32 #include "ecmascript/mem/gc_stats.h"
33 #include "ecmascript/ecma_string_table.h"
34 #include "ecmascript/taskpool/taskpool.h"
35 #include "ecmascript/runtime_call_id.h"
36
37 namespace panda::ecmascript {
IncrementalMarker(Heap * heap)38 IncrementalMarker::IncrementalMarker(Heap *heap)
39 : heap_(heap),
40 vm_(heap->GetEcmaVM()),
41 workManager_(heap->GetWorkManager()) {}
42
TriggerIncrementalMark(int64_t idleMicroSec)43 void IncrementalMarker::TriggerIncrementalMark(int64_t idleMicroSec)
44 {
45 ASSERT(!heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark());
46 double startTime = GetCurrentTimeInMs();
47 switch (states_) {
48 case IncrementalGCStates::ROOT_SCAN:
49 Mark();
50 ProcessIncrementalMark(idleMicroSec - (GetCurrentTimeInMs() - startTime));
51 RecordIdleTime(idleMicroSec, startTime, true);
52 break;
53 case IncrementalGCStates::INCREMENTAL_MARK:
54 ProcessIncrementalMark(idleMicroSec);
55 RecordIdleTime(idleMicroSec, startTime);
56 break;
57 case IncrementalGCStates::REMARK:
58 UpdateIncrementalMarkingSpeed(GetCurrentTimeInMs() - startTime_);
59 heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::IDLE);
60 RecordIdleTime(idleMicroSec, startTime);
61 PrintGCIdleUsageStatistic();
62 break;
63 default:
64 LOG_ECMA(FATAL) << "this branch is unreachable";
65 UNREACHABLE();
66 }
67 }
68
Mark()69 void IncrementalMarker::Mark()
70 {
71 LOG_GC(DEBUG) << "IncrementalMarker: Incremental Marking Begin";
72 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "IncrementalMarker::Mark");
73 MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarking);
74 Initialize();
75 heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX);
76 states_ = IncrementalGCStates::INCREMENTAL_MARK;
77 }
78
Initialize()79 void IncrementalMarker::Initialize()
80 {
81 MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarkingInitialize);
82 startTime_ = GetCurrentTimeInMs();
83 startObjectSize_ = heap_->GetHeapObjectSize();
84 isIncrementalMarking_ = true;
85 markingFinished_ = false;
86 heap_->SetMarkType(MarkType::MARK_FULL);
87 heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARKING);
88 heap_->Prepare();
89 heap_->GetOldSpace()->SelectCSet();
90 heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
91 current->ClearMarkGCBitset();
92 current->ClearCrossRegionRSet();
93 });
94 heap_->EnumerateNonNewSpaceRegions([](Region *current) {
95 current->ResetAliveObject();
96 });
97
98 workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK);
99 }
100
ProcessIncrementalMark(int64_t idleMicroSec)101 void IncrementalMarker::ProcessIncrementalMark(int64_t idleMicroSec)
102 {
103 RecursionScope recurScope(this);
104 if (idleMicroSec <= 0) {
105 return;
106 }
107 LOG_GC(DEBUG) << "IncrementalMarker: Process Incremental Marking";
108 uint32_t markStepSize = static_cast<uint32_t>(idleMicroSec) * markingSpeed_;
109 heap_->GetNonMovableMarker()->ProcessIncrementalMarkStack(MAIN_THREAD_INDEX, markStepSize);
110 if (markingFinished_) {
111 states_ = IncrementalGCStates::REMARK;
112 heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARK_FINISHED);
113 return;
114 }
115 }
116
Finish()117 void IncrementalMarker::Finish()
118 {
119 states_ = IncrementalGCStates::ROOT_SCAN;
120 isIncrementalMarking_ = false;
121 heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
122 heap_->GetOldSpace()->RevertCSet();
123 auto callback = [](Region *region) {
124 region->ClearMarkGCBitset();
125 region->ClearCrossRegionRSet();
126 region->ResetAliveObject();
127 };
128 heap_->EnumerateRegions(callback);
129 }
130
GetCurrentTimeInMs()131 double IncrementalMarker::GetCurrentTimeInMs()
132 {
133 struct timeval tv;
134 gettimeofday(&tv, nullptr);
135 return static_cast<double>(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND);
136 }
137
Reset()138 void IncrementalMarker::Reset()
139 {
140 isIncrementalMarking_ = false;
141 states_ = IncrementalGCStates::ROOT_SCAN;
142 heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
143 workManager_->Finish();
144 }
145
RecordIdleTime(int64_t idleMicroSec,double startTime,bool needInitialize)146 void IncrementalMarker::RecordIdleTime(int64_t idleMicroSec, double startTime, bool needInitialize)
147 {
148 if (needInitialize) {
149 receiveIdleTime_ = 0;
150 totalUsedIdleTime_ = 0.0;
151 exceedIdleTime_ = 0.0;
152 }
153 double usedIdleTime = GetCurrentTimeInMs() - startTime;
154 receiveIdleTime_ += idleMicroSec;
155 totalUsedIdleTime_ += std::min((double)idleMicroSec, usedIdleTime);
156 exceedIdleTime_ += usedIdleTime > idleMicroSec ? usedIdleTime - idleMicroSec : 0;
157 }
158
PrintGCIdleUsageStatistic()159 void IncrementalMarker::PrintGCIdleUsageStatistic()
160 {
161 if (!vm_->IsOptionalLogEnabled()) {
162 return;
163 }
164 LOG_GC(INFO) << "/************* GCStats Idle usage statistic: *************/";
165 LOG_GC(INFO) << "Receive idle time: " << receiveIdleTime_ << "ms\n"
166 << "GC in idle time: " << totalUsedIdleTime_ << "ms\n"
167 << "GC out of idle time: " << exceedIdleTime_ << "ms\n"
168 << "Total duration of gc:" << GetCurrentTimeInMs() - startTime_ << "ms";
169 }
170 } // namespace panda::ecmascript
171