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