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.size() <= 0) {
276 return false;
277 }
278 vector<string> longNames;
279 StringUtils::GetInstance().StringSplit(names[0], "/", longNames);
280 if (longNames.size() <= 0) {
281 return false;
282 }
283 name = longNames[longNames.size() - 1];
284 return true;
285 }
286
GetProcessInfo(int pid,PidInfo & info)287 bool DumpCommonUtils::GetProcessInfo(int pid, PidInfo &info)
288 {
289 info.Reset();
290 char filesysdir[128] = { 0 };
291 if (sprintf_s(filesysdir, sizeof(filesysdir), "/proc/%d/status", pid) < 0) {
292 return false;
293 }
294 std::string file = filesysdir;
295 std::vector<std::string> lines;
296 if (!GetLinesInFile(file, lines)) {
297 return false;
298 }
299 const std::string splitKeyValueToken = ":";
300 const std::string splitValuesToken = "\t";
301 for (size_t i = 0; i < lines.size(); i++) {
302 std::vector<std::string> keyValue;
303 SplitStr(lines[i], splitKeyValueToken, keyValue);
304 if (keyValue.size() < LINE_ITEM_MIN) {
305 continue;
306 }
307 std::string key = keyValue[LINE_KEY];
308 std::string val = TrimStr(keyValue[LINE_VALUE], '\t');
309 std::vector<std::string> values;
310 SplitStr(val, splitValuesToken, values, true);
311 if (values.empty()) {
312 continue;
313 }
314 std::string val0 = values[LINE_VALUE_0];
315 if (key == "Pid") {
316 StrToInt(val0, info.pid_);
317 } else if (key == "PPid") {
318 StrToInt(val0, info.ppid_);
319 } else if (key == "Uid") {
320 StrToInt(val0, info.uid_);
321 } else if (key == "Gid") {
322 StrToInt(val0, info.gid_);
323 } else if (key == "Name") {
324 info.name_ = val;
325 } else {
326 continue;
327 }
328 if ((info.pid_ > UNSET) && (info.ppid_ > UNSET) && (info.uid_ > UNSET) && (info.gid_ > UNSET)) {
329 return true;
330 }
331 }
332 return false;
333 }
334
FindDigitIndex(const std::string & fullFileName)335 int DumpCommonUtils::FindDigitIndex(const std::string& fullFileName)
336 {
337 for (size_t i = 0; i < fullFileName.size(); i++) {
338 if (std::isdigit(fullFileName[i])) {
339 return i;
340 }
341 }
342 return static_cast<int>(fullFileName.size());
343 }
344 } // namespace HiviewDFX
345 } // namespace OHOS
346