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/heap-inl.h"
25 #include "ecmascript/mem/mark_stack.h"
26 #include "ecmascript/mem/mark_word.h"
27 #include "ecmascript/mem/parallel_marker-inl.h"
28 #include "ecmascript/mem/space-inl.h"
29 #include "ecmascript/mem/verification.h"
30 #include "ecmascript/mem/visitor.h"
31 #include "ecmascript/mem/gc_stats.h"
32 #include "ecmascript/ecma_string_table.h"
33 #include "ecmascript/taskpool/taskpool.h"
34 #include "ecmascript/runtime_call_id.h"
35
36 namespace panda::ecmascript {
IncrementalMarker(Heap * heap)37 IncrementalMarker::IncrementalMarker(Heap *heap)
38 : heap_(heap),
39 vm_(heap->GetEcmaVM()),
40 workManager_(heap->GetWorkManager()) {}
41
TriggerIncrementalMark(int64_t idleMicroSec)42 void IncrementalMarker::TriggerIncrementalMark(int64_t idleMicroSec)
43 {
44 double startTime = GetCurrentTimeInMs();
45 switch (states_) {
46 case IncrementalGCStates::ROOT_SCAN:
47 Mark();
48 ProcessIncrementalMark(idleMicroSec - (GetCurrentTimeInMs() - startTime));
49 RecordIdleTime(idleMicroSec, startTime, true);
50 break;
51 case IncrementalGCStates::INCREMENTAL_MARK:
52 ProcessIncrementalMark(idleMicroSec);
53 RecordIdleTime(idleMicroSec, startTime);
54 break;
55 case IncrementalGCStates::REMARK:
56 UpdateIncrementalMarkingSpeed(GetCurrentTimeInMs() - startTime_);
57 heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::IDLE);
58 RecordIdleTime(idleMicroSec, startTime);
59 PrintGCIdleUsageStatistic();
60 break;
61 default:
62 LOG_ECMA(FATAL) << "this branch is unreachable";
63 UNREACHABLE();
64 }
65 }
66
Mark()67 void IncrementalMarker::Mark()
68 {
69 LOG_GC(DEBUG) << "IncrementalMarker: Incremental Marking Begin";
70 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "IncrementalMarker::Mark");
71 MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarking);
72 Initialize();
73 heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX);
74 states_ = IncrementalGCStates::INCREMENTAL_MARK;
75 }
76
Initialize()77 void IncrementalMarker::Initialize()
78 {
79 MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarkingInitialize);
80 startTime_ = GetCurrentTimeInMs();
81 startObjectSize_ = heap_->GetHeapObjectSize();
82 isIncrementalMarking_ = true;
83 markingFinished_ = false;
84 heap_->SetMarkType(MarkType::MARK_FULL);
85 heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARKING);
86 heap_->Prepare();
87 heap_->GetOldSpace()->SelectCSet();
88 heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
89 current->ClearMarkGCBitset();
90 current->ClearCrossRegionRSet();
91 });
92 heap_->EnumerateNonNewSpaceRegions([](Region *current) {
93 current->ResetAliveObject();
94 });
95
96 workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK);
97 }
98
ProcessIncrementalMark(int64_t idleMicroSec)99 void IncrementalMarker::ProcessIncrementalMark(int64_t idleMicroSec)
100 {
101 if (idleMicroSec <= 0) {
102 return;
103 }
104 LOG_GC(DEBUG) << "IncrementalMarker: Process Incremental Marking";
105 uint32_t markStepSize = static_cast<uint32_t>(idleMicroSec) * markingSpeed_;
106 heap_->GetNonMovableMarker()->ProcessIncrementalMarkStack(MAIN_THREAD_INDEX, markStepSize);
107 if (markingFinished_) {
108 states_ = IncrementalGCStates::REMARK;
109 heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARK_FINISHED);
110 return;
111 }
112 }
113
Finish()114 void IncrementalMarker::Finish()
115 {
116 states_ = IncrementalGCStates::ROOT_SCAN;
117 isIncrementalMarking_ = false;
118 heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
119 heap_->GetOldSpace()->RevertCSet();
120 auto callback = [](Region *region) {
121 region->ClearMarkGCBitset();
122 region->ClearCrossRegionRSet();
123 region->ResetAliveObject();
124 };
125 heap_->EnumerateRegions(callback);
126 }
127
GetCurrentTimeInMs()128 double IncrementalMarker::GetCurrentTimeInMs()
129 {
130 struct timeval tv;
131 gettimeofday(&tv, nullptr);
132 return static_cast<double>(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND);
133 }
134
Reset()135 void IncrementalMarker::Reset()
136 {
137 isIncrementalMarking_ = false;
138 states_ = IncrementalGCStates::ROOT_SCAN;
139 heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
140 workManager_->Finish();
141 }
142
RecordIdleTime(int64_t idleMicroSec,double startTime,bool needInitialize)143 void IncrementalMarker::RecordIdleTime(int64_t idleMicroSec, double startTime, bool needInitialize)
144 {
145 if (needInitialize) {
146 receiveIdleTime_ = 0;
147 totalUsedIdleTime_ = 0.0;
148 exceedIdleTime_ = 0.0;
149 }
150 double usedIdleTime = GetCurrentTimeInMs() - startTime;
151 receiveIdleTime_ += idleMicroSec;
152 totalUsedIdleTime_ += std::min((double)idleMicroSec, usedIdleTime);
153 exceedIdleTime_ += usedIdleTime > idleMicroSec ? usedIdleTime - idleMicroSec : 0;
154 }
155
PrintGCIdleUsageStatistic()156 void IncrementalMarker::PrintGCIdleUsageStatistic()
157 {
158 if (!vm_->IsOptionalLogEnabled()) {
159 return;
160 }
161 LOG_GC(INFO) << "/************* GCStats Idle usage statistic: *************/";
162 LOG_GC(INFO) << "Receive idle time: " << receiveIdleTime_ << "ms\n"
163 << "GC in idle time: " << totalUsedIdleTime_ << "ms\n"
164 << "GC out of idle time: " << exceedIdleTime_ << "ms\n"
165 << "Total duration of gc:" << GetCurrentTimeInMs() - startTime_ << "ms";
166 }
167 } // namespace panda::ecmascript
168