• 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 #include "memmgr_log.h"
18 
19 #include "directory_ex.h"
20 #include "file_ex.h"
21 
22 #include <securec.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 
27 #include <fstream>
28 #include <regex>
29 #include <sstream>
30 #include <csignal>
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 
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 
EchoToPath(const char * path,const char * content)46 bool KernelInterface::EchoToPath(const char* path, const char* content)
47 {
48     int fd = open(path, O_RDWR, FILE_MODE_666);
49     if (fd == -1) {
50         HILOGE("echo %{public}s > %{public}s failed: file is not open", content, path);
51         return false;
52     }
53     if (write(fd, content, strlen(content)) < 0) {
54         HILOGE("echo %{public}s > %{public}s failed: write failed", content, path);
55         close(fd);
56         return false;
57     }
58     close(fd);
59     HILOGI("echo %{public}s > %{public}s", content, path);
60     return true;
61 }
62 
IsFileExists(const std::string & path)63 bool KernelInterface::IsFileExists(const std::string& path)
64 {
65     if (path.empty()) {
66         return false;
67     }
68     struct stat st = {};
69     if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
70         return true;
71     }
72     return false;
73 }
74 
CreateFile(const std::string & path,const mode_t & mode)75 bool KernelInterface::CreateFile(const std::string& path, const mode_t& mode)
76 {
77     if (path.empty()) {
78         return false;
79     }
80     if (IsFileExists(path)) {
81         return true;
82     }
83 
84     std::ofstream fout(path);
85     if (!fout.is_open()) {
86         return false;
87     }
88     fout.flush();
89     fout.close();
90     if (chmod(path.c_str(), mode) != 0) {
91         return false;
92     }
93 
94     return true;
95 }
96 
CreateFile(const std::string & path)97 bool KernelInterface::CreateFile(const std::string& path)
98 {
99     return CreateFile(path, FILE_MODE_644);
100 }
101 
RemoveFile(const std::string & path)102 bool KernelInterface::RemoveFile(const std::string& path)
103 {
104     return OHOS::RemoveFile(path);
105 }
106 
WriteToFile(const std::string & path,const std::string & content,bool truncated)107 bool KernelInterface::WriteToFile(const std::string& path, const std::string& content, bool truncated)
108 {
109     int fd;
110     char actualPath[PATH_MAX + 1] = {0};
111     char* ptrRet = NULL;
112 
113     if (strlen(path.c_str()) == 0 || strlen(path.c_str()) > PATH_MAX) {
114         HILOGE("file path is invalid");
115         return false;
116     }
117     ptrRet = realpath(path.c_str(), actualPath);
118     if (!ptrRet) {
119         HILOGE("file path cannot be canonicalized");
120         return false;
121     }
122     HILOGD("path:%{public}s, actualPath:%{public}s", path.c_str(), actualPath);
123     fd = open(actualPath, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
124     if (fd == -1) {
125         HILOGE("echo %{public}s %{public}s %{public}s failed: file is not open",
126             content.c_str(), truncated ? ">" : ">>", path.c_str());
127         ptrRet = NULL;
128         return false;
129     }
130     if (write(fd, content.c_str(), strlen(content.c_str())) < 0) {
131         HILOGE("echo %{public}s %{public}s %{public}s failed: write failed",
132             content.c_str(), truncated ? ">" : ">>", path.c_str());
133         ptrRet = NULL;
134         close(fd);
135         return false;
136     }
137     ptrRet = NULL;
138     close(fd);
139     HILOGD("echo %{public}s %{public}s %{public}s", content.c_str(), truncated ? ">" : ">>", path.c_str());
140     return true;
141 }
142 
ReadFromFile(const std::string & path,std::string & content)143 bool KernelInterface::ReadFromFile(const std::string& path, std::string& content)
144 {
145     return OHOS::LoadStringFromFile(path, content);
146 }
147 
ReadLinesFromFile(const std::string & path,std::vector<std::string> & lines)148 bool KernelInterface::ReadLinesFromFile(const std::string& path, std::vector<std::string>& lines)
149 {
150     if (!IsFileExists(path)) {
151         HILOGE("no such file: %{public}s", path.c_str());
152         return false;
153     }
154     std::string line;
155     std::ifstream inf(path, std::ifstream::in);
156     if (!inf) {
157         HILOGE("ifstream(%{public}s) failed", path.c_str());
158         return false;
159     }
160     lines.clear();
161     while (!inf.eof()) {
162         getline(inf, line);
163         lines.push_back(line);
164     }
165     inf.close();
166     return true;
167 }
168 
IsDirExists(const std::string & path)169 bool KernelInterface::IsDirExists(const std::string& path)
170 {
171     if (path.empty()) {
172         return false;
173     }
174     struct stat st = {};
175     if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
176         return true;
177     }
178     return false;
179 }
180 
IsExists(const std::string & path)181 bool KernelInterface::IsExists(const std::string& path)
182 {
183     return OHOS::FileExists(path);
184 }
185 
IsEmptyDir(const std::string & path)186 bool KernelInterface::IsEmptyDir(const std::string& path)
187 {
188     return OHOS::IsEmptyFolder(path);
189 }
190 
CreateDir(const std::string & path)191 bool KernelInterface::CreateDir(const std::string& path)
192 {
193     return OHOS::ForceCreateDirectory(path); // default mode 755
194 }
195 
RemoveDirRecursively(const std::string & path)196 bool KernelInterface::RemoveDirRecursively(const std::string& path)
197 {
198     return OHOS::ForceRemoveDirectory(path) || (remove(path.c_str()) == 0);
199 }
200 
RmDelimiter(const std::string & path)201 std::string KernelInterface::RmDelimiter(const std::string& path)
202 {
203     return OHOS::ExcludeTrailingPathDelimiter(path);
204 }
205 
AddDelimiter(const std::string & path)206 std::string KernelInterface::AddDelimiter(const std::string& path)
207 {
208     return OHOS::IncludeTrailingPathDelimiter(path);
209 }
210 
JoinPath(const std::string & prefixPath,const std::string & subPath)211 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& subPath)
212 {
213     return AddDelimiter(prefixPath) + subPath;
214 }
215 
JoinPath(const std::string & prefixPath,const std::string & midPath,const std::string & subPath)216 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& midPath,
217                                       const std::string& subPath)
218 {
219     return JoinPath(JoinPath(prefixPath, midPath), subPath);
220 }
221 
GetPidProcInfo(struct ProcInfo & procInfo)222 bool KernelInterface::GetPidProcInfo(struct ProcInfo &procInfo)
223 {
224     HILOGD("called!");
225 
226     std::string statPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/stat");
227 
228     // format like:
229     // 1 (init) S 0 0 0 0 -1 4210944 1 ...
230     std::string stat, statm, statPid, vss, rss;
231     if (!ReadFromFile(statPath, stat)) {
232         HILOGD("stat file error!");
233         return false;
234     }
235     std::istringstream isStat(stat);
236     isStat >> statPid >> procInfo.name >> procInfo.status;
237 
238     if (statPid != std::to_string(procInfo.pid)) {
239         HILOGD("pid error!");
240         return false;
241     }
242 
243     std::string statmPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/statm");
244     // format like:
245     // 640 472 369 38 0 115 0
246     if (!ReadFromFile(statmPath, statm)) {
247         HILOGD("statm file error!");
248         return false;
249     }
250     std::istringstream isStatm(statm);
251     isStatm >> vss >> rss; // pages
252 
253     procInfo.size = atoi(rss.c_str()) * PAGE_TO_KB;
254     HILOGI("GetProcInfo success: name is %{public}s, status is %{public}s, size = %{public}d KB",
255            procInfo.name.c_str(), procInfo.status.c_str(), procInfo.size);
256     return true;
257 }
258 
GetProcNameByPid(int pid,std::string & name)259 bool KernelInterface::GetProcNameByPid(int pid, std::string &name)
260 {
261     std::string statusPath = JoinPath("/proc/", std::to_string(pid), "/status");
262     std::string statusContent, nameTag;
263     if (!ReadFromFile(statusPath, statusContent)) {
264         HILOGE("status file [%{public}s] error!", statusPath.c_str());
265         return false;
266     }
267     std::istringstream statusStream(statusContent);
268     statusStream >> nameTag >> name;
269     return true;
270 }
271 
ReadZswapdPressureShow(std::map<std::string,std::string> & result)272 void KernelInterface::ReadZswapdPressureShow(std::map<std::string, std::string>& result)
273 {
274     std::string contentStr;
275     if (!ReadFromFile(ZWAPD_PRESSURE_SHOW_PATH, contentStr)) {
276         HILOGE("read %{public}s faild, content=[%{public}s]", ZWAPD_PRESSURE_SHOW_PATH.c_str(), contentStr.c_str());
277         return;
278     }
279     char *contentPtr = new char[contentStr.size() + 1];
280     if (contentPtr == nullptr) {
281         HILOGE("alloc buffer fail");
282         return;
283     }
284     if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
285         HILOGE("copy fail");
286         delete [] contentPtr;
287         return;
288     }
289     char *restPtr;
290     char *line = strtok_r(contentPtr, "\n", &restPtr);
291     do {
292         for (size_t i = 0; i < strlen(line); i++) {
293             if (line[i] == ':') {
294                 line[i] = ' ';
295             }
296         }
297         std::string lineStr(line);
298         std::istringstream is(lineStr);
299         std::string name, value;
300         is >> name >> value;
301         result.insert(std::make_pair(name, value));
302 
303         line = strtok_r(NULL, "\n", &restPtr);
304     } while (line);
305     if (restPtr) {
306         delete [] restPtr;
307     }
308     if (contentPtr) {
309         delete [] contentPtr;
310     }
311     return;
312 }
313 
GetCurrentBuffer()314 int KernelInterface::GetCurrentBuffer()
315 {
316     std::map<std::string, std::string> result;
317     ReadZswapdPressureShow(result);
318     auto value = result.find(ZWAPD_PRESSURE_SHOW_BUFFER_SIZE);
319     if (value != result.end()) {
320         HILOGD("buffer_size=%{public}s MB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
321         return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()) * KB_PER_MB;
322     }
323     return MAX_BUFFER_KB;
324 }
325 
KillOneProcessByPid(int pid)326 int KernelInterface::KillOneProcessByPid(int pid)
327 {
328     HILOGD("called! pid=%{public}d", pid);
329     struct ProcInfo procInfo;
330     int freedBuffer = 0;
331     procInfo.pid = pid;
332 
333     if (!GetPidProcInfo(procInfo)) {
334         HILOGE("GetPidProcInfo fail !!!");
335         goto out;
336     }
337 
338     if (procInfo.status == "D") {
339         HILOGE("Task %{public}s is at D status!", procInfo.name.c_str());
340         goto out;
341     }
342 
343     if (kill(pid, SIGKILL)) {
344         HILOGE("Kill %{public}s errno=%{public}d!", procInfo.name.c_str(), errno);
345     }
346     HILOGE("%{public}s has been Killed ! (pid=%{public}d, freedSize=%{public}d KB)",
347         procInfo.name.c_str(), procInfo.pid, procInfo.size);
348 
349     freedBuffer = procInfo.size;
350 out:
351     return freedBuffer;
352 }
353 
GetAllProcPids(std::vector<unsigned int> & pids)354 bool KernelInterface::GetAllProcPids(std::vector<unsigned int> &pids)
355 {
356     pids.clear();
357     DIR *dir = opendir(ROOT_PROC_PATH.c_str());
358     if (dir == nullptr) {
359         HILOGE("dir %{public}s is not exist", ROOT_PROC_PATH.c_str());
360         return false;
361     }
362     struct dirent *ptr = nullptr;
363     while ((ptr = readdir(dir)) != nullptr) {
364         if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
365             // current dir OR parent dir
366             continue;
367         } else if (ptr->d_type == DT_DIR) {
368             int pid = atoi(ptr->d_name);
369             if (pid > 0) {
370                 pids.push_back((unsigned int)pid);
371             }
372         }
373     }
374     if (dir) {
375         closedir(dir);
376     }
377     HILOGD("there are %{public}zu pids under %{public}s", pids.size(), ROOT_PROC_PATH.c_str());
378     return true;
379 }
380 
GetUidByPid(unsigned int pid,unsigned int & uid)381 bool KernelInterface::GetUidByPid(unsigned int pid, unsigned int& uid)
382 {
383     std::string path = JoinPath(ROOT_PROC_PATH, std::to_string(pid), "status");
384     std::string content;
385     if (!ReadFromFile(path, content)) {
386         HILOGE("read file failed. %{public}s", path.c_str());
387         return false;
388     }
389     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
390     std::regex re(".*Uid:[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+).*");
391     std::smatch res;
392     if (!std::regex_match(content, res, re)) {
393         HILOGD("re not match. %{public}s", content.c_str());
394         return false;
395     }
396     try {
397         uid = (unsigned int)std::stoi(res.str(1)); // 1: Uid index
398     } catch (...) {
399         HILOGE("stoi(%{public}s) failed", res.str(1).c_str());
400         return false;
401     }
402     return true;
403 }
404 
ReadSwapOutKBSinceKernelBoot(const std::string & path,const std::string & tagStr,unsigned long long & ret)405 bool KernelInterface::ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr,
406     unsigned long long &ret)
407 {
408     std::string contentStr;
409     if (!ReadFromFile(path, contentStr)) {
410         return false;
411     }
412     char *contentPtr = new char[contentStr.size() + 1];
413     if (contentPtr == nullptr) {
414         HILOGE("alloc buffer fail");
415         return false;
416     }
417     if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
418         HILOGE("copy fail");
419         return false;
420     }
421     char *restPtr;
422     char *line = strtok_r(contentPtr, "\n", &restPtr);
423     do {
424         std::string lineStr(line);
425 
426         size_t i = 0;
427         for (; i < strlen(line); i++) {
428             if (line[i] == ':') {
429                 break;
430             }
431         }
432         if (i >= strlen(line) - 2) { // 2: no : in the line or : is at end of line
433             line = strtok_r(NULL, "\n", &restPtr);
434             continue;
435         }
436         std::string tag = lineStr.substr(0, i);
437         if (tag == tagStr) {
438             std::string value = lineStr.substr(i + 1);
439             std::istringstream iss(value);
440             std::string sizeStr, unitStr;
441             iss >> sizeStr >> unitStr;
442             try {
443                 ret = std::strtoull(sizeStr.c_str(), nullptr, 10); // 10:Decimal
444                 return true;
445             } catch (...) {
446                 HILOGE("parse [%{public}s] to unsigned long long error!", sizeStr.c_str());
447                 return false;
448             }
449         }
450 
451         line = strtok_r(NULL, "\n", &restPtr);
452     } while (line);
453     if (restPtr) {
454         delete [] restPtr;
455     }
456     if (contentPtr) {
457         delete [] contentPtr;
458     }
459     return false;
460 }
461 } // namespace Memory
462 } // namespace OHOS
463