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