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