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
TidToNstid(const int pid, const int tid, int& nstid)98 bool TidToNstid(const int pid, const int tid, int& nstid)
99 {
100 std::string path = StringPrintf("/proc/%d/task/%d/status", pid, tid);
101 if (path.empty()) {
102 return false;
103 }
104
105 struct ProcInfo procInfo;
106 if (!GetProcStatusByPath(procInfo, path)) {
107 return false;
108 }
109 nstid = procInfo.nsPid;
110 return true;
111 }
112
GetProcStatusByPid(int realPid, struct ProcInfo& procInfo)113 bool GetProcStatusByPid(int realPid, struct ProcInfo& procInfo)
114 {
115 if (realPid == getpid()) {
116 return GetProcStatus(procInfo);
117 }
118 std::string path = StringPrintf("/proc/%d/status", realPid);
119 return GetProcStatusByPath(procInfo, path);
120 }
121
GetProcStatus(struct ProcInfo& procInfo)122 bool GetProcStatus(struct ProcInfo& procInfo)
123 {
124 return GetProcStatusByPath(procInfo, PROC_SELF_STATUS_PATH);
125 }
126
IsThreadInPid(int32_t pid, int32_t tid)127 bool IsThreadInPid(int32_t pid, int32_t tid)
128 {
129 std::string path;
130 if (pid == getpid()) {
131 path = StringPrintf("%s/%d", PROC_SELF_TASK_PATH, tid);
132 } else {
133 path = StringPrintf("/proc/%d/task/%d", pid, tid);
134 }
135 return access(path.c_str(), F_OK) == 0;
136 }
137
GetTidsByPidWithFunc(const int pid, std::vector<int>& tids, std::function<bool(int)> const& func)138 bool GetTidsByPidWithFunc(const int pid, std::vector<int>& tids, std::function<bool(int)> const& func)
139 {
140 std::string path;
141 if (pid == getpid()) {
142 path = std::string(PROC_SELF_TASK_PATH);
143 } else {
144 path = StringPrintf("/proc/%d/task", pid);
145 }
146
147 std::vector<std::string> files;
148 if (ReadDirFiles(path, files)) {
149 for (size_t i = 0; i < files.size(); ++i) {
150 pid_t tid = atoi(files[i].c_str());
151 if (tid == 0) {
152 continue;
153 }
154 tids.push_back(tid);
155
156 if (func != nullptr) {
157 func(tid);
158 }
159 }
160 }
161 return (tids.size() > 0);
162 }
163
GetTidsByPid(const int pid, std::vector<int>& tids, std::vector<int>& nstids)164 bool GetTidsByPid(const int pid, std::vector<int>& tids, std::vector<int>& nstids)
165 {
166 struct ProcInfo procInfo;
167 (void)GetProcStatusByPid(pid, procInfo);
168
169 std::function<bool(int)> func = nullptr;
170 if (procInfo.ns) {
171 func = [&](int tid) {
172 pid_t nstid = tid;
173 TidToNstid(pid, tid, nstid);
174 nstids.push_back(nstid);
175 return true;
176 };
177 }
178 bool ret = GetTidsByPidWithFunc(pid, tids, func);
179 if (ret && !procInfo.ns) {
180 nstids = tids;
181 }
182 return (nstids.size() > 0);
183 }
184
GetStacktraceHeader()185 std::string GetStacktraceHeader()
186 {
187 pid_t pid = getpid();
188 std::string ss = "\nTimestamp:" + GetCurrentTimeStr() + "Pid:" + std::to_string(pid) + "\n" +
189 "Uid:" + std::to_string(getuid()) + "\n";
190 std::string processName;
191 ReadProcessName(pid, processName);
192 std::string processNameNoNul;
193 for (char c : processName) {
194 if (c != '\0') {
195 processNameNoNul += c;
196 } else {
197 break;
198 }
199 }
200 ss += "Process name::" + processNameNoNul + "\n";
201 return ss;
202 }
203
ReadThreadName(const int tid, std::string& str)204 void ReadThreadName(const int tid, std::string& str)
205 {
206 std::string path = StringPrintf("/proc/%d/comm", tid);
207 std::string name;
208 OHOS::HiviewDFX::LoadStringFromFile(path, name);
209 TrimAndDupStr(name, str);
210 }
211
ReadThreadNameByPidAndTid(const int pid, const int tid, std::string& str)212 void ReadThreadNameByPidAndTid(const int pid, const int tid, std::string& str)
213 {
214 std::string path = StringPrintf("/proc/%d/task/%d/comm", pid, tid);
215 std::string name;
216 OHOS::HiviewDFX::LoadStringFromFile(path, name);
217 TrimAndDupStr(name, str);
218 }
219
ReadProcessName(const int pid, std::string& str)220 void ReadProcessName(const int pid, std::string& str)
221 {
222 std::string path;
223 if (pid == getpid()) {
224 path = std::string(PROC_SELF_CMDLINE_PATH);
225 } else {
226 path = StringPrintf("/proc/%d/cmdline", pid);
227 }
228 std::string name;
229 OHOS::HiviewDFX::LoadStringFromFile(path, name);
230 TrimAndDupStr(name, str);
231 }
232
ReadProcessStatus(std::string& result, const int pid)233 void ReadProcessStatus(std::string& result, const int pid)
234 {
235 std::string path = StringPrintf("/proc/%d/status", pid);
236 if (access(path.c_str(), F_OK) != 0) {
237 result.append(StringPrintf("Failed to access path(%s), errno(%d).\n", path.c_str(), errno));
238 return;
239 }
240 std::string content;
241 OHOS::HiviewDFX::LoadStringFromFile(path, content);
242 if (!content.empty()) {
243 std::string str = StringPrintf("Process status:\n%s\n", content.c_str());
244 result.append(str);
245 }
246 }
247
ReadProcessWchan(std::string& result, const int pid, bool onlyPid, bool withThreadName)248 void ReadProcessWchan(std::string& result, const int pid, bool onlyPid, bool withThreadName)
249 {
250 std::string path = StringPrintf("/proc/%d/wchan", pid);
251 if (access(path.c_str(), F_OK) != 0) {
252 result.append(StringPrintf("Failed to access path(%s), errno(%d).\n", path.c_str(), errno));
253 return;
254 }
255 std::string ss;
256 std::string content;
257 OHOS::HiviewDFX::LoadStringFromFile(path, content);
258 if (!content.empty()) {
259 ss = StringPrintf("Process wchan:\n%s\n", content.c_str());
260 }
261 if (onlyPid) {
262 result.append(ss);
263 return;
264 }
265 ss += "\nProcess threads wchan:\n";
266 ss += "=======================================\n";
267 bool flag = false;
268 std::string comm = "";
269 std::string wchan = "";
270 std::string taskPath = StringPrintf("/proc/%d/task", pid);
271 std::vector<std::string> files;
272 flag = ReadDirFiles(taskPath, files);
273 for (size_t i = 0; i < files.size(); ++i) {
274 std::string tidStr = files[i];
275 std::string commPath = StringPrintf("%s/%s/comm", taskPath.c_str(), tidStr.c_str());
276 std::string wchanPath = StringPrintf("%s/%s/wchan", taskPath.c_str(), tidStr.c_str());
277 OHOS::HiviewDFX::LoadStringFromFile(commPath, comm);
278 OHOS::HiviewDFX::LoadStringFromFile(wchanPath, wchan);
279 if (!comm.empty() && !wchan.empty()) {
280 flag = true;
281 if (withThreadName) {
282 ss += "Tid:" + tidStr + ", Name:" + comm;
283 }
284 ss += "wchan:" + wchan + "\n";
285 }
286 }
287
288 if (!flag) {
289 ss += "Failed to access path: " + taskPath + "\n";
290 }
291 ss += "=======================================\n";
292 result.append(ss);
293 }
294
ReadThreadWchan(std::string& result, const int tid, bool withThreadName)295 void ReadThreadWchan(std::string& result, const int tid, bool withThreadName)
296 {
297 std::string ss;
298 if (withThreadName) {
299 std::string threadName;
300 ReadThreadName(tid, threadName);
301 ss = "Tid:" + std::to_string(tid) + ", Name:" + threadName + "\n";
302 }
303 std::string wchanPath = StringPrintf("%s/%d/wchan", PROC_SELF_TASK_PATH, tid);
304 std::string wchan;
305 if (OHOS::HiviewDFX::LoadStringFromFile(wchanPath, wchan)) {
306 ss += "wchan:" + wchan + "\n";
307 } else {
308 ss += "Load thread wchan failed.\n";
309 }
310 result = ss;
311 }
312 } // namespace HiviewDFX
313 } // namespace OHOS
314