• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_, &timeslicesNum_);
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