1 /*
2 * Copyright (c) 2023 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
16 #include "procinfo.h"
17
18 #include <cctype>
19 #include <cerrno>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <cstring>
23 #include <securec.h>
24 #include <sstream>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include "dfx_define.h"
28 #include "dfx_util.h"
29 #include "file_util.h"
30 #include "string_printf.h"
31 #include "string_util.h"
32
33 namespace OHOS {
34 namespace HiviewDFX {
35 namespace {
36 const char * const PID_STR_NAME = "Pid:";
37 const char * const PPID_STR_NAME = "PPid:";
38 const char * const NSPID_STR_NAME = "NSpid:";
39 const int ARGS_COUNT_ONE = 1;
40 const int ARGS_COUNT_TWO = 2;
41 const int STATUS_LINE_SIZE = 1024;
42 }
43
GetProcStatusByPath(struct ProcInfo & procInfo,const std::string & path)44 static bool GetProcStatusByPath(struct ProcInfo& procInfo, const std::string& path)
45 {
46 char buf[STATUS_LINE_SIZE];
47 FILE *fp = fopen(path.c_str(), "r");
48 if (fp == nullptr) {
49 return false;
50 }
51
52 int pid = 0;
53 int ppid = 0;
54 int nsPid = 0;
55 while (!feof(fp)) {
56 if (fgets(buf, STATUS_LINE_SIZE, fp) == nullptr) {
57 fclose(fp);
58 return false;
59 }
60
61 if (strncmp(buf, PID_STR_NAME, strlen(PID_STR_NAME)) == 0) {
62 // Pid: 1892
63 if (sscanf_s(buf, "%*[^0-9]%d", &pid) != ARGS_COUNT_ONE) {
64 procInfo.pid = getpid();
65 } else {
66 procInfo.pid = pid;
67 }
68 procInfo.nsPid = pid;
69 continue;
70 }
71
72 if (strncmp(buf, PPID_STR_NAME, strlen(PPID_STR_NAME)) == 0) {
73 // PPid: 240
74 if (sscanf_s(buf, "%*[^0-9]%d", &ppid) != ARGS_COUNT_ONE) {
75 procInfo.ppid = getppid();
76 } else {
77 procInfo.ppid = ppid;
78 }
79 continue;
80 }
81
82 if (strncmp(buf, NSPID_STR_NAME, strlen(NSPID_STR_NAME)) == 0) {
83 // NSpid: 1892 1
84 if (sscanf_s(buf, "%*[^0-9]%d%*[^0-9]%d", &pid, &nsPid) != ARGS_COUNT_TWO) {
85 procInfo.ns = false;
86 procInfo.nsPid = pid;
87 } else {
88 procInfo.ns = true;
89 procInfo.nsPid = nsPid;
90 }
91 procInfo.pid = pid;
92 break;
93 }
94 }
95 (void)fclose(fp);
96 return true;
97 }
98
MoveMainThreadToHead(const int pid,std::vector<std::string> & tids)99 static void MoveMainThreadToHead(const int pid, std::vector<std::string>& tids)
100 {
101 auto it = std::find(tids.begin(), tids.end(), std::to_string(pid));
102 if (it != tids.end()) {
103 std::swap(*it, tids.front());
104 }
105 }
106
TidToNstid(const int pid,const int tid,int & nstid)107 bool TidToNstid(const int pid, const int tid, int& nstid)
108 {
109 std::string path = StringPrintf("/proc/%d/task/%d/status", pid, tid);
110 if (path.empty()) {
111 return false;
112 }
113
114 struct ProcInfo procInfo;
115 if (!GetProcStatusByPath(procInfo, path)) {
116 return false;
117 }
118 nstid = procInfo.nsPid;
119 return true;
120 }
121
GetProcStatusByPid(int realPid,struct ProcInfo & procInfo)122 bool GetProcStatusByPid(int realPid, struct ProcInfo& procInfo)
123 {
124 if (realPid == getpid()) {
125 return GetProcStatus(procInfo);
126 }
127 std::string path = StringPrintf("/proc/%d/status", realPid);
128 return GetProcStatusByPath(procInfo, path);
129 }
130
GetProcStatus(struct ProcInfo & procInfo)131 bool GetProcStatus(struct ProcInfo& procInfo)
132 {
133 return GetProcStatusByPath(procInfo, PROC_SELF_STATUS_PATH);
134 }
135
IsThreadInPid(int32_t pid,int32_t tid)136 bool IsThreadInPid(int32_t pid, int32_t tid)
137 {
138 std::string path;
139 if (pid == getpid()) {
140 path = StringPrintf("%s/%d", PROC_SELF_TASK_PATH, tid);
141 } else {
142 path = StringPrintf("/proc/%d/task/%d", pid, tid);
143 }
144 return access(path.c_str(), F_OK) == 0;
145 }
146
GetTidsByPidWithFunc(const int pid,std::vector<int> & tids,std::function<bool (int)> const & func)147 bool GetTidsByPidWithFunc(const int pid, std::vector<int>& tids, std::function<bool(int)> const& func)
148 {
149 std::string path = (pid == getpid()) ? PROC_SELF_TASK_PATH : StringPrintf("/proc/%d/task", pid);
150 std::vector<std::string> files;
151 if (ReadDirFiles(path, files)) {
152 MoveMainThreadToHead(pid, files);
153 for (const auto& file : files) {
154 pid_t tid = atoi(file.c_str());
155 if (tid == 0) {
156 continue;
157 }
158 tids.emplace_back(tid);
159
160 if (func != nullptr) {
161 func(tid);
162 }
163 }
164 }
165 return (tids.size() > 0);
166 }
167
GetTidsByPid(const int pid,std::vector<int> & tids,std::vector<int> & nstids)168 bool GetTidsByPid(const int pid, std::vector<int>& tids, std::vector<int>& nstids)
169 {
170 struct ProcInfo procInfo{};
171 (void)GetProcStatusByPid(pid, procInfo);
172
173 std::function<bool(int)> func = nullptr;
174 if (procInfo.ns) {
175 func = [&](int tid) {
176 pid_t nstid = tid;
177 TidToNstid(pid, tid, nstid);
178 nstids.emplace_back(nstid);
179 return true;
180 };
181 }
182 bool ret = GetTidsByPidWithFunc(pid, tids, func);
183 if (ret && !procInfo.ns) {
184 nstids = tids;
185 }
186 return (nstids.size() > 0);
187 }
188
GetStacktraceHeader()189 std::string GetStacktraceHeader()
190 {
191 pid_t pid = getpid();
192 std::string ss = "\nTimestamp:" + GetCurrentTimeStr() + "Pid:" + std::to_string(pid) + "\n" +
193 "Uid:" + std::to_string(getuid()) + "\n";
194 std::string processName;
195 ReadProcessName(pid, processName);
196 std::string processNameNoNul;
197 for (char c : processName) {
198 if (c != '\0') {
199 processNameNoNul += c;
200 } else {
201 break;
202 }
203 }
204 ss += "Process name::" + processNameNoNul + "\n";
205 return ss;
206 }
207
ReadThreadName(const int tid,std::string & str)208 void ReadThreadName(const int tid, std::string& str)
209 {
210 std::string path = StringPrintf("/proc/%d/comm", tid);
211 std::string name;
212 OHOS::HiviewDFX::LoadStringFromFile(path, name);
213 TrimAndDupStr(name, str);
214 }
215
ReadThreadNameByPidAndTid(const int pid,const int tid,std::string & str)216 void ReadThreadNameByPidAndTid(const int pid, const int tid, std::string& str)
217 {
218 std::string path = StringPrintf("/proc/%d/task/%d/comm", pid, tid);
219 std::string name;
220 OHOS::HiviewDFX::LoadStringFromFile(path, name);
221 TrimAndDupStr(name, str);
222 }
223
ReadProcessName(const int pid,std::string & str)224 void ReadProcessName(const int pid, std::string& str)
225 {
226 std::string path;
227 if (pid == getpid()) {
228 path = std::string(PROC_SELF_CMDLINE_PATH);
229 } else {
230 path = StringPrintf("/proc/%d/cmdline", pid);
231 }
232 std::string name;
233 OHOS::HiviewDFX::LoadStringFromFile(path, name);
234 TrimAndDupStr(name, str);
235 }
236
ReadProcessStatus(std::string & result,const int pid)237 void ReadProcessStatus(std::string& result, const int pid)
238 {
239 std::string path = StringPrintf("/proc/%d/status", pid);
240 if (access(path.c_str(), F_OK) != 0) {
241 result.append(StringPrintf("Failed to access path(%s), errno(%d).\n", path.c_str(), errno));
242 return;
243 }
244 std::string content;
245 OHOS::HiviewDFX::LoadStringFromFile(path, content);
246 if (!content.empty()) {
247 std::string str = StringPrintf("Process status:\n%s\n", content.c_str());
248 result.append(str);
249 }
250 }
251
ReadProcessWchan(std::string & result,const int pid,bool onlyPid,bool withThreadName)252 void ReadProcessWchan(std::string& result, const int pid, bool onlyPid, bool withThreadName)
253 {
254 std::string path = StringPrintf("/proc/%d/wchan", pid);
255 if (access(path.c_str(), F_OK) != 0) {
256 result.append(StringPrintf("Failed to access path(%s), errno(%d).\n", path.c_str(), errno));
257 return;
258 }
259 std::string ss;
260 std::string content;
261 OHOS::HiviewDFX::LoadStringFromFile(path, content);
262 if (!content.empty()) {
263 ss = StringPrintf("Process wchan:\n%s\n", content.c_str());
264 }
265 if (onlyPid) {
266 result.append(ss);
267 return;
268 }
269 ss += "\nProcess threads wchan:\n";
270 ss += "=======================================\n";
271 bool flag = false;
272 std::string comm = "";
273 std::string wchan = "";
274 std::string taskPath = StringPrintf("/proc/%d/task", pid);
275 std::vector<std::string> files;
276 flag = ReadDirFiles(taskPath, files);
277 for (size_t i = 0; i < files.size(); ++i) {
278 std::string tidStr = files[i];
279 std::string commPath = StringPrintf("%s/%s/comm", taskPath.c_str(), tidStr.c_str());
280 std::string wchanPath = StringPrintf("%s/%s/wchan", taskPath.c_str(), tidStr.c_str());
281 OHOS::HiviewDFX::LoadStringFromFile(commPath, comm);
282 OHOS::HiviewDFX::LoadStringFromFile(wchanPath, wchan);
283 if (!comm.empty() && !wchan.empty()) {
284 flag = true;
285 if (withThreadName) {
286 ss += "Tid:" + tidStr + ", Name:" + comm;
287 }
288 ss += "wchan:" + wchan + "\n";
289 }
290 }
291
292 if (!flag) {
293 ss += "Failed to access path: " + taskPath + "\n";
294 }
295 ss += "=======================================\n";
296 result.append(ss);
297 }
298
ReadThreadWchan(std::string & result,const int tid,bool withThreadName)299 void ReadThreadWchan(std::string& result, const int tid, bool withThreadName)
300 {
301 std::string ss;
302 if (withThreadName) {
303 std::string threadName;
304 ReadThreadName(tid, threadName);
305 ss = "Tid:" + std::to_string(tid) + ", Name:" + threadName + "\n";
306 }
307 std::string wchanPath = StringPrintf("%s/%d/wchan", PROC_SELF_TASK_PATH, tid);
308 std::string wchan;
309 if (OHOS::HiviewDFX::LoadStringFromFile(wchanPath, wchan)) {
310 ss += "wchan:" + wchan + "\n";
311 } else {
312 ss += "Load thread wchan failed.\n";
313 }
314 result = ss;
315 }
316
GetProcRssMemInfo(pid_t pid)317 uint64_t GetProcRssMemInfo(pid_t pid)
318 {
319 if (pid <= 0) {
320 return 0;
321 }
322
323 constexpr int tokenNum = 2;
324 constexpr int decimalBase = 10;
325 std::string statmPath = "/proc/" + std::to_string(pid) + "/statm";
326 std::string readContent;
327
328 if (!LoadStringFromFile(statmPath, readContent)) {
329 return 0;
330 }
331
332 std::istringstream iss(readContent);
333 std::vector<std::string> tokens;
334 for (int i = 0; i < tokenNum; i++) {
335 std::string token;
336 getline(iss, token, ' ');
337 if (!token.empty()) {
338 tokens.push_back(token);
339 }
340 }
341
342 if (tokens.size() < tokenNum) {
343 return 0;
344 }
345
346 std::string rssStr = tokens.at(1);
347 uint64_t rss = static_cast<uint64_t>(strtoull(rssStr.c_str(), nullptr, decimalBase));
348 if (rss == 0) {
349 return rss;
350 }
351 constexpr int pageSizeKB = 4;
352 constexpr int sizekiB = 1024;
353 constexpr int sizekB = 1000;
354 rss = (rss * pageSizeKB * sizekiB) / sizekB;
355 return rss;
356 }
357
GetTidByThreadName(pid_t pid,const std::string & threadName)358 pid_t GetTidByThreadName(pid_t pid, const std::string& threadName)
359 {
360 if (pid <= 0) {
361 return -1;
362 }
363 std::vector<int> tids;
364 std::vector<int> nstids;
365 if (!GetTidsByPid(pid, tids, nstids)) {
366 return -1;
367 }
368 for (const auto tid : tids) {
369 std::string tmpThreadName;
370 ReadThreadNameByPidAndTid(pid, tid, tmpThreadName);
371 if (tmpThreadName == threadName) {
372 return tid;
373 }
374 }
375 return -1;
376 }
377 } // namespace HiviewDFX
378 } // namespace OHOS
379