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