1 /*
2 * Copyright (c) 2025 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 "proc_util.h"
17
18 #include <algorithm>
19 #include <fcntl.h>
20 #include <string>
21 #include <unistd.h>
22
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #include "dfx_define.h"
27 #include "dfx_log.h"
28 #include "dfx_util.h"
29 #include "securec.h"
30 #include "smart_fd.h"
31
32 #ifdef LOG_DOMAIN
33 #undef LOG_DOMAIN
34 #define LOG_DOMAIN 0xD002D11
35 #endif
36
37 #ifdef LOG_TAG
38 #undef LOG_TAG
39 #define LOG_TAG "ProcUtil"
40 #endif
41
42 namespace OHOS {
43 namespace HiviewDFX {
ParseSchedstat(const std::string & schedstatPath)44 bool Schedstat::ParseSchedstat(const std::string& schedstatPath)
45 {
46 char realPath[PATH_MAX] = {0};
47 if (realpath(schedstatPath.c_str(), realPath) == nullptr) {
48 DFXLOGE("path invalid. %{public}s", schedstatPath.c_str());
49 return false;
50 }
51
52 SmartFd fd(open(realPath, O_RDONLY));
53 if (!fd) {
54 DFXLOGE("open %{public}s failed. %{public}d", schedstatPath.c_str(), errno);
55 return false;
56 }
57
58 std::string line;
59 if (!ReadFdToString(fd.GetFd(), line)) {
60 DFXLOGE("Schedstat::FromProc read failed.");
61 return false;
62 }
63
64 constexpr int fieldCnt = 3;
65 int parsed = sscanf_s(line.c_str(),
66 "%" PRIu64 " " // run time(1)
67 "%" PRIu64 " " // wait time(2)
68 "%" PRIu64, // timeslices(3)
69 &runTime_, &waitTime_, ×licesNum_);
70
71 if (parsed != fieldCnt) {
72 DFXLOGE("Schedstat::FromProc parser failed.");
73 Reset();
74 return false;
75 }
76
77 return true;
78 }
79
ToString() const80 std::string Schedstat::ToString() const
81 {
82 // schedstat={178500, 54250, 3}
83 return "schedstat={" +
84 std::to_string(runTime_) + ", " +
85 std::to_string(waitTime_) + ", " +
86 std::to_string(timeslicesNum_) +
87 "}";
88 }
89
Reset()90 void Schedstat::Reset()
91 {
92 runTime_ = 0;
93 waitTime_ = 0;
94 timeslicesNum_ = 0;
95 }
96
ParserThreadInfo(pid_t tid)97 bool ThreadInfo::ParserThreadInfo(pid_t tid)
98 {
99 std::string schedstatPath = std::string(PROC_SELF_TASK_PATH) + "/" + std::to_string(tid) + "/schedstat";
100 if (!schedstat_.ParseSchedstat(schedstatPath) && tid == getpid()) {
101 schedstatPath = std::string("/proc/self/schedstat");
102 schedstat_.ParseSchedstat(schedstatPath);
103 }
104
105 ProcessInfo info;
106 if (!ParseProcInfo(tid, info)) {
107 return false;
108 }
109
110 state_ = info.state;
111 utime_ = info.utime;
112 stime_ = info.stime;
113 cutime_ = info.cutime;
114 return true;
115 }
116
ThreadStateToString(ThreadState state)117 std::string ThreadStateToString(ThreadState state)
118 {
119 switch (state) {
120 case ThreadState::RUNNING:
121 return "RUNNING";
122 case ThreadState::SLEEP:
123 return "SLEEP";
124 case ThreadState::DISK_SLEEP:
125 return "DISK_SLEEP";
126 case ThreadState::ZOMBIE:
127 return "ZOMBIE";
128 case ThreadState::STOPPED:
129 return "STOPPED";
130 case ThreadState::TRACING_STOP:
131 return "TRACING_STOP";
132 case ThreadState::DEAD_2_6:
133 case ThreadState::DEAD:
134 return "DEAD";
135 case ThreadState::WAKEKILL:
136 return "WAKEKILL";
137 case ThreadState::WAKING:
138 return "WAKING";
139 case ThreadState::PARKED:
140 return "PARKED";
141 case ThreadState::IDLE:
142 return "IDLE";
143 default:
144 return "Unknown";
145 }
146 }
147
ToString() const148 std::string ThreadInfo::ToString() const
149 {
150 // state=SLEEP, utime=1, stime=3, cutime=0, schedstat={178500, 54250, 3}
151 return "state=" + ThreadStateToString(state_) + ", " +
152 "utime=" + std::to_string(utime_) + ", " +
153 "stime=" + std::to_string(stime_) + ", " +
154 "cutime=" + std::to_string(cutime_) + ", " +
155 schedstat_.ToString();
156 }
157
ParseStatSubString(const std::string & statSubStr,ProcessInfo & info)158 static bool ParseStatSubString(const std::string& statSubStr, ProcessInfo& info)
159 {
160 int parsed = sscanf_s(statSubStr.c_str(),
161 ") %c " // state(3)
162 "%d " // ppid(4)
163 "%*d%*d%*d%*d" // skip pgrp(5), session(6), tty(7), tpgid(8)
164 "%*u%*u%*u%*u%*u" // skip flags(9), minflt(10), cminflt(11), majflt(12), cmajflt(13)
165 "%" PRIu64 " " // utime(14)
166 "%" PRIu64 " " // stime(15)
167 "%" PRIi64 " " // cutime(16)
168 "%" PRIi64 " " // cstime(17)
169 "%" PRIi64 " " // priority(18)
170 "%" PRIi64 " " // nice(19)
171 "%" PRIi64 " " // num_threads(20)
172 "%*ld " // skip itrealcalue(21)
173 "%" PRIu64 " " // starttime(22)
174 "%*lu " // skip vsize(23)
175 "%d " // rss(24)
176 "%*lu%*lu%*lu%*lu" // skip rsslim(25), startcode(26), endcode(27), startstack(28)
177 "%*lu%*lu" // skip kstkesp(29), kstkeip(30)
178 "%" PRIu64 " " // signal(31)
179 "%" PRIu64 " " // blocked(32)
180 "%" PRIu64 " " // sigignore(33)
181 "%" PRIu64, // sigcatch(34)
182 &info.state, 1, &info.ppid, &info.utime, &info.stime, &info.cutime, &info.cstime, &info.priority,
183 &info.nice, &info.numThreads, &info.starttime, &info.rss, &info.signal, &info.blocked,
184 &info.sigignore, &info.sigcatch
185 );
186
187 constexpr int fieldCnt = 15;
188 if (parsed != fieldCnt) {
189 DFXLOGE("parser field failed. cnt %{public}d", parsed);
190 return false;
191 }
192 return true;
193 }
194
ParseStatFromString(const std::string & statStr,ProcessInfo & info)195 static bool ParseStatFromString(const std::string& statStr, ProcessInfo& info)
196 {
197 size_t commStart = statStr.find_first_of("(");
198 size_t commEnd = statStr.find_last_of(")");
199 if (commStart == std::string::npos || commEnd == std::string::npos ||
200 commStart >= commEnd || commEnd - commStart > TASK_COMM_LEN + 1) {
201 DFXLOGE("parser comm error. %{public}zu", commEnd - commStart);
202 return false;
203 }
204
205 // pid(1)
206 int parsed = sscanf_s(statStr.c_str(), "%d (", &info.pid);
207 if (parsed != 1) {
208 DFXLOGE("parser pid failed.");
209 return false;
210 }
211
212 // comm(2)
213 std::string comm = statStr.substr(commStart + 1, commEnd - commStart - 1);
214 if (memcpy_s(info.comm, TASK_COMM_LEN, comm.c_str(), comm.length()) != EOK) {
215 DFXLOGE("Failed to copy comm.");
216 return false;
217 }
218 return ParseStatSubString(statStr.substr(commEnd), info);
219 }
220
ParseStat(const std::string & statPath,ProcessInfo & info)221 bool ParseStat(const std::string& statPath, ProcessInfo& info)
222 {
223 char realPath[PATH_MAX] = {0};
224 if (realpath(statPath.c_str(), realPath) == nullptr) {
225 DFXLOGE("path invalid. %{public}s", statPath.c_str());
226 return false;
227 }
228
229 SmartFd fd(open(realPath, O_RDONLY));
230 if (!fd) {
231 DFXLOGE("open %{public}s failed. %{public}d", statPath.c_str(), errno);
232 return false;
233 }
234
235 std::string line;
236 if (!ReadFdToString(fd.GetFd(), line)) {
237 DFXLOGE("read file %{public}s failed.", statPath.c_str());
238 return false;
239 }
240
241 return ParseStatFromString(line, info);
242 }
243
ParseProcInfo(pid_t pid,ProcessInfo & info)244 bool ParseProcInfo(pid_t pid, ProcessInfo& info)
245 {
246 std::string path = "/proc/self/task/" + std::to_string(pid) + "/stat";
247 if (ParseStat(path, info)) {
248 return true;
249 }
250 path = "/proc/" + std::to_string(pid) + "/stat";
251 return ParseStat(path, info);
252 }
253
GetFirstNumberSeq(const std::string & cont)254 std::string GetFirstNumberSeq(const std::string& cont)
255 {
256 auto start = std::find_if(cont.begin(), cont.end(), isdigit);
257 if (start == cont.end()) {
258 return "";
259 }
260 auto end = std::find_if_not(start, cont.end(), isdigit);
261 return std::string(start, end);
262 }
263
GetUidAndSigBlk(pid_t pid,long & uid,uint64_t & sigBlk)264 bool GetUidAndSigBlk(pid_t pid, long& uid, uint64_t& sigBlk)
265 {
266 std::string procPath = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(pid) + "/status";
267 FILE *fp = fopen(procPath.c_str(), "r");
268 if (fp == nullptr) {
269 return false;
270 }
271 char line[256] = {0}; // 256 : read status line is enough
272 std::string uidStr;
273 std::string sigBlkStr;
274 while (fgets(line, sizeof(line) - 1, fp) != nullptr) {
275 std::string strLine(line);
276 if (uidStr.empty() && strLine.find("Uid:") != std::string::npos) {
277 uidStr = GetFirstNumberSeq(strLine);
278 } else if (strLine.find("SigBlk:") != std::string::npos) {
279 sigBlkStr = GetFirstNumberSeq(strLine);
280 break;
281 }
282 }
283 (void)fclose(fp);
284
285 if (uidStr.empty() || sigBlkStr.empty()) {
286 return false;
287 }
288
289 char* end;
290 uid = strtol(uidStr .c_str(), &end, 10); // 10 : Uid is in decimal format
291 if (errno == ERANGE || *end != '\0') {
292 return false;
293 }
294
295 sigBlk = strtoull(sigBlkStr.c_str(), &end, 16); // 16 : SigBlk is in hex format
296 if (errno == ERANGE || *end != '\0') {
297 return false;
298 }
299 return true;
300 }
301
IsSigDumpMask(uint64_t sigBlk)302 bool IsSigDumpMask(uint64_t sigBlk)
303 {
304 constexpr uint64_t sigDumpMask = UINT64_C(1) << 34; // 34 : SIGDUMP signal is the 35th bit (0-indexed)
305 return (sigBlk & sigDumpMask) != 0;
306 }
307 } // namespace HiviewDFX
308 } // namespace OHOS
309