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
32namespace OHOS {
33namespace HiviewDFX {
34namespace {
35const char PID_STR_NAME[] = "Pid:";
36const char PPID_STR_NAME[] = "PPid:";
37const char NSPID_STR_NAME[] = "NSpid:";
38const int ARGS_COUNT_ONE = 1;
39const int ARGS_COUNT_TWO = 2;
40const int STATUS_LINE_SIZE = 1024;
41}
42
43static 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
98bool 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
113bool 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
122bool GetProcStatus(struct ProcInfo& procInfo)
123{
124    return GetProcStatusByPath(procInfo, PROC_SELF_STATUS_PATH);
125}
126
127bool 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
138bool 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
164bool 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
185std::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
204void 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
212void 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
220void 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
233void 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
248void 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
295void 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