• 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 
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