• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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