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
18 #include <csignal>
19 #include <dirent.h>
20 #include <fstream>
21 #include <regex>
22 #include <securec.h>
23 #include <sstream>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 #include "directory_ex.h"
28 #include "file_ex.h"
29 #include "memmgr_log.h"
30
31 namespace OHOS {
32 namespace Memory {
33 namespace {
34 const std::string TAG = "KernelInterface";
35 }
36
37 IMPLEMENT_SINGLE_INSTANCE(KernelInterface);
38
39 const std::string KernelInterface::ROOT_PROC_PATH = "/proc";
40 const std::string KernelInterface::MEMCG_BASE_PATH = "/dev/memcg";
41 const std::string KernelInterface::FILE_MEMCG_PROCS = "cgroup.procs";
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 const std::string KernelInterface::MEMINFO_PATH = "/proc/meminfo";
46 const std::string KernelInterface::FILE_PROC_STATUS = "status";
47 const std::string KernelInterface::TOTAL_MEMORY = "MemTotal";
48
EchoToPath(const char * path,const char * content)49 bool KernelInterface::EchoToPath(const char* path, const char* content)
50 {
51 int fd = open(path, O_WRONLY);
52 if (fd == -1) {
53 HILOGE("echo %{public}s > %{public}s failed: file is not open", content, path);
54 return false;
55 }
56 if (write(fd, content, strlen(content)) < 0) {
57 HILOGE("echo %{public}s > %{public}s failed: write failed", content, path);
58 close(fd);
59 return false;
60 }
61 close(fd);
62 HILOGI("echo %{public}s > %{public}s", content, path);
63 return true;
64 }
65
IsFileExists(const std::string & path)66 bool KernelInterface::IsFileExists(const std::string& path)
67 {
68 if (path.empty()) {
69 return false;
70 }
71 struct stat st = {};
72 if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
73 return true;
74 }
75 return false;
76 }
77
CreateFile(const std::string & path,const mode_t & mode)78 bool KernelInterface::CreateFile(const std::string& path, const mode_t& mode)
79 {
80 if (path.empty()) {
81 return false;
82 }
83 if (IsFileExists(path)) {
84 return true;
85 }
86
87 std::ofstream fout(path);
88 if (!fout.is_open()) {
89 return false;
90 }
91 fout.flush();
92 fout.close();
93 if (chmod(path.c_str(), mode) != 0) {
94 return false;
95 }
96
97 return true;
98 }
99
CreateFile(const std::string & path)100 bool KernelInterface::CreateFile(const std::string& path)
101 {
102 return CreateFile(path, FILE_MODE_644);
103 }
104
RemoveFile(const std::string & path)105 bool KernelInterface::RemoveFile(const std::string& path)
106 {
107 return OHOS::RemoveFile(path);
108 }
109
WriteToFile(const std::string & path,const std::string & content,bool truncated)110 bool KernelInterface::WriteToFile(const std::string& path, const std::string& content, bool truncated)
111 {
112 int fd;
113 char actualPath[PATH_MAX + 1] = {0};
114 char* ptrRet = NULL;
115
116 if (strlen(path.c_str()) == 0 || strlen(path.c_str()) > PATH_MAX) {
117 HILOGE("file path is invalid");
118 return false;
119 }
120 ptrRet = realpath(path.c_str(), actualPath);
121 if (!ptrRet) {
122 HILOGE("file path cannot be canonicalized");
123 return false;
124 }
125 HILOGD("path:%{public}s, actualPath:%{public}s", path.c_str(), actualPath);
126 fd = open(actualPath, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
127 if (fd == -1) {
128 HILOGE("echo %{public}s %{public}s %{public}s failed: file is not open",
129 content.c_str(), truncated ? ">" : ">>", path.c_str());
130 ptrRet = NULL;
131 return false;
132 }
133 if (write(fd, content.c_str(), strlen(content.c_str())) < 0) {
134 HILOGE("echo %{public}s %{public}s %{public}s failed: write failed",
135 content.c_str(), truncated ? ">" : ">>", path.c_str());
136 ptrRet = NULL;
137 close(fd);
138 return false;
139 }
140 ptrRet = NULL;
141 close(fd);
142 HILOGD("echo %{public}s %{public}s %{public}s", content.c_str(), truncated ? ">" : ">>", path.c_str());
143 return true;
144 }
145
ReadFromFile(const std::string & path,std::string & content)146 bool KernelInterface::ReadFromFile(const std::string& path, std::string& content)
147 {
148 return OHOS::LoadStringFromFile(path, content);
149 }
150
ReadLinesFromFile(const std::string & path,std::vector<std::string> & lines)151 bool KernelInterface::ReadLinesFromFile(const std::string& path, std::vector<std::string>& lines)
152 {
153 if (!IsFileExists(path)) {
154 HILOGE("no such file: %{public}s", path.c_str());
155 return false;
156 }
157 std::string line;
158 std::ifstream inf(path, std::ifstream::in);
159 if (!inf) {
160 HILOGE("ifstream(%{public}s) failed", path.c_str());
161 return false;
162 }
163 lines.clear();
164 while (!inf.eof()) {
165 getline(inf, line);
166 lines.push_back(line);
167 }
168 inf.close();
169 return true;
170 }
171
IsDirExists(const std::string & path)172 bool KernelInterface::IsDirExists(const std::string& path)
173 {
174 if (path.empty()) {
175 return false;
176 }
177 struct stat st = {};
178 if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
179 return true;
180 }
181 return false;
182 }
183
GetSystemCurTime()184 int64_t KernelInterface::GetSystemCurTime()
185 {
186 return std::chrono::duration_cast<std::chrono::seconds>
187 (std::chrono::system_clock::now().time_since_epoch()).count();
188 }
189
IsExists(const std::string & path)190 bool KernelInterface::IsExists(const std::string& path)
191 {
192 return OHOS::FileExists(path);
193 }
194
IsEmptyDir(const std::string & path)195 bool KernelInterface::IsEmptyDir(const std::string& path)
196 {
197 return OHOS::IsEmptyFolder(path);
198 }
199
CreateDir(const std::string & path)200 bool KernelInterface::CreateDir(const std::string& path)
201 {
202 return OHOS::ForceCreateDirectory(path); // default mode 755
203 }
204
RemoveDirRecursively(const std::string & path)205 bool KernelInterface::RemoveDirRecursively(const std::string& path)
206 {
207 return OHOS::ForceRemoveDirectory(path) || (remove(path.c_str()) == 0);
208 }
209
RmDelimiter(const std::string & path)210 std::string KernelInterface::RmDelimiter(const std::string& path)
211 {
212 if (path.empty()) {
213 return path;
214 }
215 return OHOS::ExcludeTrailingPathDelimiter(path);
216 }
217
AddDelimiter(const std::string & path)218 std::string KernelInterface::AddDelimiter(const std::string& path)
219 {
220 if (path.empty()) {
221 return path;
222 }
223 return OHOS::IncludeTrailingPathDelimiter(path);
224 }
225
JoinPath(const std::string & prefixPath,const std::string & subPath)226 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& subPath)
227 {
228 return AddDelimiter(prefixPath) + subPath;
229 }
230
JoinPath(const std::string & prefixPath,const std::string & midPath,const std::string & subPath)231 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& midPath,
232 const std::string& subPath)
233 {
234 return JoinPath(JoinPath(prefixPath, midPath), subPath);
235 }
236
GetPidProcInfo(struct ProcInfo & procInfo)237 bool KernelInterface::GetPidProcInfo(struct ProcInfo &procInfo)
238 {
239 HILOGD("called!");
240
241 std::string statPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/stat");
242
243 // format like:
244 // 1 (init) S 0 0 0 0 -1 4210944 1 ...
245 std::string stat, statm, statPid, vss, rss;
246 if (!ReadFromFile(statPath, stat)) {
247 HILOGD("stat file error!");
248 return false;
249 }
250 std::istringstream isStat(stat);
251 isStat >> statPid >> procInfo.name >> procInfo.status;
252
253 if (statPid != std::to_string(procInfo.pid)) {
254 HILOGD("pid error!");
255 return false;
256 }
257
258 std::string statmPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/statm");
259 // format like:
260 // 640 472 369 38 0 115 0
261 if (!ReadFromFile(statmPath, statm)) {
262 HILOGD("statm file error!");
263 return false;
264 }
265 std::istringstream isStatm(statm);
266 isStatm >> vss >> rss; // pages
267
268 procInfo.size = atoi(rss.c_str()) * PAGE_TO_KB;
269 HILOGI("GetProcInfo success: name is %{public}s, status is %{public}s, size = %{public}d KB",
270 procInfo.name.c_str(), procInfo.status.c_str(), procInfo.size);
271 return true;
272 }
273
GetProcNameByPid(int pid,std::string & name)274 bool KernelInterface::GetProcNameByPid(int pid, std::string &name)
275 {
276 std::string statusPath = JoinPath("/proc/", std::to_string(pid), "/status");
277 std::string statusContent, nameTag;
278 if (!ReadFromFile(statusPath, statusContent)) {
279 HILOGE("status file [%{public}s] error!", statusPath.c_str());
280 return false;
281 }
282 std::istringstream statusStream(statusContent);
283 statusStream >> nameTag >> name;
284 return true;
285 }
286
ReadZswapdPressureShow(std::map<std::string,std::string> & result)287 void KernelInterface::ReadZswapdPressureShow(std::map<std::string, std::string>& result)
288 {
289 std::string contentStr;
290 if (!ReadFromFile(ZWAPD_PRESSURE_SHOW_PATH, contentStr)) {
291 HILOGE("read %{public}s faild, content=[%{public}s]", ZWAPD_PRESSURE_SHOW_PATH.c_str(), contentStr.c_str());
292 return;
293 }
294 char *contentPtr = new char[contentStr.size() + 1];
295 if (contentPtr == nullptr) {
296 HILOGE("alloc buffer fail");
297 return;
298 }
299 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
300 HILOGE("copy fail");
301 delete [] contentPtr;
302 return;
303 }
304 char *restPtr;
305 char *line = strtok_r(contentPtr, "\n", &restPtr);
306 do {
307 for (size_t i = 0; i < strlen(line); i++) {
308 if (line[i] == ':') {
309 line[i] = ' ';
310 }
311 }
312 std::string lineStr(line);
313 std::istringstream is(lineStr);
314 std::string name, value;
315 is >> name >> value;
316 result.insert(std::make_pair(name, value));
317
318 line = strtok_r(NULL, "\n", &restPtr);
319 } while (line);
320 if (restPtr) {
321 delete [] restPtr;
322 }
323 if (contentPtr) {
324 delete [] contentPtr;
325 }
326 return;
327 }
328
GetCurrentBuffer()329 int KernelInterface::GetCurrentBuffer()
330 {
331 std::map<std::string, std::string> result;
332 ReadZswapdPressureShow(result);
333 auto value = result.find(ZWAPD_PRESSURE_SHOW_BUFFER_SIZE);
334 if (value != result.end()) {
335 HILOGD("buffer_size=%{public}s MB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
336 return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()) * KB_PER_MB;
337 }
338 return MAX_BUFFER_KB;
339 }
340
KillOneProcessByPid(int pid)341 int KernelInterface::KillOneProcessByPid(int pid)
342 {
343 HILOGD("called! pid=%{public}d", pid);
344 struct ProcInfo procInfo;
345 int freedBuffer = 0;
346 procInfo.pid = pid;
347
348 if (!GetPidProcInfo(procInfo)) {
349 HILOGE("GetPidProcInfo fail !!!");
350 goto out;
351 }
352
353 if (procInfo.status == "D") {
354 HILOGE("Task %{public}s is at D status!", procInfo.name.c_str());
355 goto out;
356 }
357
358 if (kill(pid, SIGKILL)) {
359 HILOGE("Kill %{public}s errno=%{public}d!", procInfo.name.c_str(), errno);
360 }
361 HILOGE("%{public}s has been Killed ! (pid=%{public}d, freedSize=%{public}d KB)",
362 procInfo.name.c_str(), procInfo.pid, procInfo.size);
363
364 freedBuffer = procInfo.size;
365 out:
366 return freedBuffer;
367 }
368
GetAllProcPids(std::vector<unsigned int> & pids)369 bool KernelInterface::GetAllProcPids(std::vector<unsigned int> &pids)
370 {
371 pids.clear();
372 DIR *dir = opendir(ROOT_PROC_PATH.c_str());
373 if (dir == nullptr) {
374 HILOGE("dir %{public}s is not exist", ROOT_PROC_PATH.c_str());
375 return false;
376 }
377 struct dirent *ptr = nullptr;
378 while ((ptr = readdir(dir)) != nullptr) {
379 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
380 // current dir OR parent dir
381 continue;
382 } else if (ptr->d_type == DT_DIR) {
383 int pid = atoi(ptr->d_name);
384 if (pid > 0) {
385 pids.push_back((unsigned int)pid);
386 }
387 }
388 }
389 if (dir) {
390 closedir(dir);
391 }
392 HILOGD("there are %{public}zu pids under %{public}s", pids.size(), ROOT_PROC_PATH.c_str());
393 return true;
394 }
395
GetUidByPid(unsigned int pid,unsigned int & uid)396 bool KernelInterface::GetUidByPid(unsigned int pid, unsigned int& uid)
397 {
398 std::string path = JoinPath(ROOT_PROC_PATH, std::to_string(pid), "status");
399 std::string content;
400 if (!ReadFromFile(path, content)) {
401 HILOGE("read file failed. %{public}s", path.c_str());
402 return false;
403 }
404 content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
405 std::regex re(".*Uid:[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+).*");
406 std::smatch res;
407 if (!std::regex_match(content, res, re)) {
408 HILOGD("re not match. %{public}s", content.c_str());
409 return false;
410 }
411 try {
412 uid = (unsigned int)std::stoi(res.str(1)); // 1: Uid index
413 } catch (...) {
414 HILOGE("stoi(%{public}s) failed", res.str(1).c_str());
415 return false;
416 }
417 return true;
418 }
419
DeleteCharArrayIfNotNull(char * charArray)420 void DeleteCharArrayIfNotNull(char * charArray)
421 {
422 if (charArray) {
423 delete [] charArray;
424 charArray = nullptr;
425 }
426 }
427
ReadSwapOutKBSinceKernelBoot(const std::string & path,const std::string & tagStr,unsigned long long & ret)428 bool KernelInterface::ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr,
429 unsigned long long &ret)
430 {
431 std::string contentStr;
432 if (!ReadFromFile(path, contentStr)) {
433 return false;
434 }
435 char *contentPtr = new char[contentStr.size() + 1];
436 if (contentPtr == nullptr) {
437 HILOGE("alloc buffer fail");
438 return false;
439 }
440 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
441 HILOGE("copy fail");
442 DeleteCharArrayIfNotNull(contentPtr);
443 return false;
444 }
445 bool success = false;
446 char *restPtr;
447 char *line = strtok_r(contentPtr, "\n", &restPtr);
448 do {
449 std::string lineStr(line);
450
451 size_t i = 0;
452 for (; i < strlen(line); i++) {
453 if (line[i] == ':') {
454 break;
455 }
456 }
457 if (i >= strlen(line) - 2) { // 2: no : in the line or : is at end of line
458 line = strtok_r(NULL, "\n", &restPtr);
459 continue;
460 }
461 std::string tag = lineStr.substr(0, i);
462 if (tag == tagStr) {
463 std::string value = lineStr.substr(i + 1);
464 std::istringstream iss(value);
465 std::string sizeStr, unitStr;
466 iss >> sizeStr >> unitStr;
467 try {
468 ret = std::strtoull(sizeStr.c_str(), nullptr, 10); // 10:Decimal
469 success = true;
470 } catch (...) {
471 HILOGE("parse [%{public}s] to unsigned long long error!", sizeStr.c_str());
472 }
473 break;
474 }
475
476 line = strtok_r(NULL, "\n", &restPtr);
477 } while (line);
478 DeleteCharArrayIfNotNull(contentPtr);
479 return success;
480 }
481
ParseMeminfo(const std::string & contentStr,const std::string & itemName)482 int KernelInterface::ParseMeminfo(const std::string &contentStr, const std::string &itemName)
483 {
484 char *contentPtr = new (std::nothrow) char[contentStr.size() + 1];
485 if (contentPtr == nullptr) {
486 HILOGE("alloc buffer fail");
487 return -1;
488 }
489 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
490 HILOGE("copy fail");
491 delete [] contentPtr;
492 return -1;
493 }
494 char *restPtr = nullptr;
495 char *line = strtok_r(contentPtr, "\n", &restPtr);
496 std::string name, value;
497 bool findTotalMem = false;
498 do {
499 for (size_t i = 0; i < strlen(line); i++) {
500 if (line[i] == ':') {
501 line[i] = ' ';
502 }
503 }
504 std::string lineStr(line);
505 std::istringstream is(lineStr);
506
507 is >> name >> value;
508 if (name == itemName) {
509 findTotalMem = true;
510 break;
511 }
512 line = strtok_r(NULL, "\n", &restPtr);
513 } while (line);
514 if (contentPtr) {
515 delete [] contentPtr;
516 }
517
518 if (findTotalMem == false) {
519 return -1;
520 }
521 std::string valueTemp = "";
522 for (auto c : value) {
523 if (c >= '0' && c <= '9') {
524 valueTemp = valueTemp + c;
525 }
526 }
527 return atoi(valueTemp.c_str());
528 }
529
GetTotalBuffer()530 int KernelInterface::GetTotalBuffer()
531 {
532 if (totalBuffer_ >= 0) {
533 return totalBuffer_;
534 }
535
536 std::string contentStr;
537 if (!ReadFromFile(MEMINFO_PATH, contentStr)) {
538 HILOGE("read %{public}s faild, content=[%{public}s]", MEMINFO_PATH.c_str(), contentStr.c_str());
539 return -1;
540 }
541 totalBuffer_ = ParseMeminfo(contentStr, TOTAL_MEMORY);
542 return totalBuffer_;
543 }
544
GetMemcgPids(const std::string & memcgPath,std::vector<int> & memcgPids)545 bool KernelInterface::GetMemcgPids(const std::string &memcgPath, std::vector<int> &memcgPids)
546 {
547 std::string path = JoinPath(memcgPath, FILE_MEMCG_PROCS);
548 std::vector<std::string> strLines;
549 if (!ReadLinesFromFile(path, strLines)) {
550 HILOGE("read file and split to lines failed : %{public}s", path.c_str());
551 return false;
552 }
553
554 memcgPids.clear();
555 int pid;
556 for (auto &it : strLines) {
557 try {
558 pid = stoi(it);
559 } catch (...) {
560 continue;
561 }
562 memcgPids.emplace_back(pid);
563 }
564 HILOGD("there are %{public}zu pids in %{public}s", memcgPids.size(), path.c_str());
565 return true;
566 }
567
GetAllUserIds(std::vector<int> & userIds)568 bool KernelInterface::GetAllUserIds(std::vector<int> &userIds)
569 {
570 userIds.clear();
571 DIR *dir = opendir(MEMCG_BASE_PATH.c_str());
572 if (dir == nullptr) {
573 HILOGE("dir %{public}s is not exist", MEMCG_BASE_PATH.c_str());
574 return false;
575 }
576 struct dirent *ptr = nullptr;
577 while ((ptr = readdir(dir)) != nullptr) {
578 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
579 // current dir OR parent dir
580 continue;
581 } else if (ptr->d_type == DT_DIR) {
582 int userId = atoi(ptr->d_name);
583 if (userId > 0) {
584 userIds.push_back(userId);
585 }
586 }
587 }
588 if (dir) {
589 closedir(dir);
590 }
591 HILOGD("there are %{public}zu userIds under %{public}s", userIds.size(), MEMCG_BASE_PATH.c_str());
592 return true;
593 }
594
SplitOneLineByDelim(const std::string & input,const char delimiter,std::vector<std::string> & res)595 void KernelInterface::SplitOneLineByDelim(const std::string &input, const char delimiter,
596 std::vector<std::string> &res)
597 {
598 std::stringstream ss(input);
599 std::string temp;
600 while (getline(ss, temp, delimiter)) {
601 if (!temp.empty()) {
602 res.emplace_back(temp);
603 }
604 }
605 }
606
SplitOneLineByBlank(const std::string & input,std::vector<std::string> & res)607 void KernelInterface::SplitOneLineByBlank(const std::string &input, std::vector<std::string> &res)
608 {
609 std::stringstream ss(input);
610 std::string temp;
611 while (ss >> temp) {
612 if (!temp.empty()) {
613 res.emplace_back(temp);
614 }
615 }
616 }
617 } // namespace Memory
618 } // namespace OHOS
619