1 /*
2 * Copyright (c) 2022 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 "cgroup_adjuster.h"
17
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <cerrno>
21 #include <string>
22 #include "app_mgr_constants.h"
23 #include "cgroup_event_handler.h"
24 #include "cgroup_sched_log.h"
25 #include "hitrace_meter.h"
26 #include "sched_controller.h"
27 #include "ressched_utils.h"
28 #include "res_sched_hitrace_chain.h"
29 #include "res_type.h"
30
31 #undef LOG_TAG
32 #define LOG_TAG "CgroupAdjuster"
33
34 namespace OHOS {
35 namespace ResourceSchedule {
36 using OHOS::AppExecFwk::ApplicationState;
37 using OHOS::AppExecFwk::AbilityState;
38 using OHOS::AppExecFwk::ExtensionState;
39 constexpr uint32_t MAX_SIZE = 4096;
40 constexpr uint64_t SCHEDULE_CGROUP_FDSAN_TAG = 0xd001702;
41 constexpr uint64_t COMMON_CGROUP_FDSAN_TAG = 0;
42
GetInstance()43 CgroupAdjuster& CgroupAdjuster::GetInstance()
44 {
45 static CgroupAdjuster instance;
46 return instance;
47 }
48
InitAdjuster()49 void CgroupAdjuster::InitAdjuster()
50 {
51 // Trigger load shared library
52 (void)ResSchedUtils::GetInstance();
53 auto handler = SchedController::GetInstance().GetCgroupEventHandler();
54 if (handler) {
55 handler->PostTask([this] {
56 this->AdjustSelfProcessGroup();
57 });
58 }
59 }
60
AdjustForkProcessGroup(Application & app,ProcessRecord & pr)61 void CgroupAdjuster::AdjustForkProcessGroup(Application &app, ProcessRecord &pr)
62 {
63 std::string filePath = ResSchedUtils::GetInstance().GetProcessFilePath(app.GetUid(), app.GetName(), pr.GetPid());
64 int fd = open(filePath.c_str(), O_RDONLY);
65 if (fd < 0) {
66 CGS_LOGD("%{public}s File is not opened, error is %{public}s.",
67 __func__, strerror(errno));
68 return;
69 }
70 fdsan_exchange_owner_tag(fd, COMMON_CGROUP_FDSAN_TAG, SCHEDULE_CGROUP_FDSAN_TAG);
71 char fileContent[MAX_SIZE] = {0};
72 int rd = read(fd, fileContent, sizeof(fileContent));
73 if (rd < 0) {
74 CGS_LOGE("%{public}s Read File Error, error is %{public}s.",
75 __func__, strerror(errno));
76 }
77 const char *flag = "\n";
78 char *line = strtok(fileContent, flag);
79 while (line != NULL) {
80 int32_t forkPid = std::atoi(line);
81 if (forkPid == 0) {
82 CGS_LOGE("%{public}s str to int failed, str = %{public}s", __func__, line);
83 line = strtok(NULL, flag);
84 continue;
85 }
86 line = strtok(NULL, flag);
87 if (forkPid != pr.GetPid()) {
88 int ret = CgroupSetting::SetThreadGroupSchedPolicy(forkPid, pr.curSchedGroup_);
89 if (ret != 0) {
90 CGS_LOGE("%{public}s set %{public}d to group %{public}d failed, ret = %{public}d!",
91 __func__, forkPid, (int)pr.curSchedGroup_, ret);
92 }
93 } else {
94 continue;
95 }
96 }
97 fdsan_close_with_tag(fd, SCHEDULE_CGROUP_FDSAN_TAG);
98 return;
99 }
100
AdjustProcessGroup(Application & app,ProcessRecord & pr,AdjustSource source)101 void CgroupAdjuster::AdjustProcessGroup(Application &app, ProcessRecord &pr, AdjustSource source)
102 {
103 CGS_LOGI("%{public}s for %{public}d, source : %{public}d", __func__, pr.GetPid(), source);
104 ComputeProcessGroup(app, pr, source);
105 ResSchedUtils::GetInstance().ReportArbitrationResult(app, pr, source);
106 ApplyProcessGroup(app, pr);
107
108 if (!app.IsHostProcess(pr.GetPid())) {
109 return;
110 }
111
112 /* Let the sched group of render process, gpu process, and child process follow the sched group of host process */
113 for (const auto &iter : app.GetPidsMap()) {
114 const auto &procRecord = iter.second;
115 if (procRecord && ((procRecord->processType_ == ProcRecordType::RENDER) ||
116 (procRecord->processType_ == ProcRecordType::GPU) ||
117 (procRecord->processType_ == ProcRecordType::CHILD))) {
118 auto hostProcRecord = app.GetProcessRecord(procRecord->hostPid_);
119 if (!hostProcRecord || (procRecord->hostPid_ != pr.GetPid())) {
120 continue;
121 }
122 CGS_LOGD("%{public}s for %{public}d, source : %{public}d for render process",
123 __func__, procRecord->GetPid(), source);
124 procRecord->setSchedGroup_ = hostProcRecord->curSchedGroup_;
125 if (procRecord->processType_ == ProcRecordType::RENDER ||
126 ((procRecord->processType_ == ProcRecordType::GPU) && (hostProcRecord->curSchedGroup_ == SP_TOP_APP))) {
127 CGS_LOGI("%{public}s for %{public}d, source : %{public}d for render process",
128 __func__, procRecord->GetPid(), AdjustSource::ADJS_SELF_RENDER_THREAD);
129 ResSchedUtils::GetInstance().ReportArbitrationResult(app, *(procRecord.get()),
130 AdjustSource::ADJS_SELF_RENDER_THREAD);
131 }
132 ApplyProcessGroup(app, *procRecord);
133 }
134 }
135 }
136
AdjustAllProcessGroup(Application & app,AdjustSource source)137 void CgroupAdjuster::AdjustAllProcessGroup(Application &app, AdjustSource source)
138 {
139 for (const auto &iter : app.GetPidsMap()) {
140 const auto &procRecord = iter.second;
141 if (procRecord && (procRecord->processType_ != ProcRecordType::RENDER) &&
142 (procRecord->processType_ != ProcRecordType::GPU) &&
143 (procRecord->processType_ != ProcRecordType::LINUX) &&
144 (procRecord->processType_ != ProcRecordType::CHILD)) {
145 AdjustProcessGroup(app, *procRecord, source);
146 }
147 }
148 }
149
AdjustSelfProcessGroup()150 inline void CgroupAdjuster::AdjustSelfProcessGroup()
151 {
152 int pid = getpid();
153 int group = SP_FOREGROUND;
154 int ret = CgroupSetting::SetThreadGroupSchedPolicy(pid, group);
155 if (ret != 0) {
156 CGS_LOGE("%{public}s set %{public}d to group %{public}d failed, ret=%{public}d!", __func__, pid, group, ret);
157 }
158 }
159
ComputeProcessGroup(Application & app,ProcessRecord & pr,AdjustSource source)160 void CgroupAdjuster::ComputeProcessGroup(Application &app, ProcessRecord &pr, AdjustSource source)
161 {
162 SchedPolicy group = SP_DEFAULT;
163
164 {
165 ChronoScope cs("ComputeProcessGroup");
166 if (pr.processType_ == ProcRecordType::RENDER) {
167 auto hostProcRecord = app.GetProcessRecord(pr.hostPid_);
168 group = hostProcRecord ? hostProcRecord->curSchedGroup_ : SP_DEFAULT;
169 } else if (source == AdjustSource::ADJS_PROCESS_CREATE) {
170 group = SP_DEFAULT;
171 } else if (app.focusedProcess_ && (app.focusedProcess_->GetPid() == pr.GetPid())) {
172 group = SP_TOP_APP;
173 } else {
174 if (pr.abilities_.size() == 0) {
175 group = SP_DEFAULT;
176 if (pr.processState_ == (int32_t)ApplicationState::APP_STATE_BACKGROUND) {
177 group = SP_BACKGROUND;
178 }
179 } else if (pr.IsVisible()) {
180 group = SP_FOREGROUND;
181 } else if (pr.HasServiceExtension()) {
182 group = SP_DEFAULT;
183 if (pr.processState_ == (int32_t)ApplicationState::APP_STATE_BACKGROUND) {
184 group = SP_BACKGROUND;
185 }
186 } else {
187 if (pr.processState_ == (int32_t)ApplicationState::APP_STATE_BACKGROUND) {
188 group = SP_BACKGROUND;
189 } else if (pr.processState_ == (int32_t)ApplicationState::APP_STATE_FOREGROUND) {
190 group = SP_FOREGROUND;
191 } else {
192 group = SP_DEFAULT;
193 }
194 }
195 }
196 pr.setSchedGroup_ = group;
197 } // end ChronoScope
198 }
199
ApplyProcessGroup(Application & app,ProcessRecord & pr)200 void CgroupAdjuster::ApplyProcessGroup(Application &app, ProcessRecord &pr)
201 {
202 ResSchedHiTraceChain traceChain(__func__);
203 ChronoScope cs("ApplyProcessGroup");
204 if (pr.curSchedGroup_ != pr.setSchedGroup_) {
205 pid_t pid = pr.GetPid();
206 int ret = CgroupSetting::SetThreadGroupSchedPolicy(pid, (int)pr.setSchedGroup_);
207 if (ret != 0) {
208 CGS_LOGE("%{public}s set %{public}d to group %{public}d failed, ret=%{public}d!",
209 __func__, pid, pr.setSchedGroup_, ret);
210 return;
211 }
212
213 pr.lastSchedGroup_ = pr.curSchedGroup_;
214 pr.curSchedGroup_ = pr.setSchedGroup_;
215 CGS_LOGI("%{public}s Set %{public}d's cgroup from %{public}d to %{public}d.",
216 __func__, pr.GetPid(), pr.lastSchedGroup_, pr.curSchedGroup_);
217
218 std::string traceStr(__func__);
219 traceStr.append(" for ").append(std::to_string(pid)).append(", group change from ")
220 .append(std::to_string((int32_t)(pr.lastSchedGroup_))).append(" to ")
221 .append(std::to_string((int32_t)(pr.curSchedGroup_)));
222 StartTraceEx(HITRACE_LEVEL_INFO, HITRACE_TAG_OHOS | HITRACE_TAG_APP, traceStr.c_str());
223
224 nlohmann::json payload;
225 payload["pid"] = std::to_string(pr.GetPid());
226 payload["uid"] = std::to_string(pr.GetUid());
227 payload["name"] = app.GetName();
228 payload["oldGroup"] = std::to_string((int32_t)(pr.lastSchedGroup_));
229 payload["newGroup"] = std::to_string((int32_t)(pr.curSchedGroup_));
230 ResSchedUtils::GetInstance().ReportDataInProcess(ResType::RES_TYPE_CGROUP_ADJUSTER, 0, payload);
231 AdjustForkProcessGroup(app, pr);
232 FinishTraceEx(HITRACE_LEVEL_INFO, HITRACE_TAG_OHOS | HITRACE_TAG_APP);
233 }
234 }
235 } // namespace ResourceSchedule
236 } // namespace OHOS
237