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