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