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 "purgeable_mem_utils.h"
17
18 #include <climits>
19 #include <unordered_map>
20 #include <string>
21
22 #include "kernel_interface.h"
23 #include "memmgr_log.h"
24
25 namespace OHOS {
26 namespace Memory {
27 namespace {
28 const std::string TAG = "PurgeableMemUtils";
29 }
30
31 IMPLEMENT_SINGLE_INSTANCE(PurgeableMemUtils);
32
33 const std::string PurgeableMemUtils::PATH_PURGE_HEAP = "/proc/sys/kernel/purgeable";
34 const std::string PurgeableMemUtils::PATH_PURGEABLE_ASHMEM = "/proc/purgeable_ashmem_trigger";
35 const std::string PurgeableMemUtils::FILE_PURGE_MEMCG_HEAP = "memory.force_shrink_purgeable_bysize";
36 const std::string PurgeableMemUtils::ACTIVE_PURGEABLE_HEAP = "Active(purg):";
37 const std::string PurgeableMemUtils::INACTIVE_PURGEABLE_HEAP = "Inactive(purg):";
38 const std::string PurgeableMemUtils::PINED_PURGEABLE_HEAP = "Pined(purg):";
39 const std::string PurgeableMemUtils::PROC_PURGEABLE_HEAP = "PurgSum:";
40 const std::string PurgeableMemUtils::PROC_PINED_PURGEABLE_HEAP = "PurgPin:";
41 const unsigned int PurgeableMemUtils::ASHM_PARAM_SIZE_ONE_LINE = 10;
42 const unsigned int PurgeableMemUtils::ASHM_ID_INDEX = 6;
43 const unsigned int PurgeableMemUtils::ASHM_ADJ_INDEX = 2;
44 const unsigned int PurgeableMemUtils::ASHM_REF_COUNT_INDEX = 8;
45 const unsigned int PurgeableMemUtils::ASHM_PURGED_INDEX = 9;
46 const unsigned int PurgeableMemUtils::ASHM_SIZE_INDEX = 5;
47 const unsigned int PurgeableMemUtils::ASHM_TIME_INDEX = 7;
48 const unsigned int PurgeableMemUtils::HEAPINFO_SIZE_ONE_LINE = 3;
49 const unsigned int PurgeableMemUtils::ASHM_PROCESS_NAME_INDEX = 0;
50
GetPurgeableHeapInfo(int & reclaimableKB)51 bool PurgeableMemUtils::GetPurgeableHeapInfo(int &reclaimableKB)
52 {
53 std::vector<std::string> strLines;
54 if (!KernelInterface::GetInstance().ReadLinesFromFile(KernelInterface::MEMINFO_PATH, strLines)) {
55 HILOGE("read file and split to lines failed : %{public}s", KernelInterface::MEMINFO_PATH.c_str());
56 return false;
57 }
58
59 int activeKB = -1;
60 int inactiveKB = -1;
61 int pinedKB = -1;
62 for (auto &it : strLines) {
63 std::vector<std::string> words;
64 KernelInterface::GetInstance().SplitOneLineByBlank(it, words);
65 if (words.size() != HEAPINFO_SIZE_ONE_LINE) {
66 continue;
67 }
68 try {
69 if (words[0] == ACTIVE_PURGEABLE_HEAP) {
70 activeKB = stoi(words[1]);
71 } else if (words[0] == INACTIVE_PURGEABLE_HEAP) {
72 inactiveKB = stoi(words[1]);
73 } else if (words[0] == PINED_PURGEABLE_HEAP) {
74 pinedKB = stoi(words[1]);
75 }
76 } catch (...) {
77 HILOGE("stoi(%{public}s) failed", words[1].c_str());
78 return false;
79 }
80 }
81
82 if (activeKB < 0 || inactiveKB < 0 || pinedKB < 0) {
83 return false;
84 }
85 if (activeKB > (INT_MAX - inactiveKB) || (activeKB + inactiveKB) < (INT_MIN + pinedKB)) {
86 return false;
87 }
88 reclaimableKB = activeKB + inactiveKB - pinedKB;
89 if (reclaimableKB >= 0) {
90 return true;
91 }
92 return false;
93 }
94
GetProcPurgeableHeapInfo(const int pid,int & reclaimableKB)95 bool PurgeableMemUtils::GetProcPurgeableHeapInfo(const int pid, int &reclaimableKB)
96 {
97 std::string path = KernelInterface::GetInstance().JoinPath(KernelInterface::ROOT_PROC_PATH, std::to_string(pid),
98 KernelInterface::FILE_PROC_STATUS);
99 std::vector<std::string> strLines;
100 if (!KernelInterface::GetInstance().ReadLinesFromFile(path, strLines)) {
101 HILOGE("read file and split to lines failed : %{public}s", path.c_str());
102 return false;
103 }
104
105 int purgSumKB = -1;
106 int purgPinKB = -1;
107 for (auto &it : strLines) {
108 std::vector<std::string> words;
109 KernelInterface::GetInstance().SplitOneLineByBlank(it, words);
110 if (words.size() != HEAPINFO_SIZE_ONE_LINE) {
111 continue;
112 }
113 try {
114 if (words[0] == PROC_PURGEABLE_HEAP) {
115 purgSumKB = stoi(words[1]);
116 } else if (words[0] == PROC_PINED_PURGEABLE_HEAP) {
117 purgPinKB = stoi(words[1]);
118 }
119 } catch (...) {
120 HILOGE("stoi(%{public}s) failed", words[1].c_str());
121 return false;
122 }
123 }
124
125 if (purgSumKB < 0 || purgPinKB < 0) {
126 return false;
127 }
128 reclaimableKB = purgSumKB - purgPinKB;
129 if (reclaimableKB >= 0) {
130 return true;
131 }
132 return false;
133 }
134
PurgeHeapAll()135 bool PurgeableMemUtils::PurgeHeapAll()
136 {
137 HILOGD("enter! Purg heap memory all");
138 return KernelInterface::GetInstance().EchoToPath(PATH_PURGE_HEAP.c_str(), "1");
139 }
140
PurgeHeapMemcg(const std::string & memcgPath,const int sizeKB)141 bool PurgeableMemUtils::PurgeHeapMemcg(const std::string &memcgPath, const int sizeKB)
142 {
143 std::string path = KernelInterface::GetInstance().JoinPath(memcgPath, FILE_PURGE_MEMCG_HEAP);
144 HILOGD("enter! Purg heap memory by memcg: size=%{public}d, path=%{public}s\n", sizeKB, path.c_str());
145 return KernelInterface::GetInstance().EchoToPath(path.c_str(), std::to_string(sizeKB).c_str());
146 }
147
GetPurgeableAshmInfo(int & reclaimableKB,std::vector<PurgeableAshmInfo> & ashmInfoToReclaim)148 bool PurgeableMemUtils::GetPurgeableAshmInfo(int &reclaimableKB, std::vector<PurgeableAshmInfo> &ashmInfoToReclaim)
149 {
150 std::vector<std::string> strLines;
151 if (!KernelInterface::GetInstance().ReadLinesFromFile(PATH_PURGEABLE_ASHMEM, strLines)) {
152 HILOGE("read file and split to lines failed : %{public}s", PATH_PURGEABLE_ASHMEM.c_str());
153 return false;
154 }
155
156 std::unordered_map<std::string, PurgeableAshmInfo> ashmIdToInfoMap;
157 ashmIdToInfoMap = PurgeableMemUtils::GetashmIdToInfoMap(strLines);
158
159 reclaimableKB = 0;
160 ashmInfoToReclaim.clear();
161 for (const auto &[_1, value] : ashmIdToInfoMap) {
162 if (value.sizeKB > 0) {
163 ashmInfoToReclaim.emplace_back(value);
164 reclaimableKB += value.sizeKB;
165 }
166 }
167 HILOGD("there are %{public}dKB reclaimable purgeable [ASHM], ashmInfoVector.size()=%{public}zu", reclaimableKB,
168 ashmInfoToReclaim.size());
169 return true;
170 }
171
PurgeAshmAll()172 bool PurgeableMemUtils::PurgeAshmAll()
173 {
174 HILOGD("enter! Purg ashmem memory all");
175 return KernelInterface::GetInstance().EchoToPath(PATH_PURGEABLE_ASHMEM.c_str(), "0 0");
176 }
177
PurgeAshmByIdWithTime(const std::string & idWithTime)178 bool PurgeableMemUtils::PurgeAshmByIdWithTime(const std::string &idWithTime)
179 {
180 HILOGD("enter! Purg ashmem memory: IdWithTime=%{public}s", idWithTime.c_str());
181 return KernelInterface::GetInstance().EchoToPath(PATH_PURGEABLE_ASHMEM.c_str(), idWithTime.c_str());
182 }
183
GetashmIdToInfoMap(const std::vector<std::string> & strLines) const184 PurgeableAshmInfoMap PurgeableMemUtils::GetashmIdToInfoMap(const std::vector<std::string> &strLines) const
185 {
186 std::unordered_map<std::string, PurgeableAshmInfo> ashmIdToInfoMap;
187 for (auto &it : strLines) {
188 HILOGD("[ASHM]: %{public}s", it.c_str());
189 std::vector<std::string> words;
190 KernelInterface::GetInstance().SplitOneLineByDelim(it, ',', words);
191 if (words.size() != ASHM_PARAM_SIZE_ONE_LINE || words[ASHM_REF_COUNT_INDEX] != "0" ||
192 words[ASHM_PURGED_INDEX] != "0") {
193 continue;
194 }
195 std::string curAppName;
196 int minPriority;
197 int sizeKB;
198 try {
199 curAppName = words[ASHM_PROCESS_NAME_INDEX];
200 minPriority = stoi(words[ASHM_ADJ_INDEX]);
201 sizeKB = stoi(words[ASHM_SIZE_INDEX]);
202 } catch (...) {
203 HILOGE("stoi(%{public}s) or stoi(%{public}s) or stoi(%{public}s) failed",
204 words[ASHM_PROCESS_NAME_INDEX].c_str(), words[ASHM_ADJ_INDEX].c_str(), words[ASHM_SIZE_INDEX].c_str());
205 continue;
206 }
207 std::string key = words[ASHM_ID_INDEX] + std::string(" ") + words[ASHM_TIME_INDEX];
208 auto iter = ashmIdToInfoMap.find(key);
209 if (iter == ashmIdToInfoMap.end()) {
210 PurgeableAshmInfo info;
211 info.curAppName = curAppName;
212 info.minPriority = minPriority;
213 info.sizeKB = sizeKB;
214 info.idWithTime = key;
215 ashmIdToInfoMap[key] = info;
216 } else if (iter->second.minPriority > minPriority) {
217 iter->second.minPriority = minPriority;
218 }
219 }
220 return ashmIdToInfoMap;
221 }
222 } // namespace Memory
223 } // namespace OHOS
224