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 #include "low_memory_killer.h"
16 #include "memmgr_config_manager.h"
17 #include "memmgr_log.h"
18 #include "memmgr_ptr_util.h"
19 #include "kernel_interface.h"
20 #include "reclaim_priority_manager.h"
21
22 namespace OHOS {
23 namespace Memory {
24 namespace {
25 const std::string TAG = "LowMemoryKiller";
26 const int LOW_MEM_KILL_LEVELS = 5;
27 const int MAX_KILL_CNT_PER_EVENT = 3;
28 const int NOT_TO_KILL_DURING = 3;
29 /*
30 * LMKD_DBG_TRIGGER_FILE_PATH:
31 * print process meminfo when write 0/1 to the file,
32 * 0: print all info anyway. 1: print limited by interval.
33 * It is used before killing one bundle.
34 */
35 const std::string LMKD_DBG_TRIGGER_FILE_PATH = "/proc/lmkd_dbg_trigger";
36 }
37
38 IMPLEMENT_SINGLE_INSTANCE(LowMemoryKiller);
39
40 enum class MinPrioField {
41 MIN_BUFFER = 0,
42 MIN_PRIO,
43 MIN_PRIO_FIELD_COUNT,
44 };
45
46 static int g_minPrioTable[LOW_MEM_KILL_LEVELS][static_cast<int32_t>(MinPrioField::MIN_PRIO_FIELD_COUNT)] = {
47 {100 * 1024, 0}, // 100M buffer, 0 priority
48 {200 * 1024, 100}, // 200M buffer, 100 priority
49 {300 * 1024, 200}, // 300M buffer, 200 priority
50 {400 * 1024, 300}, // 400M buffer, 300 priority
51 {500 * 1024, 400} // 500M buffer, 400 priority
52 };
53
LowMemoryKiller()54 LowMemoryKiller::LowMemoryKiller()
55 {
56 initialized_ = GetEventHandler();
57 if (initialized_) {
58 HILOGI("init successed");
59 } else {
60 HILOGE("init failed");
61 }
62 }
63
GetEventHandler()64 bool LowMemoryKiller::GetEventHandler()
65 {
66 if (!handler_) {
67 MAKE_POINTER(handler_, shared, AppExecFwk::EventHandler, "failed to create event handler", return false,
68 AppExecFwk::EventRunner::Create());
69 }
70 return true;
71 }
72
GetKillLevel()73 int32_t LowMemoryKiller::GetKillLevel()
74 {
75 return killLevel_;
76 }
77
KillOneBundleByPrio(int minPrio)78 int LowMemoryKiller::KillOneBundleByPrio(int minPrio)
79 {
80 HILOGE("called. minPrio=%{public}d", minPrio);
81 int freedBuf = 0;
82 ReclaimPriorityManager::BunldeCopySet bundles;
83
84 ReclaimPriorityManager::GetInstance().GetOneKillableBundle(minPrio, bundles);
85 HILOGD("get BundlePrioSet size=%{public}zu", bundles.size());
86
87 int count = 0;
88 for (auto bundle : bundles) {
89 HILOGI("iter bundle %{public}d/%{public}zu, uid=%{public}d, name=%{public}s, priority=%{public}d",
90 count, bundles.size(), bundle.uid_, bundle.name_.c_str(), bundle.priority_);
91 if (bundle.priority_ < minPrio) {
92 HILOGD("finish to handle all bundles with priority bigger than %{public}d, break!", minPrio);
93 break;
94 }
95 if (KernelInterface::GetInstance().GetSystemCurTime() - bundle.GetCreateTime() < NOT_TO_KILL_DURING) {
96 HILOGD("bundle uid<%{public}d> <%{public}s> is protected, skiped.",
97 bundle.uid_, bundle.name_.c_str());
98 count++;
99 continue;
100 }
101 if (bundle.GetState() == BundleState::STATE_WAITING_FOR_KILL) {
102 HILOGD("bundle uid<%{public}d> <%{public}s> is waiting to kill, skiped.",
103 bundle.uid_, bundle.name_.c_str());
104 count++;
105 continue;
106 }
107
108 for (auto itrProcess = bundle.procs_.begin(); itrProcess != bundle.procs_.end(); itrProcess++) {
109 HILOGI("killing pid<%{public}d> with uid<%{public}d> of bundle<%{public}s>",
110 itrProcess->first, bundle.uid_, bundle.name_.c_str());
111 freedBuf += KernelInterface::GetInstance().KillOneProcessByPid(itrProcess->first);
112 }
113
114 ReclaimPriorityManager::GetInstance().SetBundleState(bundle.accountId_, bundle.uid_,
115 BundleState::STATE_WAITING_FOR_KILL);
116 if (freedBuf) {
117 HILOGD("freedBuf = %{public}d, break iter", freedBuf);
118 break;
119 }
120 count++;
121 }
122 HILOGD("iter bundles end");
123 return freedBuf;
124 }
125
QueryKillMemoryPriorityPair(unsigned int currBufferKB,unsigned int & targetBufKB,int & killLevel)126 std::pair<unsigned int, int> LowMemoryKiller::QueryKillMemoryPriorityPair(unsigned int currBufferKB,
127 unsigned int &targetBufKB, int &killLevel)
128 {
129 unsigned int minBufKB = 0;
130 int minPrio = RECLAIM_PRIORITY_UNKNOWN + 1;
131 int tempKillLevel = 0;
132
133 targetBufKB = 0; /* default val */
134 static const KillConfig::KillLevelsMap levelMap = MemmgrConfigManager::GetInstance().GetKillLevelsMap();
135 if (levelMap.empty()) { /* xml not config, using default table */
136 for (int i = 0; i < LOW_MEM_KILL_LEVELS; i++) {
137 int minBufInTable = g_minPrioTable[i][static_cast<int32_t>(MinPrioField::MIN_BUFFER)];
138 if (minBufInTable < 0) {
139 HILOGE("error: negative value(%{public}d) of mem in g_minPrioTable", minBufInTable);
140 continue;
141 }
142 tempKillLevel++;
143 if (currBufferKB < (unsigned int)minBufInTable) {
144 minBufKB = (unsigned int)minBufInTable;
145 minPrio = g_minPrioTable[i][static_cast<int32_t>(MinPrioField::MIN_PRIO)];
146 break;
147 }
148 }
149 /* set targetBufKB = max mem val in g_minPrioTable */
150 int maxMemInTable = g_minPrioTable[LOW_MEM_KILL_LEVELS - 1][static_cast<int32_t>(MinPrioField::MIN_BUFFER)];
151 targetBufKB = (maxMemInTable > 0 ? (unsigned int)maxMemInTable : 0);
152 killLevel = tempKillLevel;
153 return std::make_pair(minBufKB, minPrio);
154 }
155 /* query from xml */
156 for (auto it = levelMap.begin(); it != levelMap.end(); it++) {
157 tempKillLevel++;
158 if (currBufferKB < it->first) {
159 minBufKB = it->first;
160 minPrio = it->second;
161 break;
162 }
163 }
164 /* set targetBufKB = max mem val in levelMap */
165 targetBufKB = levelMap.rbegin()->first;
166 killLevel = tempKillLevel;
167 HILOGD("(%{public}u) return from xml mem:%{public}u prio:%{public}d target:%{public}u",
168 currBufferKB, minBufKB, minPrio, targetBufKB);
169 return std::make_pair(minBufKB, minPrio);
170 }
171
172 /* Low memory killer core function */
PsiHandlerInner()173 void LowMemoryKiller::PsiHandlerInner()
174 {
175 HILOGD("[%{public}ld] called", ++calledCount_);
176 int freedBuf = 0;
177 unsigned int targetBuf = 0;
178 unsigned int targetKillKb = 0;
179 unsigned int currKillKb = 0;
180 int killCnt = 0;
181
182 unsigned int curBuf = static_cast<unsigned int>(KernelInterface::GetInstance().GetCurrentBuffer());
183 HILOGE("[%{public}ld] current buffer = %{public}u KB", calledCount_, curBuf);
184 if (curBuf == MAX_BUFFER_KB) {
185 HILOGD("[%{public}ld] get buffer failed, skiped!", calledCount_);
186 return;
187 }
188
189 std::pair<unsigned int, int> memPrioPair = QueryKillMemoryPriorityPair(curBuf, targetBuf, killLevel_);
190 unsigned int minBuf = memPrioPair.first;
191 int minPrio = memPrioPair.second;
192 if (curBuf > 0 && targetBuf > curBuf) {
193 targetKillKb = targetBuf - curBuf;
194 }
195
196 HILOGE("[%{public}ld] minPrio = %{public}d", calledCount_, minPrio);
197
198 if (minPrio < RECLAIM_PRIORITY_MIN || minPrio > RECLAIM_PRIORITY_MAX) {
199 HILOGD("[%{public}ld] no minPrio, skiped!", calledCount_);
200 return;
201 }
202
203 do {
204 /* print process mem info in dmesg, 1 means it is limited by print interval. Ignore return val */
205 KernelInterface::GetInstance().EchoToPath(LMKD_DBG_TRIGGER_FILE_PATH.c_str(), "1");
206 if ((freedBuf = KillOneBundleByPrio(minPrio)) <= 0) {
207 HILOGD("[%{public}ld] Noting to kill above score %{public}d!", calledCount_, minPrio);
208 goto out;
209 }
210 currKillKb += (unsigned int)freedBuf;
211 killCnt++;
212 HILOGD("[%{public}ld] killCnt = %{public}d", calledCount_, killCnt);
213
214 int availBuf = KernelInterface::GetInstance().GetCurrentBuffer();
215 if (availBuf < 0 || availBuf >= MAX_BUFFER_KB) {
216 HILOGE("[%{public}ld] get buffer failed, go out!", calledCount_);
217 goto out;
218 }
219 if ((unsigned int)availBuf >= targetBuf) {
220 killLevel_ = 0;
221 goto out;
222 }
223 } while (currKillKb < targetKillKb && killCnt < MAX_KILL_CNT_PER_EVENT);
224
225 out:
226 if (currKillKb > 0) {
227 HILOGI("[%{public}ld] Reclaimed %{public}uK when currBuff %{public}uK below %{public}uK target %{public}uK",
228 calledCount_, currKillKb, curBuf, minBuf, targetBuf);
229 }
230 }
231
PsiHandler()232 void LowMemoryKiller::PsiHandler()
233 {
234 if (!initialized_) {
235 HILOGE("is not initialized, return!");
236 return;
237 }
238 handler_->PostImmediateTask([this] { this->PsiHandlerInner(); });
239 }
240 } // namespace Memory
241 } // namespace OHOS