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