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
16 #include "kernel_interface.h"
17 #include "memmgr_log.h"
18
19 #include "directory_ex.h"
20 #include "file_ex.h"
21
22 #include <securec.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <dirent.h>
26
27 #include <fstream>
28
29 #include <sstream>
30 #include <csignal>
31
32 namespace OHOS {
33 namespace Memory {
34 namespace {
35 const std::string TAG = "KernelInterface";
36 }
37
38 IMPLEMENT_SINGLE_INSTANCE(KernelInterface);
39
40 const std::string KernelInterface::MEMCG_BASE_PATH = "/dev/memcg";
41
42 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_PATH = "/dev/memcg/memory.zswapd_pressure_show";
43 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_BUFFER_SIZE = "buffer_size";
44
EchoToPath(const char * path,const char * content)45 bool KernelInterface::EchoToPath(const char* path, const char* content)
46 {
47 int fd = open(path, O_RDWR, 0666);
48 if (fd == -1) {
49 HILOGE("echo %{public}s > %{public}s failed: file is not open", content, path);
50 return false;
51 }
52 if (write(fd, content, strlen(content)) < 0) {
53 HILOGE("echo %{public}s > %{public}s failed: write failed", content, path);
54 close(fd);
55 return false;
56 }
57 close(fd);
58 HILOGI("echo %{public}s > %{public}s", content, path);
59 return true;
60 }
61
IsFileExists(const std::string & path)62 bool KernelInterface::IsFileExists(const std::string& path)
63 {
64 if (path.empty()) {
65 return false;
66 }
67 struct stat st = {};
68 if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
69 return true;
70 }
71 return false;
72 }
73
CreateFile(const std::string & path,const mode_t & mode)74 bool KernelInterface::CreateFile(const std::string& path, const mode_t& mode)
75 {
76 if (path.empty()) {
77 return false;
78 }
79 if (IsFileExists(path)) {
80 return true;
81 }
82
83 std::ofstream fout(path);
84 if (!fout.is_open()) {
85 return false;
86 }
87 fout.flush();
88 fout.close();
89 if (chmod(path.c_str(), mode) != 0) {
90 return false;
91 }
92
93 return true;
94 }
95
CreateFile(const std::string & path)96 bool KernelInterface::CreateFile(const std::string& path)
97 {
98 return CreateFile(path, FILE_MODE_644);
99 }
100
RemoveFile(const std::string & path)101 bool KernelInterface::RemoveFile(const std::string& path)
102 {
103 return OHOS::RemoveFile(path);
104 }
105
WriteToFile(const std::string & path,const std::string & content,bool truncated)106 bool KernelInterface::WriteToFile(const std::string& path, const std::string& content, bool truncated)
107 {
108 return OHOS::SaveStringToFile(path, content, truncated);
109 }
110
WriteLinesToFile(const std::string & path,const std::vector<std::string> & lines,bool truncated)111 bool KernelInterface::WriteLinesToFile(const std::string& path, const std::vector<std::string>& lines, bool truncated)
112 {
113 if (truncated) {
114 RemoveFile(path); // clear file content
115 }
116 for (std::string line : lines) {
117 if (!SaveStringToFile(path, line + "\n", false)) {
118 return false;
119 }
120 }
121 return true;
122 }
123
ReadFromFile(const std::string & path,std::string & content)124 bool KernelInterface::ReadFromFile(const std::string& path, std::string& content)
125 {
126 return OHOS::LoadStringFromFile(path, content);
127 }
128
ReadLinesFromFile(const std::string & path,std::vector<std::string> & lines)129 bool KernelInterface::ReadLinesFromFile(const std::string& path, std::vector<std::string>& lines)
130 {
131 if (!IsFileExists(path)) {
132 HILOGE("no such file: %{public}s", path.c_str());
133 return false;
134 }
135 std::string line;
136 std::ifstream inf(path, std::ifstream::in);
137 if (!inf) {
138 HILOGE("ifstream(%{public}s) failed", path.c_str());
139 return false;
140 }
141 lines.clear();
142 while (!inf.eof()) {
143 getline(inf, line);
144 lines.push_back(line);
145 }
146 inf.close();
147 return true;
148 }
149
IsDirExists(const std::string & path)150 bool KernelInterface::IsDirExists(const std::string& path)
151 {
152 if (path.empty()) {
153 return false;
154 }
155 struct stat st = {};
156 if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
157 return true;
158 }
159 return false;
160 }
161
IsExists(const std::string & path)162 bool KernelInterface::IsExists(const std::string& path)
163 {
164 return OHOS::FileExists(path);
165 }
166
IsEmptyDir(const std::string & path)167 bool KernelInterface::IsEmptyDir(const std::string& path)
168 {
169 return OHOS::IsEmptyFolder(path);
170 }
171
CreateDir(const std::string & path)172 bool KernelInterface::CreateDir(const std::string& path)
173 {
174 return OHOS::ForceCreateDirectory(path); // default mode 755
175 }
176
RemoveDirRecursively(const std::string & path)177 bool KernelInterface::RemoveDirRecursively(const std::string& path)
178 {
179 return OHOS::ForceRemoveDirectory(path) || (remove(path.c_str()) == 0);
180 }
181
RmDelimiter(const std::string & path)182 std::string KernelInterface::RmDelimiter(const std::string& path)
183 {
184 return OHOS::ExcludeTrailingPathDelimiter(path);
185 }
186
AddDelimiter(const std::string & path)187 std::string KernelInterface::AddDelimiter(const std::string& path)
188 {
189 return OHOS::IncludeTrailingPathDelimiter(path);
190 }
191
JoinPath(const std::string & prefixPath,const std::string & subPath)192 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& subPath)
193 {
194 return AddDelimiter(prefixPath) + subPath;
195 }
196
JoinPath(const std::string & prefixPath,const std::string & midPath,const std::string & subPath)197 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& midPath,
198 const std::string& subPath)
199 {
200 return JoinPath(JoinPath(prefixPath, midPath), subPath);
201 }
202
GetPidProcInfo(struct ProcInfo & procInfo)203 bool KernelInterface::GetPidProcInfo(struct ProcInfo &procInfo)
204 {
205 HILOGD("called!");
206
207 std::string statPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/stat");
208
209 // format like:
210 // 1 (init) S 0 0 0 0 -1 4210944 1 ...
211 std::string stat, statm, statPid, vss, rss;
212 if (!ReadFromFile(statPath, stat)) {
213 return false;
214 }
215 std::istringstream isStat(stat);
216 isStat >> statPid >> procInfo.name >> procInfo.status;
217
218 if (statPid != std::to_string(procInfo.pid)) {
219 return false;
220 }
221
222 std::string statmPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/statm");
223 // format like:
224 // 640 472 369 38 0 115 0
225 if (!ReadFromFile(statmPath, statm)) {
226 return false;
227 }
228 std::istringstream isStatm(statm);
229 isStatm >> vss >> rss; // pages
230
231 procInfo.size = atoi(rss.c_str()) * PAGE_TO_KB;
232 HILOGI("GetProcInfo success: name is %{public}s, status is %{public}s, size = %{public}d KB",
233 procInfo.name.c_str(), procInfo.status.c_str(), procInfo.size);
234 return true;
235 }
236
ReadZswapdPressureShow(std::map<std::string,std::string> & result)237 void KernelInterface::ReadZswapdPressureShow(std::map<std::string, std::string>& result)
238 {
239 std::string contentStr;
240 if (!ReadFromFile(ZWAPD_PRESSURE_SHOW_PATH, contentStr)) {
241 HILOGE("read %{public}s faild, content=[%{public}s]", ZWAPD_PRESSURE_SHOW_PATH.c_str(), contentStr.c_str());
242 return;
243 }
244 char *contentPtr = new char[contentStr.size() + 1];
245 if (contentPtr == nullptr) {
246 HILOGE("alloc buffer fail");
247 return;
248 }
249 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
250 HILOGE("copy fail");
251 return;
252 }
253 char *restPtr;
254 char *line = strtok_r(contentPtr, "\n", &restPtr);
255 do {
256 for (size_t i = 0; i < strlen(line); i++) {
257 if (line[i] == ':') {
258 line[i] = ' ';
259 }
260 }
261 std::string lineStr(line);
262 std::istringstream is(lineStr);
263 std::string name, value;
264 is >> name >> value;
265 result.insert(std::make_pair(name, value));
266
267 line = strtok_r(NULL, "\n", &restPtr);
268 } while (line);
269 if (restPtr) {
270 delete [] restPtr;
271 }
272 if (contentPtr) {
273 delete [] contentPtr;
274 }
275 return;
276 }
277
GetCurrentBuffer()278 int KernelInterface::GetCurrentBuffer()
279 {
280 std::map<std::string, std::string> result;
281 ReadZswapdPressureShow(result);
282 auto value = result.find(ZWAPD_PRESSURE_SHOW_BUFFER_SIZE);
283 if (value != result.end()) {
284 HILOGD("buffer_size=%{public}s MB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
285 return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()) * KB_PER_MB;
286 }
287 return MAX_BUFFER_KB;
288 }
289
KillOneProcessByPid(int pid)290 int KernelInterface::KillOneProcessByPid(int pid)
291 {
292 HILOGD("called! pid=%{public}d", pid);
293 struct ProcInfo procInfo;
294 int freedBuffer = 0;
295 procInfo.pid = pid;
296
297 if (!GetPidProcInfo(procInfo)) {
298 HILOGE("GetPidProcInfo fail !!!");
299 goto out;
300 }
301
302 if (procInfo.status == "D") {
303 HILOGE("Task %{public}s is at D status!", procInfo.name.c_str());
304 goto out;
305 }
306
307 if (kill(pid, SIGKILL)) {
308 HILOGE("Kill %{public}s errno=%{public}d!", procInfo.name.c_str(), errno);
309 }
310 HILOGE("%{public}s has been Killed ! (pid=%{public}d, freedSize=%{public}d KB)",
311 procInfo.name.c_str(), procInfo.pid, procInfo.size);
312
313 freedBuffer = procInfo.size;
314 out:
315 return freedBuffer;
316 }
317 } // namespace Memory
318 } // namespace OHOS
319