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