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