• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "dump_common_utils.h"
16 #include <fcntl.h>
17 #include <file_ex.h>
18 #include <securec.h>
19 #include <string_ex.h>
20 #include <dirent.h>
21 #include <fstream>
22 #include <iostream>
23 #include "hilog_wrapper.h"
24 #ifdef HIDUMPER_HIVIEWDFX_HISYSEVENT_ENABLE
25 #include "hisysevent.h"
26 #endif
27 #include "sys/stat.h"
28 #include "util/string_utils.h"
29 #include "util/file_utils.h"
30 
31 using namespace std;
32 namespace OHOS {
33 namespace HiviewDFX {
34 namespace {
35 constexpr int LINE_ITEM_MIN = 2;
36 constexpr int LINE_KEY = 0;
37 constexpr int LINE_VALUE = 1;
38 constexpr int LINE_VALUE_0 = 0;
39 constexpr int UNSET = -1;
40 constexpr double ONE_DAY_TO_SECONDS = 24 * 60 * 60;
41 static const std::string CPU_STR = "cpu";
42 static const std::string STORAGE_PATH_PREFIX = "/data/storage/el";
43 static const size_t STORAGE_PATH_SIZE = STORAGE_PATH_PREFIX.size();
44 static const int TM_START_YEAR = 1900;
45 static const int DEC_SYSTEM_VALUE = 10;
46 }
47 
GetSubNodes(const std::string & path,bool digit)48 std::vector<std::string> DumpCommonUtils::GetSubNodes(const std::string &path, bool digit)
49 {
50     std::vector<std::string> subNodes;
51     auto dir = opendir(path.c_str());
52     if (dir == nullptr) {
53         return subNodes;
54     }
55     for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
56         std::string childNode = ent->d_name;
57         if (childNode == "." || childNode == "..") {
58             continue;
59         }
60         if (digit && !IsNumericStr(childNode)) {
61             continue;
62         }
63         subNodes.push_back(childNode);
64     }
65     closedir(dir);
66     return subNodes;
67 }
68 
69 // the parameter of path should be full.
IsDirectory(const std::string & path)70 bool DumpCommonUtils::IsDirectory(const std::string &path)
71 {
72     struct stat statBuffer;
73     if (stat(path.c_str(), &statBuffer) == 0 && S_ISDIR(statBuffer.st_mode)) {
74         return true;
75     }
76     return false;
77 }
78 
GetSubDir(const std::string & path,bool digit)79 std::vector<std::string> DumpCommonUtils::GetSubDir(const std::string &path, bool digit)
80 {
81     std::vector<std::string> subDirs;
82     auto dir = opendir(path.c_str());
83     if (dir == nullptr) {
84         DUMPER_HILOGE(MODULE_SERVICE, "failed to open dir: %{public}s, errno: %{public}d", path.c_str(), errno);
85         return subDirs;
86     }
87     for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
88         std::string childNode = ent->d_name;
89         if (childNode == "." || childNode == "..") {
90             continue;
91         }
92         if (digit && !IsNumericStr(childNode)) {
93             continue;
94         }
95         if (!IsDirectory(path + "/" + childNode)) {
96             continue; // skip directory
97         }
98         subDirs.push_back(childNode);
99     }
100     closedir(dir);
101     return subDirs;
102 }
103 
GetAllPids()104 std::vector<int32_t> DumpCommonUtils::GetAllPids()
105 {
106     std::string path = "/proc";
107     std::vector<int32_t> pids;
108     std::vector<std::string> allPids = GetSubDir(path, true);
109     for (const auto &pid : allPids) {
110         if (!IsNumericStr(pid)) {
111             continue;
112         }
113         pids.push_back(std::stoi(pid));
114     }
115     return pids;
116 }
117 
CpuInfo()118 DumpCommonUtils::CpuInfo::CpuInfo()
119 {
120     id_ = UNSET;
121 }
122 
GetCpuInfos(std::vector<CpuInfo> & infos)123 bool DumpCommonUtils::GetCpuInfos(std::vector<CpuInfo> &infos)
124 {
125     std::vector<std::string> names;
126     if (!GetNamesInFolder("/sys/devices/system/cpu/", names)) {
127         return false;
128     }
129     for (size_t i = 0; i < names.size(); i++) {
130         std::string name = names[i];
131         if (!StartWith(name, CPU_STR)) {
132             continue;
133         }
134         std::string cpuId = name.substr(CPU_STR.size());
135         if (cpuId.empty() || (!IsNumericStr(cpuId))) {
136             continue;
137         }
138         CpuInfo cpuInfo;
139         StrToInt(cpuId, cpuInfo.id_);
140         infos.push_back(cpuInfo);
141     }
142     return true;
143 }
144 
PidInfo()145 DumpCommonUtils::PidInfo::PidInfo()
146 {
147     Reset();
148 }
149 
Reset()150 void DumpCommonUtils::PidInfo::Reset()
151 {
152     pid_ = UNSET;
153     uid_ = UNSET;
154     gid_ = UNSET;
155     ppid_ = UNSET;
156     name_.clear();
157     cmdline_.clear();
158 }
159 
GetPidInfos(std::vector<PidInfo> & infos,bool all)160 bool DumpCommonUtils::GetPidInfos(std::vector<PidInfo> &infos, bool all)
161 {
162     std::vector<std::string> names;
163     if (!GetNamesInFolder("/proc/", names)) {
164         return false;
165     }
166     for (size_t i = 0; i < names.size(); i++) {
167         std::string name = names[i];
168         if (name.empty()) {
169             continue;
170         }
171         if (!IsNumericStr(name)) {
172             continue;
173         }
174         PidInfo pidInfo;
175         StrToInt(name, pidInfo.pid_);
176         GetProcessInfo(pidInfo.pid_, pidInfo);
177         if (all) {
178             GetProcessNameByPid(pidInfo.pid_, pidInfo.cmdline_);
179         }
180         infos.push_back(pidInfo);
181     }
182     return true;
183 }
184 
IsUserPid(const std::string & pid)185 bool DumpCommonUtils::IsUserPid(const std::string &pid)
186 {
187     string path = "/proc/" + pid + "/smaps";
188     string lineContent;
189     bool ret = FileUtils::GetInstance().LoadStringFromProcCb(path, true, false, [&](const string& line) -> void {
190         lineContent += line;
191     });
192     if (!ret) {
193         return false;
194     }
195     if (!lineContent.empty()) {
196         return true;
197     }
198     return false;
199 }
200 
GetUserPids(std::vector<int> & pids)201 bool DumpCommonUtils::GetUserPids(std::vector<int> &pids)
202 {
203     std::vector<std::string> files;
204     if (!GetNamesInFolder("/proc/", files)) {
205         return false;
206     }
207 
208     for (auto file : files) {
209         if (file.empty()) {
210             continue;
211         }
212         if (!IsNumericStr(file)) {
213             continue;
214         }
215 
216         if (!IsUserPid(file)) {
217             continue;
218         }
219 
220         int pid;
221         StrToInt(file, pid);
222 
223         pids.push_back(pid);
224     }
225     return true;
226 }
227 
GetLinesInFile(const std::string & file,std::vector<std::string> & lines)228 bool DumpCommonUtils::GetLinesInFile(const std::string& file, std::vector<std::string>& lines)
229 {
230     std::string content;
231     bool ret = FileUtils::GetInstance().LoadStringFromProcCb(file, false, false, [&](const std::string& line) -> void {
232         content += line;
233     });
234     if (!ret) {
235         return false;
236     }
237     SplitStr(content, "\n", lines);
238     return true;
239 }
240 
GetNamesInFolder(const std::string & folder,std::vector<std::string> & names)241 bool DumpCommonUtils::GetNamesInFolder(const std::string& folder, std::vector<std::string>& names)
242 {
243     bool ret = false;
244     DIR* dir = nullptr;
245     if ((dir = opendir(folder.c_str())) != nullptr) {
246         dirent* ptr = nullptr;
247         while ((ptr = readdir(dir)) != nullptr) {
248             std::string name = ptr->d_name;
249             if ((name == ".") || (name == "..")) {
250                 continue;
251             }
252             names.push_back(name);
253         }
254         closedir(dir);
255         ret = true;
256     }
257     return ret;
258 }
259 
StartWith(const std::string & str,const std::string & head)260 bool DumpCommonUtils::StartWith(const std::string& str, const std::string& head)
261 {
262     if (str.length() < head.length()) {
263         return false;
264     }
265     return (str.compare(0, head.size(), head) == 0);
266 }
267 
GetProcessNameByPid(int pid,std::string & name)268 bool DumpCommonUtils::GetProcessNameByPid(int pid, std::string& name)
269 {
270     char filesysdir[128] = { 0 };
271     if (sprintf_s(filesysdir, sizeof(filesysdir), "/proc/%d/cmdline", pid) < 0) {
272         return false;
273     }
274     std::string filePath = filesysdir;
275     std::string content;
276     bool ret = FileUtils::GetInstance().LoadStringFromProcCb(filePath, false, false, [&](const string& line) -> void {
277         content += line;
278     });
279     if (!ret) {
280         return false;
281     }
282     vector<string> names;
283     StringUtils::GetInstance().StringSplit(content, " ", names);
284     if (names.empty()) {
285         return false;
286     }
287     vector<string> longNames;
288     StringUtils::GetInstance().StringSplit(names[0], "/", longNames);
289     if (longNames.empty()) {
290         return false;
291     }
292     if (names[0].find("/bin") != std::string::npos) {
293         name = longNames[longNames.size() - 1];
294     } else {
295         name = names[0];
296     }
297     return true;
298 }
299 
GetProcessInfo(int pid,PidInfo & info)300 bool DumpCommonUtils::GetProcessInfo(int pid, PidInfo &info)
301 {
302     info.Reset();
303     char filesysdir[128] = { 0 };
304     if (sprintf_s(filesysdir, sizeof(filesysdir), "/proc/%d/status", pid) < 0) {
305         return false;
306     }
307     std::string file = filesysdir;
308     std::vector<std::string> lines;
309     if (!GetLinesInFile(file, lines)) {
310         return false;
311     }
312     const std::string splitKeyValueToken = ":";
313     const std::string splitValuesToken = "\t";
314     for (size_t i = 0; i < lines.size(); i++) {
315         std::vector<std::string> keyValue;
316         SplitStr(lines[i], splitKeyValueToken, keyValue);
317         if (keyValue.size() < LINE_ITEM_MIN) {
318             continue;
319         }
320         std::string key = keyValue[LINE_KEY];
321         std::string val = TrimStr(keyValue[LINE_VALUE], '\t');
322         std::vector<std::string> values;
323         SplitStr(val, splitValuesToken, values, true);
324         if (values.empty()) {
325             continue;
326         }
327         std::string val0 = values[LINE_VALUE_0];
328         if (key == "Pid") {
329             StrToInt(val0, info.pid_);
330         } else if (key == "PPid") {
331             StrToInt(val0, info.ppid_);
332         } else if (key == "Uid") {
333             StrToInt(val0, info.uid_);
334         } else if (key == "Gid") {
335             StrToInt(val0, info.gid_);
336         } else if (key == "Name") {
337             info.name_ = val;
338         } else {
339             continue;
340         }
341         if ((info.pid_ > UNSET) && (info.ppid_ > UNSET) && (info.uid_ > UNSET) && (info.gid_ > UNSET)) {
342             return true;
343         }
344     }
345     return false;
346 }
347 
FindNonSandBoxPathIndex(const std::string & fullFileName)348 int DumpCommonUtils::FindNonSandBoxPathIndex(const std::string& fullFileName)
349 {
350     for (size_t i = 0; i < fullFileName.size(); i++) {
351         if (std::isdigit(fullFileName[i])) {
352             return i;
353         }
354     }
355     return static_cast<int>(fullFileName.size());
356 }
357 
FindFdClusterStartIndex(const std::string & fullFileName)358 int DumpCommonUtils::FindFdClusterStartIndex(const std::string& fullFileName)
359 {
360     size_t fileNameSize = fullFileName.size();
361     if (fullFileName.compare(0, STORAGE_PATH_SIZE, STORAGE_PATH_PREFIX) != 0 ||
362         !std::isdigit(fullFileName[STORAGE_PATH_SIZE])) {
363             return FindNonSandBoxPathIndex(fullFileName);
364     }
365     for (size_t i = STORAGE_PATH_SIZE + 1; i < fileNameSize; i++) {
366         if (std::isdigit(fullFileName[i])) {
367             return static_cast<int>(i);
368         }
369     }
370     return static_cast<int>(fileNameSize);
371 }
372 
ReportCmdUsage(const std::unique_ptr<DumperSysEventParams> & param)373 void DumpCommonUtils::ReportCmdUsage(const std::unique_ptr<DumperSysEventParams>& param)
374 {
375     ClearHisyseventTmpFile();
376     int fd = -1;
377     fd = TEMP_FAILURE_RETRY(open(HISYSEVENT_TMP_FILE.c_str(), O_RDWR | O_CREAT | O_APPEND,
378         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
379     if (fd < 0) {
380         DUMPER_HILOGE(MODULE_COMMON, "open hisysevent_tmp file error: %{public}d", errno);
381         return;
382     }
383     std::string content = "";
384     if (!OHOS::LoadStringFromFd(fd, content)) {
385         DUMPER_HILOGE(MODULE_COMMON, "LoadStringFromFd error! %{public}d", errno);
386         close(fd);
387         return;
388     }
389     std::string option = "";
390     param->errorCode == 0 ? option = "OPT:" + param->opt + " SUB_OPT:" + param->subOpt + "\n" :
391         option = "ERROR_MESSAGE:" + param->errorMsg + "\n";
392     if (content.find(option) != std::string::npos) {
393         DUMPER_HILOGD(MODULE_COMMON, "hisysevent data contain option, not report");
394         close(fd);
395         return;
396     }
397     std::string callerProcess = "unknown";
398     DumpCommonUtils::GetProcessNameByPid(param->callerPpid, callerProcess);
399 #ifdef HIDUMPER_HIVIEWDFX_HISYSEVENT_ENABLE
400     int ret = HiSysEventWrite(HiSysEvent::Domain::HIDUMPER, "CMD_USAGE",
401         OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
402         "OPT", param->opt, "CALLER", callerProcess, "SUB_OPT", param->subOpt, "TARGET", param->target,
403         "ARGS", param->arguments, "ERROR_CODE", param->errorCode, "ERROR_MESSAGE", param->errorMsg);
404     if (ret != 0) {
405         DUMPER_HILOGE(MODULE_COMMON, "hisysevent report hidumper usage failed! ret %{public}d.", ret);
406         close(fd);
407         return;
408     }
409 #endif
410     SaveStringToFd(fd, option.c_str());
411     close(fd);
412 }
413 
ClearHisyseventTmpFile()414 void DumpCommonUtils::ClearHisyseventTmpFile()
415 {
416     if (!OHOS::FileExists(HISYSEVENT_TMP_FILE)) {
417         DUMPER_HILOGE(MODULE_COMMON, "HISYSEVENT_TMP_FILE not exists");
418         return;
419     }
420     time_t lastWriteTime;
421     if (!FileUtils::GetInstance().GetLastWriteTime(HISYSEVENT_TMP_FILE, lastWriteTime)) {
422         DUMPER_HILOGE(MODULE_COMMON, "GetLastWriteTime failed");
423         return;
424     }
425     time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
426     struct tm currentTimeData = {0};
427     localtime_r(&currentTime, &currentTimeData);
428     struct tm lastTimeData = {0};
429     localtime_r(&lastWriteTime, &lastTimeData);
430     if (difftime(currentTime, lastWriteTime) > ONE_DAY_TO_SECONDS ||
431         (currentTimeData.tm_year == lastTimeData.tm_year && currentTimeData.tm_mon == lastTimeData.tm_mon &&
432         currentTimeData.tm_mday - lastTimeData.tm_mday >= 1)) {
433         DUMPER_HILOGI(MODULE_COMMON, "start clear data");
434         std::ofstream ofs(HISYSEVENT_TMP_FILE, std::ios::out | std::ios::trunc);
435         ofs.close();
436     }
437 }
438 
GetMilliseconds()439 uint64_t DumpCommonUtils::GetMilliseconds()
440 {
441     auto now = std::chrono::system_clock::now();
442     auto millisecs = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
443     return millisecs.count();
444 }
445 
GetDateAndTime(uint64_t timeStamp,std::string & dateTime)446 void DumpCommonUtils::GetDateAndTime(uint64_t timeStamp, std::string &dateTime)
447 {
448     time_t time = static_cast<time_t>(timeStamp);
449     struct tm timeData = {0};
450     localtime_r(&time, &timeData);
451 
452     dateTime = "";
453     dateTime.append(std::to_string(TM_START_YEAR + timeData.tm_year));
454     dateTime.append("-");
455     if (1 + timeData.tm_mon < DEC_SYSTEM_VALUE) {
456         dateTime.append(std::to_string(0));
457     }
458     dateTime.append(std::to_string(1 + timeData.tm_mon));
459     dateTime.append("-");
460     if (timeData.tm_mday < DEC_SYSTEM_VALUE) {
461         dateTime.append(std::to_string(0));
462     }
463     dateTime.append(std::to_string(timeData.tm_mday));
464     dateTime.append(" ");
465     if (timeData.tm_hour < DEC_SYSTEM_VALUE) {
466         dateTime.append(std::to_string(0));
467     }
468     dateTime.append(std::to_string(timeData.tm_hour));
469     dateTime.append(":");
470     if (timeData.tm_min < DEC_SYSTEM_VALUE) {
471         dateTime.append(std::to_string(0));
472     }
473     dateTime.append(std::to_string(timeData.tm_min));
474     dateTime.append(":");
475     if (timeData.tm_sec < DEC_SYSTEM_VALUE) {
476         dateTime.append(std::to_string(0));
477     }
478     dateTime.append(std::to_string(timeData.tm_sec));
479 }
480 
481 } // namespace HiviewDFX
482 } // namespace OHOS
483