1 /*
2 * Copyright (c) 2021-2022 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 #include "util/dump_cpu_info_util.h"
16 #include <dirent.h>
17 #include <unistd.h>
18 #include <chrono>
19 #include <mutex>
20 #include "string_ex.h"
21 #include "hilog_wrapper.h"
22 #include "datetime_ex.h"
23 #include "util/file_utils.h"
24
25 namespace OHOS {
26 namespace HiviewDFX {
27 const std::string DumpCpuInfoUtil::PROC_STAT_PATH = "/proc/stat";
28 const std::string DumpCpuInfoUtil::SPACE = " ";
29 const int DumpCpuInfoUtil::TM_START_YEAR = 1900;
30 const int DumpCpuInfoUtil::DEC_SYSTEM_VALUE = 10;
31
DumpCpuInfoUtil()32 DumpCpuInfoUtil::DumpCpuInfoUtil()
33 {
34 DUMPER_HILOGD(MODULE_COMMON, "create debug|");
35 curCPUInfo_ = std::make_shared<CPUInfo>();
36 oldCPUInfo_ = std::make_shared<CPUInfo>();
37 }
38
~DumpCpuInfoUtil()39 DumpCpuInfoUtil::~DumpCpuInfoUtil()
40 {
41 DUMPER_HILOGD(MODULE_COMMON, "release debug|");
42 curCPUInfo_.reset();
43 oldCPUInfo_.reset();
44 curProcs_.clear();
45 oldProcs_.clear();
46 }
47
UpdateCpuInfo()48 void DumpCpuInfoUtil::UpdateCpuInfo()
49 {
50 DUMPER_HILOGI(MODULE_COMMON, "UpdateCpuInfo debug|enter");
51 static std::mutex mutexUpdateCpu;
52 std::unique_lock<std::mutex> lock(mutexUpdateCpu);
53 CopyCpuInfo(oldCPUInfo_, curCPUInfo_);
54 GetCurCPUInfo(curCPUInfo_);
55 oldProcs_.assign(curProcs_.begin(), curProcs_.end());
56 GetCurProcInfo(curProcs_);
57 GetDateAndTime(startTime_);
58 DUMPER_HILOGI(MODULE_COMMON, "UpdateCpuInfo debug|exit");
59 }
60
GetCurCPUInfo(std::shared_ptr<CPUInfo> & cpuInfo)61 bool DumpCpuInfoUtil::GetCurCPUInfo(std::shared_ptr<CPUInfo> &cpuInfo)
62 {
63 if (cpuInfo == nullptr) {
64 return false;
65 }
66 std::string statRawData;
67 auto& fileInstance = FileUtils::GetInstance();
68 bool ret = fileInstance.LoadStringFromProcCb(PROC_STAT_PATH, false, false, [&](const std::string& line) -> void {
69 statRawData += line;
70 });
71 if (!ret) {
72 return false;
73 }
74 size_t pos = statRawData.find_first_of("\n");
75 if (pos == std::string::npos) {
76 return false;
77 }
78
79 statRawData = statRawData.substr(0, pos);
80 std::vector<std::string> cpuStates;
81 SplitStr(statRawData, SPACE, cpuStates);
82 if (cpuStates.size() <= static_cast<size_t>(CPU_STAT_SIRQ_TIME_INDEX)) {
83 return false;
84 }
85
86 SetCPUInfo(cpuInfo->uTime, cpuStates[CPU_STAT_USER_TIME_INDEX]);
87 SetCPUInfo(cpuInfo->nTime, cpuStates[CPU_STAT_NICE_TIME_INDEX]);
88 SetCPUInfo(cpuInfo->sTime, cpuStates[CPU_STAT_SYS_TIME_INDEX]);
89 SetCPUInfo(cpuInfo->iTime, cpuStates[CPU_STAT_IDLE_TIME_INDEX]);
90 SetCPUInfo(cpuInfo->iowTime, cpuStates[CPU_STAT_IOW_TIME_INDEX]);
91 SetCPUInfo(cpuInfo->irqTime, cpuStates[CPU_STAT_IRQ_TIME_INDEX]);
92 SetCPUInfo(cpuInfo->sirqTime, cpuStates[CPU_STAT_SIRQ_TIME_INDEX]);
93 return true;
94 }
95
SetCPUInfo(long unsigned & info,const std::string & strInfo)96 void DumpCpuInfoUtil::SetCPUInfo(long unsigned &info, const std::string &strInfo)
97 {
98 if (IsNumericStr(strInfo)) {
99 const int base = CONSTANT_NUM_10;
100 info = strtoul(strInfo.c_str(), nullptr, base);
101 } else {
102 info = 0;
103 }
104 }
105
GetCurProcInfo(std::vector<std::shared_ptr<ProcInfo>> & procInfos)106 bool DumpCpuInfoUtil::GetCurProcInfo(std::vector<std::shared_ptr<ProcInfo>> &procInfos)
107 {
108 std::vector<std::string> procFiles;
109 GetProcessDirFiles("/proc", "stat", procFiles);
110 if (procFiles.size() < 1) {
111 return false;
112 }
113 // Set procInfos size 0
114 procInfos.clear();
115 auto& fileInstance = FileUtils::GetInstance();
116 for (size_t i = 0; i < procFiles.size(); i++) {
117 std::string rawData;
118 bool ret = fileInstance.LoadStringFromProcCb(procFiles[i], false, false, [&](const std::string& line) -> void {
119 rawData += line;
120 });
121 if (!ret) {
122 continue;
123 }
124
125 // Get comm name, (xxx) in stat file.
126 std::vector<std::string> comms;
127 GetSubStrBetween(rawData, "(", ")", comms);
128 if (comms.empty()) {
129 continue;
130 }
131
132 /**
133 * @brief (xxx xxx xx) contain ' ' will effect function SplitStr,
134 * there will be wrong string for infos, so replace '()' instead of (xxx xxx xx)
135 */
136 rawData = ReplaceStr(rawData, comms[0], "()");
137 std::vector<std::string> procInfosStr;
138 SplitStr(rawData, SPACE, procInfosStr);
139 if (procInfosStr.size() <= static_cast<size_t>(PROC_INFO_SYS_TIME_INDEX)) {
140 continue;
141 }
142
143 std::shared_ptr<ProcInfo> ptrProcInfo = std::make_shared<ProcInfo>();
144 ptrProcInfo->comm = comms[0];
145 ptrProcInfo->pid = procInfosStr[0];
146 SetCPUInfo(ptrProcInfo->uTime, procInfosStr[PROC_INFO_USER_TIME_INDEX]);
147 SetCPUInfo(ptrProcInfo->sTime, procInfosStr[PROC_INFO_SYS_TIME_INDEX]);
148 ptrProcInfo->minflt = procInfosStr[PROC_INFO_MINOR_FAULT_INDEX];
149 ptrProcInfo->majflt = procInfosStr[PROC_INFO_MAJOR_FAULT_INDEX];
150 procInfos.push_back(ptrProcInfo);
151 }
152 return true;
153 }
154
GetProcessDirFiles(const std::string & path,const std::string & file,std::vector<std::string> & files)155 void DumpCpuInfoUtil::GetProcessDirFiles(const std::string &path, const std::string &file,
156 std::vector<std::string> &files)
157 {
158 DIR *dir = opendir(path.c_str());
159 if (dir == nullptr) {
160 return;
161 }
162
163 while (true) {
164 struct dirent *pidDir = readdir(dir);
165 if (pidDir == nullptr) {
166 break;
167 }
168
169 if (!IsNumericStr(std::string(pidDir->d_name))) {
170 continue;
171 }
172
173 std::string filePath(path + "/" + std::string(pidDir->d_name) + "/" + file);
174 files.push_back(std::move(filePath));
175 }
176 closedir(dir);
177 }
178
GetCurSpecProcInfo(int pid,std::shared_ptr<ProcInfo> & specProc)179 bool DumpCpuInfoUtil::GetCurSpecProcInfo(int pid, std::shared_ptr<ProcInfo> &specProc)
180 {
181 if (specProc == nullptr) {
182 return false;
183 }
184
185 std::string path = "/proc/" + std::to_string(pid) + "/stat";
186 std::string rawData;
187 bool ret = FileUtils::GetInstance().LoadStringFromProcCb(path, false, false, [&](const std::string& line) -> void {
188 rawData += line;
189 });
190 if (!ret) {
191 return false;
192 }
193
194 // Get comm name, (xxx) in stat file.
195 std::vector<std::string> comms;
196 GetSubStrBetween(rawData, "(", ")", comms);
197 if (comms.empty()) {
198 return false;
199 }
200
201 /**
202 * @brief (xxx xxx xx) contain ' ' will effect function SplitStr,
203 * there will be wrong string for infos, so replace '()' instead of (xxx xxx xx)
204 */
205 rawData = ReplaceStr(rawData, comms[0], "()");
206 std::vector<std::string> procInfosStr;
207 SplitStr(rawData, SPACE, procInfosStr);
208 if (procInfosStr.size() <= static_cast<size_t>(PROC_INFO_SYS_TIME_INDEX)) {
209 return false;
210 }
211
212 specProc->comm = comms[0];
213 specProc->pid = procInfosStr[0];
214 SetCPUInfo(specProc->uTime, procInfosStr[PROC_INFO_USER_TIME_INDEX]);
215 SetCPUInfo(specProc->sTime, procInfosStr[PROC_INFO_SYS_TIME_INDEX]);
216 specProc->minflt = procInfosStr[PROC_INFO_MINOR_FAULT_INDEX];
217 specProc->majflt = procInfosStr[PROC_INFO_MAJOR_FAULT_INDEX];
218 return true;
219 }
220
GetOldCPUInfo(std::shared_ptr<CPUInfo> & cpuInfo)221 bool DumpCpuInfoUtil::GetOldCPUInfo(std::shared_ptr<CPUInfo> &cpuInfo)
222 {
223 if (!CheckFrequentDumpping()) {
224 CopyCpuInfo(cpuInfo, curCPUInfo_);
225 } else {
226 CopyCpuInfo(cpuInfo, oldCPUInfo_);
227 }
228 return true;
229 }
230
GetOldProcInfo(std::vector<std::shared_ptr<ProcInfo>> & procInfos)231 bool DumpCpuInfoUtil::GetOldProcInfo(std::vector<std::shared_ptr<ProcInfo>> &procInfos)
232 {
233 if (!CheckFrequentDumpping()) {
234 procInfos.assign(curProcs_.begin(), curProcs_.end());
235 } else {
236 procInfos.assign(oldProcs_.begin(), oldProcs_.end());
237 }
238 return true;
239 }
240
GetOldSpecProcInfo(int pid,std::shared_ptr<ProcInfo> & specProc)241 bool DumpCpuInfoUtil::GetOldSpecProcInfo(int pid, std::shared_ptr<ProcInfo> &specProc)
242 {
243 if (!CheckFrequentDumpping()) {
244 for (size_t i = 0; i < curProcs_.size(); i++) {
245 if (curProcs_[i] == nullptr) {
246 continue;
247 }
248 if (!IsNumericStr(curProcs_[i]->pid)) {
249 return false;
250 }
251 int specPid = 0;
252 if (!StrToInt(curProcs_[i]->pid, specPid)) {
253 return false;
254 }
255 if (pid == specPid) {
256 CopyProcInfo(specProc, curProcs_[i]);
257 return true;
258 }
259 }
260 } else {
261 for (size_t i = 0; i < oldProcs_.size(); i++) {
262 if (oldProcs_[i] == nullptr) {
263 continue;
264 }
265 if (!IsNumericStr(oldProcs_[i]->pid)) {
266 return false;
267 }
268 int specPid = 0;
269 if (!StrToInt(oldProcs_[i]->pid, specPid)) {
270 return false;
271 }
272 if (pid == specPid) {
273 CopyProcInfo(specProc, oldProcs_[i]);
274 return true;
275 }
276 }
277 }
278 return false;
279 }
280
CopyCpuInfo(std::shared_ptr<CPUInfo> & tar,const std::shared_ptr<CPUInfo> & source)281 void DumpCpuInfoUtil::CopyCpuInfo(std::shared_ptr<CPUInfo> &tar, const std::shared_ptr<CPUInfo> &source)
282 {
283 if ((tar == nullptr) || (source == nullptr)) {
284 return;
285 }
286
287 tar->uTime = source->uTime;
288 tar->nTime = source->nTime;
289 tar->sTime = source->sTime;
290 tar->iTime = source->iTime;
291 tar->iowTime = source->iowTime;
292 tar->irqTime = source->irqTime;
293 tar->sirqTime = source->sirqTime;
294 }
295
CopyProcInfo(std::shared_ptr<ProcInfo> & tar,const std::shared_ptr<ProcInfo> & source)296 void DumpCpuInfoUtil::CopyProcInfo(std::shared_ptr<ProcInfo> &tar, const std::shared_ptr<ProcInfo> &source)
297 {
298 if ((tar == nullptr) || (source == nullptr)) {
299 return;
300 }
301
302 tar->pid = source->pid;
303 tar->comm = source->comm;
304 tar->uTime = source->uTime;
305 tar->sTime = source->sTime;
306 tar->minflt = source->minflt;
307 tar->majflt = source->majflt;
308 }
309
CheckFrequentDumpping()310 bool DumpCpuInfoUtil::CheckFrequentDumpping()
311 {
312 time_t curTime;
313 struct tm ptrCurtime = {0};
314 (void)time(&curTime);
315 gmtime_r(&curTime, &ptrCurtime);
316 int interval = ptrCurtime.tm_sec - dumpTimeSec_;
317 if (interval > 0 && interval < DUMP_TIME_INTERVAL) {
318 return true;
319 }
320 dumpTimeSec_ = ptrCurtime.tm_sec;
321 return false;
322 }
323
IsNeedRefreshCpu()324 bool DumpCpuInfoUtil::IsNeedRefreshCpu()
325 {
326 std::chrono::milliseconds ms = std::chrono::duration_cast< std::chrono::milliseconds >(
327 std::chrono::system_clock::now().time_since_epoch());
328 if (lastCpuTimeMs_ > 0 && (ms.count() - lastCpuTimeMs_) < CUP_REFRESH_MIN_TIME) {
329 return true;
330 }
331 lastCpuTimeMs_ = ms.count();
332 return false;
333 }
334
GetCpuUsage(const int pid)335 float DumpCpuInfoUtil::GetCpuUsage(const int pid)
336 {
337 std::shared_ptr<CPUInfo> oldCPUInfo = std::make_shared<CPUInfo>();
338 if (!GetCurCPUInfo(oldCPUInfo)) {
339 return false;
340 }
341 std::shared_ptr<ProcInfo> oldSpecProc = std::make_shared<ProcInfo>();
342 if (!GetCurSpecProcInfo(pid, oldSpecProc)) {
343 return false;
344 }
345
346 sleep(1);
347
348 std::shared_ptr<CPUInfo> curCPUInfo = std::make_shared<CPUInfo>();
349 if (!GetCurCPUInfo(curCPUInfo)) {
350 return false;
351 }
352 std::shared_ptr<ProcInfo> curSpecProc = std::make_shared<ProcInfo>();
353 if (!GetCurSpecProcInfo(pid, curSpecProc)) {
354 return false;
355 }
356
357 long unsigned totalDeltaTime = (curCPUInfo->uTime + curCPUInfo->nTime + curCPUInfo->sTime + curCPUInfo->iTime
358 + curCPUInfo->iowTime + curCPUInfo->irqTime + curCPUInfo->sirqTime)
359 - (oldCPUInfo->uTime + oldCPUInfo->nTime + oldCPUInfo->sTime + oldCPUInfo->iTime
360 + oldCPUInfo->iowTime + oldCPUInfo->irqTime + oldCPUInfo->sirqTime);
361
362 if (totalDeltaTime != 0) {
363 float cpuUsage = static_cast<float>((curSpecProc->uTime - oldSpecProc->uTime)
364 + (curSpecProc->sTime - oldSpecProc->sTime)) / static_cast<float>(totalDeltaTime);
365 return cpuUsage;
366 } else {
367 return 0;
368 }
369 }
370
GetUpdateCpuStartTime(std::string & dateTime)371 void DumpCpuInfoUtil::GetUpdateCpuStartTime(std::string& dateTime)
372 {
373 dateTime = startTime_;
374 }
375
GetDateAndTime(std::string & dateTime)376 bool DumpCpuInfoUtil::GetDateAndTime(std::string &dateTime)
377 {
378 struct tm timeData = {0};
379 if (!GetSystemCurrentTime(&timeData)) {
380 return false;
381 }
382
383 dateTime = " ";
384 dateTime.append(std::to_string(TM_START_YEAR + timeData.tm_year));
385 dateTime.append("-");
386 if (1 + timeData.tm_mon < DEC_SYSTEM_VALUE) {
387 dateTime.append(std::to_string(0));
388 }
389 dateTime.append(std::to_string(1 + timeData.tm_mon));
390 dateTime.append("-");
391 if (timeData.tm_mday < DEC_SYSTEM_VALUE) {
392 dateTime.append(std::to_string(0));
393 }
394 dateTime.append(std::to_string(timeData.tm_mday));
395 dateTime.append(" ");
396 if (timeData.tm_hour < DEC_SYSTEM_VALUE) {
397 dateTime.append(std::to_string(0));
398 }
399 dateTime.append(std::to_string(timeData.tm_hour));
400 dateTime.append(":");
401 if (timeData.tm_min < DEC_SYSTEM_VALUE) {
402 dateTime.append(std::to_string(0));
403 }
404 dateTime.append(std::to_string(timeData.tm_min));
405 dateTime.append(":");
406 if (timeData.tm_sec < DEC_SYSTEM_VALUE) {
407 dateTime.append(std::to_string(0));
408 }
409 dateTime.append(std::to_string(timeData.tm_sec));
410 return true;
411 }
412
413 } // namespace HiviewDFX
414 } // namespace OHOS
415