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 #include "fault_detector_util.h"
16 
17 #include <cstdio>
18 #include <ctime>
19 #include <fstream>
20 #include <iostream>
21 #include <list>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25 
26 #include <fcntl.h>
27 #include <parameters.h>
28 #include <securec.h>
29 #include <sys/stat.h>
30 
31 #include "bundle_mgr_client.h"
32 #include "dirent.h"
33 #include "hiview_logger.h"
34 
35 
36 namespace OHOS {
37 namespace HiviewDFX {
38 DEFINE_LOG_TAG("FaultLeakDetectorUtil");
39 
40 using namespace OHOS::AppExecFwk;
41 using std::string;
42 using std::vector;
43 using std::stoi;
44 using std::to_string;
45 using std::ifstream;
46 using std::istringstream;
47 using std::stoull;
48 using std::getline;
49 using std::list;
50 
51 namespace {
52 const string MEMORY_LEAK_ENABLE_PROPERTY = "hiview.memleakcheck";
53 const string MEMORY_LEAK_TEST_PROPERTY = "hiview.memleak.test";
54 constexpr uint32_t KBYTE_PER_BYTE = 1024;
55 constexpr uint32_t PID_KTHREADD = 2;
56 constexpr int HAS_LEN = 128;
57 constexpr int MIN_APP_USERID = 10000;
58 
59 // index to split /proc/pid/stat info begin ")" with " "
60 enum StatIndex {
61     MIN_INDEX = 0,
62     PPID_INDEX,
63     GROUP_INDEX, // GRUOP_INDEX
64     RUN_TIME_INDEX = 11, // the time of process run from start
65     START_TIME_INDEX = 19,
66     VSS_INDEX = 20,
67     RSS_INDEX = 21,
68     MAX_INDEX
69 };
70 }
71 
IsMemLeakEnable()72 bool FaultDetectorUtil::IsMemLeakEnable()
73 {
74     bool flag = true;
75     string value = system::GetParameter(MEMORY_LEAK_ENABLE_PROPERTY, "");
76     if (value == "disable" || value == "false") {
77         HIVIEW_LOGI("disable memory leak function");
78         flag = false;
79     }
80     return flag;
81 }
82 
IsMemTestEnable()83 bool FaultDetectorUtil::IsMemTestEnable()
84 {
85     bool testEnable = false;
86     string value = system::GetParameter(MEMORY_LEAK_TEST_PROPERTY, "");
87     if (value == "enable" || value == "true") {
88         HIVIEW_LOGI("enable memory leak test");
89         testEnable = true;
90     }
91     return testEnable;
92 }
93 
GetIsHmKernel()94 bool FaultDetectorUtil::GetIsHmKernel()
95 {
96     static int isHmKernel = -1;
97     if (isHmKernel == -1) {
98         string meminfoPath = "/proc/memview";
99         auto fd = open(meminfoPath.c_str(), O_RDONLY);
100         if (fd < 0) {
101             isHmKernel = 0;
102             HIVIEW_LOGE("open %{public}s failed, may be linux kernel, errno is %{public}d", meminfoPath.c_str(), errno);
103             return false;
104         }
105         close(fd);
106         isHmKernel = 1;
107     }
108     return isHmKernel == 1;
109 }
110 
GetMemInfoPath()111 string FaultDetectorUtil::GetMemInfoPath()
112 {
113     return GetIsHmKernel() ? "/proc/memview" : "proc/meminfo";
114 }
115 
Split(const string & str,char delim)116 vector<string> FaultDetectorUtil::Split(const string &str, char delim)
117 {
118     istringstream ss(str);
119     vector<string> elems;
120     for (string item; getline(ss, item, delim);) {
121         if (item.empty()) {
122             continue;
123         }
124         elems.push_back(item);
125     }
126     return elems;
127 }
128 
IsDirectory(const string & path)129 bool FaultDetectorUtil::IsDirectory(const string &path)
130 {
131     struct stat statBuffer;
132     if (stat(path.c_str(), &statBuffer) == 0 && S_ISDIR(statBuffer.st_mode)) {
133         return true;
134     }
135     return false;
136 }
137 
GetSubDir(const string & path,bool digit)138 vector<string> FaultDetectorUtil::GetSubDir(const string &path, bool digit)
139 {
140     vector<string> subDirs;
141     auto dir = opendir(path.c_str());
142     if (dir == nullptr) {
143         HIVIEW_LOGE("failed to open dir : %{public}s, errno: %{public}d", path.c_str(), errno);
144         return subDirs;
145     }
146     for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
147         string childNode = ent->d_name;
148         if (childNode == "." || childNode == "..") {
149             continue;
150         }
151         if (digit && !isdigit(childNode[0])) {
152             continue;
153         }
154         if (!IsDirectory(path + "/" + childNode)) {
155             continue; // skip directory
156         }
157         subDirs.push_back(childNode);
158     }
159     closedir(dir);
160     return subDirs;
161 }
162 
GetSubFile(const string & path,bool digit)163 vector<string> FaultDetectorUtil::GetSubFile(const string &path, bool digit)
164 {
165     vector<string> subFiles;
166     auto dir = opendir(path.c_str());
167     if (dir == nullptr) {
168         HIVIEW_LOGE("failed to open dir: %{public}s, errno: %{public}d", path.c_str(), errno);
169         return subFiles;
170     }
171     for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
172         string childNode = ent->d_name;
173         if (childNode == "." || childNode == "..") {
174             continue;
175         }
176         if (digit && !isdigit(childNode[0])) {
177             continue;
178         }
179         if (IsDirectory(path + "/" + childNode)) {
180             continue; // skip directory
181         }
182         subFiles.push_back(childNode);
183     }
184     closedir(dir);
185     return subFiles;
186 }
187 
GetAllPids()188 vector<int32_t> FaultDetectorUtil::GetAllPids()
189 {
190     string path = "/proc";
191     vector<string> allPids = GetSubDir(path, true);
192     vector<int32_t> pids;
193     for (const auto &pid : allPids) {
194         if (!isdigit(pid[0])) {
195             continue;
196         }
197         pids.push_back(stoi(pid));
198     }
199     return pids;
200 }
201 
ReadFileByChar(const string & path)202 string FaultDetectorUtil::ReadFileByChar(const string &path)
203 {
204     ifstream fin(path);
205     if (!fin.is_open()) {
206         return UNKNOWN_PROCESS;
207     }
208     string content;
209     char c;
210     while (!fin.eof()) {
211         fin >> std::noskipws >> c;
212         if (c == '\0' || c == '\n') {
213             break;
214         }
215         content += c;
216     }
217     return content;
218 }
219 
RenameFile(const string & path,const string & newPath)220 bool FaultDetectorUtil::RenameFile(const string &path, const string &newPath)
221 {
222     if (rename(path.c_str(), newPath.c_str())) {
223         HIVIEW_LOGE("failed to move %{public}s to %{public}s", path.c_str(), newPath.c_str());
224         return false;
225     }
226     return true;
227 }
228 
GetRealTime()229 string FaultDetectorUtil::GetRealTime()
230 {
231     time_t now = time(nullptr);
232     return TimeStampToStr(now);
233 }
234 
GetRealTimeStampStr()235 string FaultDetectorUtil::GetRealTimeStampStr()
236 {
237     time_t now = time(nullptr);
238     return TimeStampToSimplelyStr(now);
239 }
240 
TimeStampToStr(const time_t timeStamp)241 string FaultDetectorUtil::TimeStampToStr(const time_t timeStamp)
242 {
243     struct tm tm;
244     constexpr int timeLength = 64;
245     char stampStr[timeLength] = { 0 };
246 
247     if (localtime_r(&timeStamp, &tm) == nullptr || strftime(stampStr, timeLength, "%Y/%m/%d %H/%M/%S", &tm) == 0) {
248         HIVIEW_LOGE("failed to get real time");
249         return "error time fourmat!";
250     }
251     return string(stampStr);
252 }
253 
TimeStampToSimplelyStr(const time_t timeStamp)254 string FaultDetectorUtil::TimeStampToSimplelyStr(const time_t timeStamp)
255 {
256     struct tm tm;
257     constexpr int timeLength = 64;
258     char stampStr[timeLength] = { 0 };
259 
260     if (localtime_r(&timeStamp, &tm) == nullptr || strftime(stampStr, timeLength, "%Y%m%d%H%M%S", &tm) == 0) {
261         HIVIEW_LOGE("failed to get real time");
262         return "error time fourmat!";
263     }
264     return string(stampStr);
265 }
266 
GetStatInfo(const string & path)267 vector<string> FaultDetectorUtil::GetStatInfo(const string &path)
268 {
269     string statInfo = ReadFileByChar(path);
270     // process_name was included in pair (), find ")" as start for skip special in process_name.
271     size_t pos = statInfo.find(')');
272     if (pos != string::npos) {
273         statInfo = statInfo.substr(++pos);
274     }
275     return Split(statInfo, ' ');
276 }
277 
GetProcessName(int32_t pid)278 string FaultDetectorUtil::GetProcessName(int32_t pid)
279 {
280     string path = "/proc/" + to_string(pid) + "/cmdline";
281     string name = ReadFileByChar(path);
282     // cmdline is empty ? use comm instead
283     if (name.length() < 2) { // we consider namelen < 2 as empty.
284         path = "/proc/" + to_string(pid) + "/comm";
285         name = ReadFileByChar(path);
286     }
287     auto pos = name.find_last_not_of(" \n\r\t");
288     if (pos != string::npos) {
289         name.erase(pos + 1);
290     }
291     pos = name.find_last_of("/");
292     name = name.substr(pos + 1);
293     return name;
294 }
295 
GetProcessStartTime(int pid)296 time_t FaultDetectorUtil::GetProcessStartTime(int pid)
297 {
298     string path = "/proc/" + to_string(pid) + "/stat";
299     vector<string> statInfo = GetStatInfo(path);
300     if (statInfo.size() < MAX_INDEX) {
301         return -1;
302     }
303     string startTime = statInfo[START_TIME_INDEX];
304     if (!isdigit(startTime[0])) {
305         HIVIEW_LOGE("failed to get process start time, reason: not digital, pid: %{public}d", pid);
306         return -1;
307     }
308     return stol(startTime);
309 }
310 
GetProcessRss(int pid)311 uint64_t FaultDetectorUtil::GetProcessRss(int pid)
312 {
313     string path = "/proc/" + to_string(pid) + "/rss";
314     string rssString = ReadFileByChar(path);
315     string rss = "";
316     for (char c : rssString) {
317         if (isdigit(c)) {
318             rss += c;
319         }
320     }
321     if (rss.empty()) {
322         HIVIEW_LOGD("failed to get process rss, reason: not digital, pid: %{public}d", pid);
323         return 0;
324     }
325     return stoull(rss);
326 }
327 
GetParentPid(int pid)328 int FaultDetectorUtil::GetParentPid(int pid)
329 {
330     string path = "/proc/" + to_string(pid) + "/stat";
331     vector<string> statInfo = GetStatInfo(path);
332     if (statInfo.size() < MAX_INDEX) {
333         return 0;
334     }
335     string ppid = statInfo[PPID_INDEX];
336     if (!isdigit(ppid[0])) {
337         HIVIEW_LOGE("failed to get process parent pid, reason: not digital, pid: %{public}d", pid);
338         return 0;
339     }
340     return stoi(ppid);
341 }
342 
IsKernelProcess(int pid)343 bool FaultDetectorUtil::IsKernelProcess(int pid)
344 {
345     int ppid = GetParentPid(pid);
346     return ppid == PID_KTHREADD;
347 }
348 
GetRunningMonotonicTime()349 time_t FaultDetectorUtil::GetRunningMonotonicTime()
350 {
351     struct timespec now;
352     now.tv_sec = 0;
353     now.tv_nsec = 0;
354     int32_t ret = clock_gettime(CLOCK_MONOTONIC_RAW, &now);
355     if (ret != 0) {
356         HIVIEW_LOGE("failed to get monotonic time");
357     }
358     return now.tv_sec;
359 }
360 
GetProcessUid(int32_t pid)361 int32_t FaultDetectorUtil::GetProcessUid(int32_t pid)
362 {
363     int32_t uid = -1;
364     struct stat st;
365     string path = "/proc/" + to_string(pid) + "/attr";
366     if (stat(path.c_str(), &st) == -1) {
367         HIVIEW_LOGE("stat %{public}s error", path.c_str());
368         return uid;
369     }
370     uid = static_cast<int32_t>(st.st_uid);
371     return uid;
372 }
373 
GetApplicationNameByUid(int32_t uid)374 string FaultDetectorUtil::GetApplicationNameByUid(int32_t uid)
375 {
376     string bundleName;
377     BundleMgrClient client;
378     if (client.GetNameForUid(uid, bundleName) != ERR_OK) {
379         HIVIEW_LOGE("failed to query bundleName from bms, uid: %{public}d", uid);
380         return "";
381     }
382     HIVIEW_LOGI("bundleName of uid: %{public}d is %{public}s", uid, bundleName.c_str());
383     return bundleName;
384 }
385 
GetApplicationVersion(int32_t pid)386 string FaultDetectorUtil::GetApplicationVersion(int32_t pid)
387 {
388     int32_t uid = GetProcessUid(pid);
389     if (uid < MIN_APP_USERID) {
390         return "";
391     }
392     const string bundleName = GetApplicationNameByUid(uid);
393     if (bundleName.empty()) {
394         return "";
395     }
396     BundleInfo info;
397     BundleMgrClient client;
398     if (!client.GetBundleInfo(bundleName, BundleFlag::GET_BUNDLE_DEFAULT, info, Constants::ALL_USERID)) {
399         HIVIEW_LOGE("failed to query BundleInfo from bms, uid: %{public}d", uid);
400         return "";
401     }
402     HIVIEW_LOGI("the version of %{public}s is %{public}s", bundleName.c_str(), info.versionName.c_str());
403     return info.versionName;
404 }
405 
IsBetaVersion()406 bool FaultDetectorUtil::IsBetaVersion()
407 {
408     string userType = system::GetParameter(KEY_HIVIEW_USER_TYPE, "");
409     return userType == "beta";
410 }
411 
GetStatm(int32_t pid,uint64_t & vss,uint64_t & rss)412 void FaultDetectorUtil::GetStatm(int32_t pid, uint64_t &vss, uint64_t &rss)
413 {
414     ifstream statmStream("/proc/" + to_string(pid) + "/statm");
415     if (!statmStream) {
416         HIVIEW_LOGE("Fail to open /proc/%{public}d/statm", pid);
417         return;
418     }
419     string statmLine;
420     getline(statmStream, statmLine);
421     HIVIEW_LOGI("/proc/%{public}d/statm : %{public}s", pid, statmLine.c_str());
422     statmStream.close();
423     list<string> numStrArr = GetDightStrArr(statmLine);
424     auto it = numStrArr.begin();
425     unsigned long long multiples = 4;
426     vss = multiples * stoull(*it);
427     it++;
428     rss = multiples * stoull(*it);
429 }
430 
GetMeminfo(uint64_t & avaliableMem,uint64_t & freeMem,uint64_t & totalMem)431 void FaultDetectorUtil::GetMeminfo(uint64_t &avaliableMem, uint64_t &freeMem, uint64_t &totalMem)
432 {
433     ifstream meminfoStream("/proc/meminfo");
434     if (!meminfoStream) {
435         HIVIEW_LOGE("Fail to open /proc/meminfo");
436         return;
437     }
438     string meminfoLine;
439     getline(meminfoStream, meminfoLine);
440     totalMem = stoull(GetDightStrArr(meminfoLine).front());
441     getline(meminfoStream, meminfoLine);
442     freeMem = stoull(GetDightStrArr(meminfoLine).front());
443     getline(meminfoStream, meminfoLine);
444     avaliableMem = stoull(GetDightStrArr(meminfoLine).front());
445     meminfoStream.close();
446 }
447 
GetDightStrArr(const string & target)448 list<string> FaultDetectorUtil::GetDightStrArr(const string &target)
449 {
450     list<string> ret;
451     string temp = "";
452     for (size_t i = 0, len = target.size(); i < len; i++) {
453         if (target[i] >= '0' && target[i] <= '9') {
454             temp += target[i];
455             continue;
456         }
457         if (temp.size() != 0) {
458             ret.push_back(temp);
459             temp = "";
460         }
461     }
462     if (temp.size() != 0) {
463         ret.push_back(temp);
464     }
465     ret.push_back("0");
466     return ret;
467 }
468 
469 } // namespace HiviewDFX
470 } // namespace OHOS
471