• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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