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 <iostream>
24 #include <sstream>
25 #include <securec.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include "dfx_define.h"
29 #include "dfx_util.h"
30 #include "file_util.h"
31 #include "string_util.h"
32
33 namespace OHOS {
34 namespace HiviewDFX {
35 namespace {
36 const char PID_STR_NAME[] = "Pid:";
37 const char PPID_STR_NAME[] = "PPid:";
38 const char 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
TidToNstid(const int pid,const int tid,int & nstid)99 bool TidToNstid(const int pid, const int tid, int& nstid)
100 {
101 std::string path = StringPrintf("/proc/%d/task/%d/status", pid, tid);
102 if (path.empty()) {
103 return false;
104 }
105
106 struct ProcInfo procInfo;
107 if (!GetProcStatusByPath(procInfo, path)) {
108 return false;
109 }
110 nstid = procInfo.nsPid;
111 return true;
112 }
113
GetProcStatusByPid(int realPid,struct ProcInfo & procInfo)114 bool GetProcStatusByPid(int realPid, struct ProcInfo& procInfo)
115 {
116 if (realPid == getpid()) {
117 return GetProcStatus(procInfo);
118 }
119 std::string path = StringPrintf("/proc/%d/status", realPid);
120 return GetProcStatusByPath(procInfo, path);
121 }
122
GetProcStatus(struct ProcInfo & procInfo)123 bool GetProcStatus(struct ProcInfo& procInfo)
124 {
125 return GetProcStatusByPath(procInfo, PROC_SELF_STATUS_PATH);
126 }
127
IsThreadInPid(int32_t pid,int32_t tid)128 bool IsThreadInPid(int32_t pid, int32_t tid)
129 {
130 std::string path;
131 if (pid == getpid()) {
132 path = StringPrintf("%s/%d", PROC_SELF_TASK_PATH, tid);
133 } else {
134 path = StringPrintf("/proc/%d/task/%d", pid, tid);
135 }
136 return access(path.c_str(), F_OK) == 0;
137 }
138
GetTidsByPidWithFunc(const int pid,std::vector<int> & tids,std::function<bool (int)> const & func)139 bool GetTidsByPidWithFunc(const int pid, std::vector<int>& tids, std::function<bool(int)> const& func)
140 {
141 std::vector<std::string> files;
142 if (ReadDirFilesByPid(pid, files)) {
143 for (size_t i = 0; i < files.size(); ++i) {
144 pid_t tid = atoi(files[i].c_str());
145 if (tid == 0) {
146 continue;
147 }
148 tids.push_back(tid);
149
150 if (func != nullptr) {
151 func(tid);
152 }
153 }
154 }
155 return (tids.size() > 0);
156 }
157
GetTidsByPid(const int pid,std::vector<int> & tids,std::vector<int> & nstids)158 bool GetTidsByPid(const int pid, std::vector<int>& tids, std::vector<int>& nstids)
159 {
160 struct ProcInfo procInfo;
161 (void)GetProcStatusByPid(pid, procInfo);
162
163 std::function<bool(int)> func = nullptr;
164 if (procInfo.ns) {
165 func = [&](int tid) {
166 pid_t nstid = tid;
167 TidToNstid(pid, tid, nstid);
168 nstids.push_back(nstid);
169 return true;
170 };
171 }
172 bool ret = GetTidsByPidWithFunc(pid, tids, func);
173 if (ret && !procInfo.ns) {
174 nstids = tids;
175 }
176 return (nstids.size() > 0);
177 }
178
ReadThreadName(const int tid,std::string & str)179 void ReadThreadName(const int tid, std::string& str)
180 {
181 std::string path = StringPrintf("/proc/%d/comm", tid);
182 std::string name;
183 OHOS::HiviewDFX::LoadStringFromFile(path, name);
184 TrimAndDupStr(name, str);
185 }
186
ReadProcessName(const int pid,std::string & str)187 void ReadProcessName(const int pid, std::string& str)
188 {
189 std::string path;
190 if (pid == getpid()) {
191 path = std::string(PROC_SELF_CMDLINE_PATH);
192 } else {
193 path = StringPrintf("/proc/%d/cmdline", pid);
194 }
195 std::string name;
196 OHOS::HiviewDFX::LoadStringFromFile(path, name);
197 TrimAndDupStr(name, str);
198 }
199
ReadProcessStatus(std::string & result,const int pid)200 void ReadProcessStatus(std::string& result, const int pid)
201 {
202 std::string path = StringPrintf("/proc/%d/status", pid);
203 if (access(path.c_str(), F_OK) != 0) {
204 result.append(StringPrintf("Failed to access path(%s), errno(%d).\n", path.c_str(), errno));
205 return;
206 }
207 std::string content;
208 OHOS::HiviewDFX::LoadStringFromFile(path, content);
209 if (!content.empty()) {
210 std::string str = StringPrintf("Process status:\n%s\n", content.c_str());
211 result.append(str);
212 }
213 }
214
ReadProcessWchan(std::string & result,const int pid,bool onlyPid,bool withThreadName)215 void ReadProcessWchan(std::string& result, const int pid, bool onlyPid, bool withThreadName)
216 {
217 std::string path = StringPrintf("/proc/%d/wchan", pid);
218 if (access(path.c_str(), F_OK) != 0) {
219 result.append(StringPrintf("Failed to access path(%s), errno(%d).\n", path.c_str(), errno));
220 return;
221 }
222 std::ostringstream ss;
223 std::string content;
224 OHOS::HiviewDFX::LoadStringFromFile(path, content);
225 if (!content.empty()) {
226 ss << "Process wchan:\n";
227 ss << StringPrintf("%s\n", content.c_str());
228 }
229 if (onlyPid) {
230 result.append(ss.str());
231 return;
232 }
233 ss << "\nProcess threads wchan:\n";
234 ss << "=======================================\n";
235 bool flag = false;
236 std::string comm;
237 std::string wchan;
238 std::string taskPath = StringPrintf("/proc/%d/task", pid);
239 std::vector<std::string> files;
240 flag = ReadDirFiles(taskPath, files);
241 for (size_t i = 0; i < files.size(); ++i) {
242 std::string tidStr = files[i];
243 std::string commPath = StringPrintf("%s/%s/comm", taskPath.c_str(), tidStr.c_str());
244 std::string wchanPath = StringPrintf("%s/%s/wchan", taskPath.c_str(), tidStr.c_str());
245 OHOS::HiviewDFX::LoadStringFromFile(commPath, comm);
246 OHOS::HiviewDFX::LoadStringFromFile(wchanPath, wchan);
247 if (!comm.empty() && !wchan.empty()) {
248 flag = true;
249 if (withThreadName) {
250 ss << "Tid:" << tidStr << ", Name:" << comm;
251 }
252 ss << "wchan:" << wchan << std::endl;
253 }
254 }
255
256 if (!flag) {
257 ss << "Load process wchan failed." << std::endl;
258 }
259 ss << "=======================================\n";
260 result.append(ss.str());
261 }
262
ReadThreadWchan(std::string & result,const int tid,bool withThreadName)263 void ReadThreadWchan(std::string& result, const int tid, bool withThreadName)
264 {
265 std::ostringstream ss;
266 if (withThreadName) {
267 std::string threadName;
268 ReadThreadName(tid, threadName);
269 ss << "Tid:" << tid << ", Name:" << threadName << std::endl;
270 }
271 std::string wchanPath = StringPrintf("%s/%d/wchan", PROC_SELF_TASK_PATH, tid);
272 std::string wchan;
273 if (OHOS::HiviewDFX::LoadStringFromFile(wchanPath, wchan)) {
274 ss << "wchan:" << wchan << std::endl;
275 } else {
276 ss << "Load thread wchan failed." << std::endl;
277 }
278 result = ss.str();
279 }
280 } // namespace HiviewDFX
281 } // namespace OHOS
282