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