1 /*
2 * Copyright (c) 2024 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 "ark_idle_monitor.h"
17
18 #include <chrono>
19
20 #include "utils/log.h"
21 #if defined(ENABLE_FFRT)
22 #include "ffrt.h"
23 #include "c/executor_task.h"
24 #endif
25 #ifdef ENABLE_UCOLLECTION
26 #include "cpu_collector.h"
27 #endif
28
29 namespace panda::ecmascript {
30
NotifyLooperIdleStart(int64_t timestamp,int idleTime)31 void ArkIdleMonitor::NotifyLooperIdleStart(int64_t timestamp, int idleTime)
32 {
33 SetIdleState(true);
34 AddIdleNotifyCount();
35 if (idleTime < MIN_TRIGGER_GC_IDLE_INTERVAL || !ShouldTryTriggerGC(timestamp)) {
36 return;
37 }
38 JSNApi::NotifyLooperIdleStart(vm_, timestamp, idleTime);
39 }
40
ShouldTryTriggerGC(int64_t timestamp)41 bool ArkIdleMonitor::ShouldTryTriggerGC(int64_t timestamp)
42 {
43 int64_t notifyInterval = timestamp - GetNotifyTimestamp();
44 recordedIdleNotifyInterval_.Push(notifyInterval);
45 SetNotifyTimestamp(timestamp);
46 if (notifyInterval < MIN_TRIGGER_GC_IDLE_INTERVAL ||
47 recordedIdleNotifyInterval_.Count() != IDLE_CHECK_INTERVAL_LENGTH) {
48 return false;
49 }
50 int64_t sumInterval = recordedIdleNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
51 int64_t averageInterval = sumInterval / recordedIdleNotifyInterval_.Count();
52 if (averageInterval > MIN_TRIGGER_GC_IDLE_INTERVAL && notifyInterval < averageInterval * DOUBLE_INTERVAL_CHECK) {
53 return true;
54 }
55 return false;
56 }
57
NotifyLooperIdleEnd(int64_t timestamp)58 void ArkIdleMonitor::NotifyLooperIdleEnd(int64_t timestamp)
59 {
60 SetIdleState(false);
61 AddIdleDuration(timestamp - GetNotifyTimestamp());
62 JSNApi::NotifyLooperIdleEnd(vm_, timestamp);
63 }
64
CheckLowNotifyState() const65 bool ArkIdleMonitor::CheckLowNotifyState() const
66 {
67 int checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
68 HILOG_DEBUG("ArkIdleMonitor: low Notify checkCounts '%{public}d', result '%{public}d' ",
69 checkCounts, static_cast<int>(numberOfLowIdleNotifyCycles_));
70 return numberOfLowIdleNotifyCycles_ >= static_cast<int64_t>(checkCounts);
71 }
72
CheckLowRunningDurationState() const73 bool ArkIdleMonitor::CheckLowRunningDurationState() const
74 {
75 int checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
76 HILOG_DEBUG("ArkIdleMonitor: low Duration checkCounts '%{public}d', result '%{public}d' ",
77 checkCounts, static_cast<int>(numberOfHighIdleTimeRatio_));
78 return numberOfHighIdleTimeRatio_ >= static_cast<int64_t>(checkCounts);
79 }
80
IntervalMonitor()81 void ArkIdleMonitor::IntervalMonitor()
82 {
83 auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
84 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
85 if (IsIdleState()) {
86 AddIdleDuration(nowTimestamp - GetNotifyTimestamp());
87 SetNotifyTimestamp(nowTimestamp);
88 }
89 if (GetIdleNotifyCount() <= LOW_IDLE_NOTIFY_THRESHOLD) {
90 numberOfLowIdleNotifyCycles_++;
91 } else {
92 numberOfLowIdleNotifyCycles_ = 0;
93 }
94 ResetIdleNotifyCount();
95 int64_t recordTotalDuration = nowTimestamp - startRecordTimestamp_;
96 if (recordTotalDuration <= 0) {
97 numberOfHighIdleTimeRatio_ = 0;
98 HILOG_ERROR("ArkIdleMonitor: recordTotalDuration <= 0");
99 } else {
100 double idleTimeRatio = static_cast<double>(GetTotalIdleDuration()) / recordTotalDuration;
101 idleTimeRatio >= IDLE_RATIO ? numberOfHighIdleTimeRatio_++ : (numberOfHighIdleTimeRatio_ = 0);
102 }
103 startRecordTimestamp_ = nowTimestamp;
104 ResetTotalIdleDuration();
105
106 if (CheckLowNotifyState() && CheckLowRunningDurationState()) {
107 NotifyTryCompressGC();
108 PostMonitorTask(SLEEP_MONITORING_INTERVAL);
109 ClearIdleStats();
110 } else {
111 PostMonitorTask(IDLE_MONITORING_INTERVAL);
112 }
113 }
114
PostMonitorTask(uint64_t delayMs)115 void ArkIdleMonitor::PostMonitorTask(uint64_t delayMs)
116 {
117 #if defined(ENABLE_FFRT)
118 auto task = [](void* idleMonitorPtr) {
119 if (idleMonitorPtr != nullptr) {
120 ArkIdleMonitor* arkIdleMonitor = reinterpret_cast<ArkIdleMonitor *>(idleMonitorPtr);
121 arkIdleMonitor->IntervalMonitor();
122 }
123 };
124 if (waitForStopTimerHandler_ != -1) {
125 int ret = ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
126 if (ret != 0) {
127 HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
128 waitForStopTimerHandler_, ret);
129 }
130 }
131 waitForStopTimerHandler_ = currentTimerHandler_;
132 currentTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, delayMs, this, task, false);
133 #endif
134 }
135
~ArkIdleMonitor()136 ArkIdleMonitor::~ArkIdleMonitor()
137 {
138 #if defined(ENABLE_FFRT)
139 if (waitForStopTimerHandler_ != -1) {
140 ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
141 }
142 if (currentTimerHandler_ != -1) {
143 ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
144 }
145 #endif
146 }
147
ClearIdleStats()148 void ArkIdleMonitor::ClearIdleStats()
149 {
150 ResetIdleNotifyCount();
151 ResetTotalIdleDuration();
152 startRecordTimestamp_ = 0;
153 numberOfLowIdleNotifyCycles_ = 0;
154 numberOfHighIdleTimeRatio_ = 0;
155 }
156
NotifyTryCompressGC()157 void ArkIdleMonitor::NotifyTryCompressGC()
158 {
159 #if defined(ENABLE_EVENT_HANDLER)
160 double cpuUsage = GetCpuUsage();
161 if (cpuUsage >= IDLE_CPU_USAGE) {
162 HILOG_INFO("ArkIdleMonitor: Sending a quiet notification is canceled due to high CPU usage: %{public}.2f",
163 cpuUsage);
164 return;
165 }
166 if (mainThreadHandler_ == nullptr) {
167 mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
168 OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
169 };
170 auto task = [this]() {
171 JSNApi::TriggerIdleGC(vm_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
172 JSNApi::TriggerIdleGC(vm_, TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
173 };
174 mainThreadHandler_->PostTask(task, "ARKTS_IDLE_COMPRESS::usaged:" + std::to_string(cpuUsage),
175 0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
176 #endif
177 }
178
SetStartTimerCallback()179 void ArkIdleMonitor::SetStartTimerCallback()
180 {
181 JSNApi::SetStartIdleMonitorCallback([this]() {
182 this->PostMonitorTask();
183 });
184 }
185
NotifyChangeBackgroundState(bool inBackground)186 void ArkIdleMonitor::NotifyChangeBackgroundState(bool inBackground)
187 {
188 inBackground_.store(inBackground, std::memory_order_relaxed);
189 ClearIdleStats();
190 }
191
GetCpuUsage() const192 double ArkIdleMonitor::GetCpuUsage() const
193 {
194 #ifdef ENABLE_UCOLLECTION
195 auto collector = OHOS::HiviewDFX::UCollectClient::CpuCollector::Create();
196 auto collectResult = collector->GetSysCpuUsage();
197 if (collectResult.retCode == OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
198 HILOG_DEBUG("ArkIdleMonitor cpu usage: %{public}.2f", collectResult.data);
199 return collectResult.data;
200 }
201 HILOG_ERROR("ArkIdleMonitor get cpu usage failed, error code:%{public}d", collectResult.retCode);
202 #endif
203 return 0.0f;
204 }
205 }
206
207