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