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 #include <regex>
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::ROOT_PROC_PATH = "/proc";
41 const std::string KernelInterface::MEMCG_BASE_PATH = "/dev/memcg";
42
43 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_PATH = "/dev/memcg/memory.zswapd_pressure_show";
44 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_BUFFER_SIZE = "buffer_size";
45
EchoToPath(const char * path,const char * content)46 bool KernelInterface::EchoToPath(const char* path, const char* content)
47 {
48 int fd = open(path, O_RDWR, FILE_MODE_666);
49 if (fd == -1) {
50 HILOGE("echo %{public}s > %{public}s failed: file is not open", content, path);
51 return false;
52 }
53 if (write(fd, content, strlen(content)) < 0) {
54 HILOGE("echo %{public}s > %{public}s failed: write failed", content, path);
55 close(fd);
56 return false;
57 }
58 close(fd);
59 HILOGI("echo %{public}s > %{public}s", content, path);
60 return true;
61 }
62
IsFileExists(const std::string & path)63 bool KernelInterface::IsFileExists(const std::string& path)
64 {
65 if (path.empty()) {
66 return false;
67 }
68 struct stat st = {};
69 if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
70 return true;
71 }
72 return false;
73 }
74
CreateFile(const std::string & path,const mode_t & mode)75 bool KernelInterface::CreateFile(const std::string& path, const mode_t& mode)
76 {
77 if (path.empty()) {
78 return false;
79 }
80 if (IsFileExists(path)) {
81 return true;
82 }
83
84 std::ofstream fout(path);
85 if (!fout.is_open()) {
86 return false;
87 }
88 fout.flush();
89 fout.close();
90 if (chmod(path.c_str(), mode) != 0) {
91 return false;
92 }
93
94 return true;
95 }
96
CreateFile(const std::string & path)97 bool KernelInterface::CreateFile(const std::string& path)
98 {
99 return CreateFile(path, FILE_MODE_644);
100 }
101
RemoveFile(const std::string & path)102 bool KernelInterface::RemoveFile(const std::string& path)
103 {
104 return OHOS::RemoveFile(path);
105 }
106
WriteToFile(const std::string & path,const std::string & content,bool truncated)107 bool KernelInterface::WriteToFile(const std::string& path, const std::string& content, bool truncated)
108 {
109 int fd;
110 char actualPath[PATH_MAX + 1] = {0};
111 char* ptrRet = NULL;
112
113 if (strlen(path.c_str()) == 0 || strlen(path.c_str()) > PATH_MAX) {
114 HILOGE("file path is invalid");
115 return false;
116 }
117 ptrRet = realpath(path.c_str(), actualPath);
118 if (!ptrRet) {
119 HILOGE("file path cannot be canonicalized");
120 return false;
121 }
122 HILOGD("path:%{public}s, actualPath:%{public}s", path.c_str(), actualPath);
123 fd = open(actualPath, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
124 if (fd == -1) {
125 HILOGE("echo %{public}s %{public}s %{public}s failed: file is not open",
126 content.c_str(), truncated ? ">" : ">>", path.c_str());
127 ptrRet = NULL;
128 return false;
129 }
130 if (write(fd, content.c_str(), strlen(content.c_str())) < 0) {
131 HILOGE("echo %{public}s %{public}s %{public}s failed: write failed",
132 content.c_str(), truncated ? ">" : ">>", path.c_str());
133 ptrRet = NULL;
134 close(fd);
135 return false;
136 }
137 ptrRet = NULL;
138 close(fd);
139 HILOGD("echo %{public}s %{public}s %{public}s", content.c_str(), truncated ? ">" : ">>", path.c_str());
140 return true;
141 }
142
ReadFromFile(const std::string & path,std::string & content)143 bool KernelInterface::ReadFromFile(const std::string& path, std::string& content)
144 {
145 return OHOS::LoadStringFromFile(path, content);
146 }
147
ReadLinesFromFile(const std::string & path,std::vector<std::string> & lines)148 bool KernelInterface::ReadLinesFromFile(const std::string& path, std::vector<std::string>& lines)
149 {
150 if (!IsFileExists(path)) {
151 HILOGE("no such file: %{public}s", path.c_str());
152 return false;
153 }
154 std::string line;
155 std::ifstream inf(path, std::ifstream::in);
156 if (!inf) {
157 HILOGE("ifstream(%{public}s) failed", path.c_str());
158 return false;
159 }
160 lines.clear();
161 while (!inf.eof()) {
162 getline(inf, line);
163 lines.push_back(line);
164 }
165 inf.close();
166 return true;
167 }
168
IsDirExists(const std::string & path)169 bool KernelInterface::IsDirExists(const std::string& path)
170 {
171 if (path.empty()) {
172 return false;
173 }
174 struct stat st = {};
175 if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
176 return true;
177 }
178 return false;
179 }
180
IsExists(const std::string & path)181 bool KernelInterface::IsExists(const std::string& path)
182 {
183 return OHOS::FileExists(path);
184 }
185
IsEmptyDir(const std::string & path)186 bool KernelInterface::IsEmptyDir(const std::string& path)
187 {
188 return OHOS::IsEmptyFolder(path);
189 }
190
CreateDir(const std::string & path)191 bool KernelInterface::CreateDir(const std::string& path)
192 {
193 return OHOS::ForceCreateDirectory(path); // default mode 755
194 }
195
RemoveDirRecursively(const std::string & path)196 bool KernelInterface::RemoveDirRecursively(const std::string& path)
197 {
198 return OHOS::ForceRemoveDirectory(path) || (remove(path.c_str()) == 0);
199 }
200
RmDelimiter(const std::string & path)201 std::string KernelInterface::RmDelimiter(const std::string& path)
202 {
203 return OHOS::ExcludeTrailingPathDelimiter(path);
204 }
205
AddDelimiter(const std::string & path)206 std::string KernelInterface::AddDelimiter(const std::string& path)
207 {
208 return OHOS::IncludeTrailingPathDelimiter(path);
209 }
210
JoinPath(const std::string & prefixPath,const std::string & subPath)211 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& subPath)
212 {
213 return AddDelimiter(prefixPath) + subPath;
214 }
215
JoinPath(const std::string & prefixPath,const std::string & midPath,const std::string & subPath)216 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& midPath,
217 const std::string& subPath)
218 {
219 return JoinPath(JoinPath(prefixPath, midPath), subPath);
220 }
221
GetPidProcInfo(struct ProcInfo & procInfo)222 bool KernelInterface::GetPidProcInfo(struct ProcInfo &procInfo)
223 {
224 HILOGD("called!");
225
226 std::string statPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/stat");
227
228 // format like:
229 // 1 (init) S 0 0 0 0 -1 4210944 1 ...
230 std::string stat, statm, statPid, vss, rss;
231 if (!ReadFromFile(statPath, stat)) {
232 HILOGD("stat file error!");
233 return false;
234 }
235 std::istringstream isStat(stat);
236 isStat >> statPid >> procInfo.name >> procInfo.status;
237
238 if (statPid != std::to_string(procInfo.pid)) {
239 HILOGD("pid error!");
240 return false;
241 }
242
243 std::string statmPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/statm");
244 // format like:
245 // 640 472 369 38 0 115 0
246 if (!ReadFromFile(statmPath, statm)) {
247 HILOGD("statm file error!");
248 return false;
249 }
250 std::istringstream isStatm(statm);
251 isStatm >> vss >> rss; // pages
252
253 procInfo.size = atoi(rss.c_str()) * PAGE_TO_KB;
254 HILOGI("GetProcInfo success: name is %{public}s, status is %{public}s, size = %{public}d KB",
255 procInfo.name.c_str(), procInfo.status.c_str(), procInfo.size);
256 return true;
257 }
258
GetProcNameByPid(int pid,std::string & name)259 bool KernelInterface::GetProcNameByPid(int pid, std::string &name)
260 {
261 std::string statusPath = JoinPath("/proc/", std::to_string(pid), "/status");
262 std::string statusContent, nameTag;
263 if (!ReadFromFile(statusPath, statusContent)) {
264 HILOGE("status file [%{public}s] error!", statusPath.c_str());
265 return false;
266 }
267 std::istringstream statusStream(statusContent);
268 statusStream >> nameTag >> name;
269 return true;
270 }
271
ReadZswapdPressureShow(std::map<std::string,std::string> & result)272 void KernelInterface::ReadZswapdPressureShow(std::map<std::string, std::string>& result)
273 {
274 std::string contentStr;
275 if (!ReadFromFile(ZWAPD_PRESSURE_SHOW_PATH, contentStr)) {
276 HILOGE("read %{public}s faild, content=[%{public}s]", ZWAPD_PRESSURE_SHOW_PATH.c_str(), contentStr.c_str());
277 return;
278 }
279 char *contentPtr = new char[contentStr.size() + 1];
280 if (contentPtr == nullptr) {
281 HILOGE("alloc buffer fail");
282 return;
283 }
284 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
285 HILOGE("copy fail");
286 delete [] contentPtr;
287 return;
288 }
289 char *restPtr;
290 char *line = strtok_r(contentPtr, "\n", &restPtr);
291 do {
292 for (size_t i = 0; i < strlen(line); i++) {
293 if (line[i] == ':') {
294 line[i] = ' ';
295 }
296 }
297 std::string lineStr(line);
298 std::istringstream is(lineStr);
299 std::string name, value;
300 is >> name >> value;
301 result.insert(std::make_pair(name, value));
302
303 line = strtok_r(NULL, "\n", &restPtr);
304 } while (line);
305 if (restPtr) {
306 delete [] restPtr;
307 }
308 if (contentPtr) {
309 delete [] contentPtr;
310 }
311 return;
312 }
313
GetCurrentBuffer()314 int KernelInterface::GetCurrentBuffer()
315 {
316 std::map<std::string, std::string> result;
317 ReadZswapdPressureShow(result);
318 auto value = result.find(ZWAPD_PRESSURE_SHOW_BUFFER_SIZE);
319 if (value != result.end()) {
320 HILOGD("buffer_size=%{public}s MB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
321 return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()) * KB_PER_MB;
322 }
323 return MAX_BUFFER_KB;
324 }
325
KillOneProcessByPid(int pid)326 int KernelInterface::KillOneProcessByPid(int pid)
327 {
328 HILOGD("called! pid=%{public}d", pid);
329 struct ProcInfo procInfo;
330 int freedBuffer = 0;
331 procInfo.pid = pid;
332
333 if (!GetPidProcInfo(procInfo)) {
334 HILOGE("GetPidProcInfo fail !!!");
335 goto out;
336 }
337
338 if (procInfo.status == "D") {
339 HILOGE("Task %{public}s is at D status!", procInfo.name.c_str());
340 goto out;
341 }
342
343 if (kill(pid, SIGKILL)) {
344 HILOGE("Kill %{public}s errno=%{public}d!", procInfo.name.c_str(), errno);
345 }
346 HILOGE("%{public}s has been Killed ! (pid=%{public}d, freedSize=%{public}d KB)",
347 procInfo.name.c_str(), procInfo.pid, procInfo.size);
348
349 freedBuffer = procInfo.size;
350 out:
351 return freedBuffer;
352 }
353
GetAllProcPids(std::vector<unsigned int> & pids)354 bool KernelInterface::GetAllProcPids(std::vector<unsigned int> &pids)
355 {
356 pids.clear();
357 DIR *dir = opendir(ROOT_PROC_PATH.c_str());
358 if (dir == nullptr) {
359 HILOGE("dir %{public}s is not exist", ROOT_PROC_PATH.c_str());
360 return false;
361 }
362 struct dirent *ptr = nullptr;
363 while ((ptr = readdir(dir)) != nullptr) {
364 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
365 // current dir OR parent dir
366 continue;
367 } else if (ptr->d_type == DT_DIR) {
368 int pid = atoi(ptr->d_name);
369 if (pid > 0) {
370 pids.push_back((unsigned int)pid);
371 }
372 }
373 }
374 if (dir) {
375 closedir(dir);
376 }
377 HILOGD("there are %{public}zu pids under %{public}s", pids.size(), ROOT_PROC_PATH.c_str());
378 return true;
379 }
380
GetUidByPid(unsigned int pid,unsigned int & uid)381 bool KernelInterface::GetUidByPid(unsigned int pid, unsigned int& uid)
382 {
383 std::string path = JoinPath(ROOT_PROC_PATH, std::to_string(pid), "status");
384 std::string content;
385 if (!ReadFromFile(path, content)) {
386 HILOGE("read file failed. %{public}s", path.c_str());
387 return false;
388 }
389 content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
390 std::regex re(".*Uid:[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+).*");
391 std::smatch res;
392 if (!std::regex_match(content, res, re)) {
393 HILOGD("re not match. %{public}s", content.c_str());
394 return false;
395 }
396 try {
397 uid = (unsigned int)std::stoi(res.str(1)); // 1: Uid index
398 } catch (...) {
399 HILOGE("stoi(%{public}s) failed", res.str(1).c_str());
400 return false;
401 }
402 return true;
403 }
404
ReadSwapOutKBSinceKernelBoot(const std::string & path,const std::string & tagStr,unsigned long long & ret)405 bool KernelInterface::ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr,
406 unsigned long long &ret)
407 {
408 std::string contentStr;
409 if (!ReadFromFile(path, contentStr)) {
410 return false;
411 }
412 char *contentPtr = new char[contentStr.size() + 1];
413 if (contentPtr == nullptr) {
414 HILOGE("alloc buffer fail");
415 return false;
416 }
417 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
418 HILOGE("copy fail");
419 return false;
420 }
421 char *restPtr;
422 char *line = strtok_r(contentPtr, "\n", &restPtr);
423 do {
424 std::string lineStr(line);
425
426 size_t i = 0;
427 for (; i < strlen(line); i++) {
428 if (line[i] == ':') {
429 break;
430 }
431 }
432 if (i >= strlen(line) - 2) { // 2: no : in the line or : is at end of line
433 line = strtok_r(NULL, "\n", &restPtr);
434 continue;
435 }
436 std::string tag = lineStr.substr(0, i);
437 if (tag == tagStr) {
438 std::string value = lineStr.substr(i + 1);
439 std::istringstream iss(value);
440 std::string sizeStr, unitStr;
441 iss >> sizeStr >> unitStr;
442 try {
443 ret = std::strtoull(sizeStr.c_str(), nullptr, 10); // 10:Decimal
444 return true;
445 } catch (...) {
446 HILOGE("parse [%{public}s] to unsigned long long error!", sizeStr.c_str());
447 return false;
448 }
449 }
450
451 line = strtok_r(NULL, "\n", &restPtr);
452 } while (line);
453 if (restPtr) {
454 delete [] restPtr;
455 }
456 if (contentPtr) {
457 delete [] contentPtr;
458 }
459 return false;
460 }
461 } // namespace Memory
462 } // namespace OHOS
463