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 <file_ex.h>
17 #include <securec.h>
18 #include <string_ex.h>
19 #include <dirent.h>
20 #include <fstream>
21 #include <iostream>
22 #include "hilog_wrapper.h"
23 #include "sys/stat.h"
24 #include "util/string_utils.h"
25 #include "util/file_utils.h"
26 
27 using namespace std;
28 namespace OHOS {
29 namespace HiviewDFX {
30 namespace {
31 constexpr int LINE_ITEM_MIN = 2;
32 constexpr int LINE_KEY = 0;
33 constexpr int LINE_VALUE = 1;
34 constexpr int LINE_VALUE_0 = 0;
35 constexpr int UNSET = -1;
36 static const std::string CPU_STR = "cpu";
37 }
38 
GetSubNodes(const std::string & path,bool digit)39 std::vector<std::string> DumpCommonUtils::GetSubNodes(const std::string &path, bool digit)
40 {
41     std::vector<std::string> subNodes;
42     auto dir = opendir(path.c_str());
43     if (dir == nullptr) {
44         return subNodes;
45     }
46     for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
47         std::string childNode = ent->d_name;
48         if (childNode == "." || childNode == "..") {
49             continue;
50         }
51         if (digit && !isdigit(childNode[0])) {
52             continue;
53         }
54         subNodes.push_back(childNode);
55     }
56     closedir(dir);
57     return subNodes;
58 }
59 
60 // the parameter of path should be full.
IsDirectory(const std::string & path)61 bool DumpCommonUtils::IsDirectory(const std::string &path)
62 {
63     struct stat statBuffer;
64     if (stat(path.c_str(), &statBuffer) == 0 && S_ISDIR(statBuffer.st_mode)) {
65         return true;
66     }
67     return false;
68 }
69 
GetSubDir(const std::string & path,bool digit)70 std::vector<std::string> DumpCommonUtils::GetSubDir(const std::string &path, bool digit)
71 {
72     std::vector<std::string> subDirs;
73     auto dir = opendir(path.c_str());
74     if (dir == nullptr) {
75         DUMPER_HILOGE(MODULE_SERVICE, "failed to open dir: %{public}s, errno: %{public}d", path.c_str(), errno);
76         return subDirs;
77     }
78     for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
79         std::string childNode = ent->d_name;
80         if (childNode == "." || childNode == "..") {
81             continue;
82         }
83         if (digit && !isdigit(childNode[0])) {
84             continue;
85         }
86         if (!IsDirectory(path + "/" + childNode)) {
87             continue; // skip directory
88         }
89         subDirs.push_back(childNode);
90     }
91     closedir(dir);
92     return subDirs;
93 }
94 
GetAllPids()95 std::vector<int32_t> DumpCommonUtils::GetAllPids()
96 {
97     std::string path = "/proc";
98     std::vector<int32_t> pids;
99     std::vector<std::string> allPids = GetSubDir(path, true);
100     for (const auto &pid : allPids) {
101         if (!isdigit(pid[0])) {
102             continue;
103         }
104         pids.push_back(std::stoi(pid));
105     }
106     return pids;
107 }
108 
CpuInfo()109 DumpCommonUtils::CpuInfo::CpuInfo()
110 {
111     id_ = UNSET;
112 }
113 
GetCpuInfos(std::vector<CpuInfo> & infos)114 bool DumpCommonUtils::GetCpuInfos(std::vector<CpuInfo> &infos)
115 {
116     std::vector<std::string> names;
117     if (!GetNamesInFolder("/sys/devices/system/cpu/", names)) {
118         return false;
119     }
120     for (size_t i = 0; i < names.size(); i++) {
121         std::string name = names[i];
122         if (!StartWith(name, CPU_STR)) {
123             continue;
124         }
125         std::string cpuId = name.substr(CPU_STR.size());
126         if (cpuId.empty() || (!IsNumericStr(cpuId))) {
127             continue;
128         }
129         CpuInfo cpuInfo;
130         StrToInt(cpuId, cpuInfo.id_);
131         infos.push_back(cpuInfo);
132     }
133     return true;
134 }
135 
PidInfo()136 DumpCommonUtils::PidInfo::PidInfo()
137 {
138     Reset();
139 }
140 
Reset()141 void DumpCommonUtils::PidInfo::Reset()
142 {
143     pid_ = UNSET;
144     uid_ = UNSET;
145     gid_ = UNSET;
146     ppid_ = UNSET;
147     name_.clear();
148     cmdline_.clear();
149 }
150 
GetPidInfos(std::vector<PidInfo> & infos,bool all)151 bool DumpCommonUtils::GetPidInfos(std::vector<PidInfo> &infos, bool all)
152 {
153     std::vector<std::string> names;
154     if (!GetNamesInFolder("/proc/", names)) {
155         return false;
156     }
157     for (size_t i = 0; i < names.size(); i++) {
158         std::string name = names[i];
159         if (name.empty()) {
160             continue;
161         }
162         if (!IsNumericStr(name)) {
163             continue;
164         }
165         PidInfo pidInfo;
166         StrToInt(name, pidInfo.pid_);
167         GetProcessInfo(pidInfo.pid_, pidInfo);
168         if (all) {
169             GetProcessNameByPid(pidInfo.pid_, pidInfo.cmdline_);
170         }
171         infos.push_back(pidInfo);
172     }
173     return true;
174 }
175 
IsUserPid(const std::string & pid)176 bool DumpCommonUtils::IsUserPid(const std::string &pid)
177 {
178     string path = "/proc/" + pid + "/smaps";
179     string lineContent;
180     bool ret = FileUtils::GetInstance().LoadStringFromProcCb(path, true, false, [&](const string& line) -> void {
181         lineContent += line;
182     });
183     if (!ret) {
184         return false;
185     }
186     if (!lineContent.empty()) {
187         return true;
188     }
189     return false;
190 }
191 
GetUserPids(std::vector<int> & pids)192 bool DumpCommonUtils::GetUserPids(std::vector<int> &pids)
193 {
194     std::vector<std::string> files;
195     if (!GetNamesInFolder("/proc/", files)) {
196         return false;
197     }
198 
199     for (auto file : files) {
200         if (file.empty()) {
201             continue;
202         }
203         if (!IsNumericStr(file)) {
204             continue;
205         }
206 
207         if (!IsUserPid(file)) {
208             continue;
209         }
210 
211         int pid;
212         StrToInt(file, pid);
213 
214         pids.push_back(pid);
215     }
216     return true;
217 }
218 
GetLinesInFile(const std::string & file,std::vector<std::string> & lines)219 bool DumpCommonUtils::GetLinesInFile(const std::string& file, std::vector<std::string>& lines)
220 {
221     std::string content;
222     bool ret = FileUtils::GetInstance().LoadStringFromProcCb(file, false, false, [&](const std::string& line) -> void {
223         content += line;
224     });
225     if (!ret) {
226         return false;
227     }
228     SplitStr(content, "\n", lines);
229     return true;
230 }
231 
GetNamesInFolder(const std::string & folder,std::vector<std::string> & names)232 bool DumpCommonUtils::GetNamesInFolder(const std::string& folder, std::vector<std::string>& names)
233 {
234     bool ret = false;
235     DIR* dir = nullptr;
236     if ((dir = opendir(folder.c_str())) != nullptr) {
237         dirent* ptr = nullptr;
238         while ((ptr = readdir(dir)) != nullptr) {
239             std::string name = ptr->d_name;
240             if ((name == ".") || (name == "..")) {
241                 continue;
242             }
243             names.push_back(name);
244         }
245         closedir(dir);
246         ret = true;
247     }
248     return ret;
249 }
250 
StartWith(const std::string & str,const std::string & head)251 bool DumpCommonUtils::StartWith(const std::string& str, const std::string& head)
252 {
253     if (str.length() < head.length()) {
254         return false;
255     }
256     return (str.compare(0, head.size(), head) == 0);
257 }
258 
GetProcessNameByPid(int pid,std::string & name)259 bool DumpCommonUtils::GetProcessNameByPid(int pid, std::string& name)
260 {
261     char filesysdir[128] = { 0 };
262     if (sprintf_s(filesysdir, sizeof(filesysdir), "/proc/%d/cmdline", pid) < 0) {
263         return false;
264     }
265     std::string filePath = filesysdir;
266     std::string content;
267     bool ret = FileUtils::GetInstance().LoadStringFromProcCb(filePath, false, false, [&](const string& line) -> void {
268         content += line;
269     });
270     if (!ret) {
271         return false;
272     }
273     vector<string> names;
274     StringUtils::GetInstance().StringSplit(content, " ", names);
275     if (names.empty()) {
276         return false;
277     }
278     vector<string> longNames;
279     StringUtils::GetInstance().StringSplit(names[0], "/", longNames);
280     if (longNames.empty()) {
281         return false;
282     }
283     if (names[0].find("/bin") != std::string::npos) {
284         name = longNames[longNames.size() - 1];
285     } else {
286         name = names[0];
287     }
288     return true;
289 }
290 
GetProcessInfo(int pid,PidInfo & info)291 bool DumpCommonUtils::GetProcessInfo(int pid, PidInfo &info)
292 {
293     info.Reset();
294     char filesysdir[128] = { 0 };
295     if (sprintf_s(filesysdir, sizeof(filesysdir), "/proc/%d/status", pid) < 0) {
296         return false;
297     }
298     std::string file = filesysdir;
299     std::vector<std::string> lines;
300     if (!GetLinesInFile(file, lines)) {
301         return false;
302     }
303     const std::string splitKeyValueToken = ":";
304     const std::string splitValuesToken = "\t";
305     for (size_t i = 0; i < lines.size(); i++) {
306         std::vector<std::string> keyValue;
307         SplitStr(lines[i], splitKeyValueToken, keyValue);
308         if (keyValue.size() < LINE_ITEM_MIN) {
309             continue;
310         }
311         std::string key = keyValue[LINE_KEY];
312         std::string val = TrimStr(keyValue[LINE_VALUE], '\t');
313         std::vector<std::string> values;
314         SplitStr(val, splitValuesToken, values, true);
315         if (values.empty()) {
316             continue;
317         }
318         std::string val0 = values[LINE_VALUE_0];
319         if (key == "Pid") {
320             StrToInt(val0, info.pid_);
321         } else if (key == "PPid") {
322             StrToInt(val0, info.ppid_);
323         } else if (key == "Uid") {
324             StrToInt(val0, info.uid_);
325         } else if (key == "Gid") {
326             StrToInt(val0, info.gid_);
327         } else if (key == "Name") {
328             info.name_ = val;
329         } else {
330             continue;
331         }
332         if ((info.pid_ > UNSET) && (info.ppid_ > UNSET) && (info.uid_ > UNSET) && (info.gid_ > UNSET)) {
333             return true;
334         }
335     }
336     return false;
337 }
338 
FindDigitIndex(const std::string & fullFileName)339 int DumpCommonUtils::FindDigitIndex(const std::string& fullFileName)
340 {
341     for (size_t i = 0; i < fullFileName.size(); i++) {
342         if (std::isdigit(fullFileName[i])) {
343             return i;
344         }
345     }
346     return static_cast<int>(fullFileName.size());
347 }
348 } // namespace HiviewDFX
349 } // namespace OHOS
350