1 /*
2 * Copyright (C) 2021 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 "executor/cpu_dumper.h"
16 #include <dirent.h>
17 #include "file_ex.h"
18 #include "datetime_ex.h"
19 #include "dump_utils.h"
20 #include "securec.h"
21 #include "util/string_utils.h"
22 namespace OHOS {
23 namespace HiviewDFX {
24 const std::string CPUDumper::LOAD_AVG_FILE_PATH = "/proc/loadavg";
25 const size_t CPUDumper::LOAD_AVG_INFO_COUNT = 3;
26 const int CPUDumper::TM_START_YEAR = 1900;
27 const std::string CPUDumper::PROC_STAT_FILE_PATH = "/proc/stat";
28 const useconds_t CPUDumper::DUMP_DELAY_MICROSECOND = 1 * 1000 * 1000;
29 const int CPUDumper::DEC_SYSTEM_VALUE = 10;
30 const int CPUDumper::PROC_CPU_LENGTH = 256;
31 const int CPUDumper::CPU_STAT_USER_TIME_INDEX = 1;
32 const int CPUDumper::CPU_STAT_NICE_TIME_INDEX = 2;
33 const int CPUDumper::CPU_STAT_SYS_TIME_INDEX = 3;
34 const int CPUDumper::CPU_STAT_IDLE_TIME_INDEX = 4;
35 const int CPUDumper::CPU_STAT_IOW_TIME_INDEX = 5;
36 const int CPUDumper::CPU_STAT_IRQ_TIME_INDEX = 6;
37 const int CPUDumper::CPU_STAT_SIRQ_TIME_INDEX = 7;
38 const long unsigned CPUDumper::HUNDRED_PERCENT_VALUE = 100;
39 const int CPUDumper::PROC_INFO_USER_TIME_INDEX = 13;
40 const int CPUDumper::PROC_INFO_SYS_TIME_INDEX = 14;
41 const int CPUDumper::PROC_INFO_MINOR_FAULT_INDEX = 9;
42 const int CPUDumper::PROC_INFO_MAJOR_FAULT_INDEX = 11;
43
CPUDumper()44 CPUDumper::CPUDumper()
45 {
46 }
47
~CPUDumper()48 CPUDumper::~CPUDumper()
49 {
50 }
51
PreExecute(const std::shared_ptr<DumperParameter> & parameter,StringMatrix dumpDatas)52 DumpStatus CPUDumper::PreExecute(const std::shared_ptr<DumperParameter> ¶meter, StringMatrix dumpDatas)
53 {
54 DUMPER_HILOGD(MODULE_COMMON, "debug|CPUDumper PreExecute");
55 dumpCPUDatas_ = dumpDatas;
56 isDumpCpuUsage_ = (parameter->GetOpts()).isDumpCpuUsage_;
57 cpuUsagePid_ = (parameter->GetOpts()).cpuUsagePid_;
58 return DumpStatus::DUMP_OK;
59 }
60
Execute()61 DumpStatus CPUDumper::Execute()
62 {
63 DUMPER_HILOGD(MODULE_COMMON, "debug|CPUDumper Execute");
64 DumpStatus ret = DumpStatus::DUMP_FAIL;
65 if (isDumpCpuUsage_) {
66 ret = DumpCpuUsageData();
67 } else {
68 return DumpStatus::DUMP_FAIL;
69 }
70 return ret;
71 }
72
AfterExecute()73 DumpStatus CPUDumper::AfterExecute()
74 {
75 curCPUInfo_.reset();
76 oldCPUInfo_.reset();
77 curProcs_.clear();
78 oldProcs_.clear();
79 curSpecProc_.reset();
80 oldSpecProc_.reset();
81 return DumpStatus::DUMP_OK;
82 }
83
DumpCpuUsageData()84 DumpStatus CPUDumper::DumpCpuUsageData()
85 {
86 GetDateAndTime(startTime_);
87 DumpStatus ret = ReadCPUInfo();
88 if (ret != DumpStatus::DUMP_OK) {
89 return DumpStatus::DUMP_FAIL;
90 }
91 ret = ReadProcInfo();
92 if (ret != DumpStatus::DUMP_OK) {
93 return DumpStatus::DUMP_FAIL;
94 }
95
96 usleep(DUMP_DELAY_MICROSECOND);
97 oldCPUInfo_ = std::move(curCPUInfo_);
98 oldProcs_.assign(curProcs_.begin(), curProcs_.end());
99 curProcs_.clear();
100
101 ret = ReadCPUInfo();
102 if (ret != DumpStatus::DUMP_OK) {
103 return DumpStatus::DUMP_FAIL;
104 }
105 ret = ReadProcInfo();
106 if (ret != DumpStatus::DUMP_OK) {
107 return DumpStatus::DUMP_FAIL;
108 }
109
110 std::string avgInfo;
111 ret = ReadLoadAvgInfo(LOAD_AVG_FILE_PATH, avgInfo);
112 if (ret != DumpStatus::DUMP_OK) {
113 DUMPER_HILOGD(MODULE_COMMON, "Get LoadAvgInfo failed!");
114 return DumpStatus::DUMP_FAIL;
115 }
116 AddStrLineToDumpInfo(avgInfo);
117
118 GetDateAndTime(endTime_);
119 std::string dumpTimeStr;
120 CreateDumpTimeString(startTime_, endTime_, dumpTimeStr);
121 AddStrLineToDumpInfo(dumpTimeStr);
122
123 std::string cpuStatStr;
124 CreateCPUStatString(cpuStatStr);
125 AddStrLineToDumpInfo(cpuStatStr);
126
127 DumpProcInfo();
128 return DumpStatus::DUMP_OK;
129 }
130
ReadLoadAvgInfo(const std::string & filePath,std::string & info)131 DumpStatus CPUDumper::ReadLoadAvgInfo(const std::string &filePath, std::string &info)
132 {
133 if (!FileExists(filePath)) {
134 return DumpStatus::DUMP_FAIL;
135 }
136
137 std::string rawData;
138 if (!LoadStringFromFile(filePath, rawData)) {
139 return DumpStatus::DUMP_FAIL;
140 }
141 DUMPER_HILOGD(MODULE_COMMON, "debug|rawData is %{public}s", rawData.c_str());
142 std::vector<std::string> vec;
143 SplitStr(rawData, DumpUtils::SPACE, vec);
144 info = "Load average:";
145 for (size_t i = 0; i < LOAD_AVG_INFO_COUNT; i++) {
146 info.append(" ");
147 info.append(vec[i]);
148 if (i == LOAD_AVG_INFO_COUNT - 1) {
149 info.append(";");
150 } else {
151 info.append(" /");
152 }
153 }
154 info.append(" the cpu load average in 1 min, 5 min and 15 min");
155 DUMPER_HILOGD(MODULE_COMMON, "debug|info is %{public}s", info.c_str());
156 return DumpStatus::DUMP_OK;
157 }
158
GetDateAndTime(std::string & dateTime)159 bool CPUDumper::GetDateAndTime(std::string &dateTime)
160 {
161 struct tm timeData = {0};
162 if (!GetSystemCurrentTime(&timeData)) {
163 return false;
164 }
165
166 dateTime = " ";
167 dateTime.append(std::to_string(TM_START_YEAR + timeData.tm_year));
168 dateTime.append("-");
169 if (1 + timeData.tm_mon < DEC_SYSTEM_VALUE) {
170 dateTime.append(std::to_string(0));
171 }
172 dateTime.append(std::to_string(1 + timeData.tm_mon));
173 dateTime.append("-");
174 if (timeData.tm_mday < DEC_SYSTEM_VALUE) {
175 dateTime.append(std::to_string(0));
176 }
177 dateTime.append(std::to_string(timeData.tm_mday));
178 dateTime.append(" ");
179 if (timeData.tm_hour < DEC_SYSTEM_VALUE) {
180 dateTime.append(std::to_string(0));
181 }
182 dateTime.append(std::to_string(timeData.tm_hour));
183 dateTime.append(":");
184 if (timeData.tm_min < DEC_SYSTEM_VALUE) {
185 dateTime.append(std::to_string(0));
186 }
187 dateTime.append(std::to_string(timeData.tm_min));
188 dateTime.append(":");
189 if (timeData.tm_sec < DEC_SYSTEM_VALUE) {
190 dateTime.append(std::to_string(0));
191 }
192 dateTime.append(std::to_string(timeData.tm_sec));
193 return true;
194 }
195
CreateDumpTimeString(const std::string & startTime,const std::string & endTime,std::string & timeStr)196 void CPUDumper::CreateDumpTimeString(const std::string &startTime, const std::string &endTime, std::string &timeStr)
197 {
198 timeStr = "CPU usage from";
199 timeStr.append(startTime);
200 timeStr.append(" to");
201 timeStr.append(endTime);
202 }
203
ReadCPUInfo()204 DumpStatus CPUDumper::ReadCPUInfo()
205 {
206 std::string statRawData;
207 if (!LoadStringFromFile(PROC_STAT_FILE_PATH, statRawData)) {
208 return DumpStatus::DUMP_FAIL;
209 }
210 size_t pos = statRawData.find_first_of("\n");
211 if (pos == std::string::npos) {
212 return DumpStatus::DUMP_FAIL;
213 }
214
215 statRawData = statRawData.substr(0, pos);
216 std::vector<std::string> cpuStates;
217 SplitStr(statRawData, DumpUtils::SPACE, cpuStates);
218 curCPUInfo_ = std::make_shared<CPUInfo>();
219 SetCPUInfo(curCPUInfo_->uTime, cpuStates[CPU_STAT_USER_TIME_INDEX]);
220 SetCPUInfo(curCPUInfo_->nTime, cpuStates[CPU_STAT_NICE_TIME_INDEX]);
221 SetCPUInfo(curCPUInfo_->sTime, cpuStates[CPU_STAT_SYS_TIME_INDEX]);
222 SetCPUInfo(curCPUInfo_->iTime, cpuStates[CPU_STAT_IDLE_TIME_INDEX]);
223 SetCPUInfo(curCPUInfo_->iowTime, cpuStates[CPU_STAT_IOW_TIME_INDEX]);
224 SetCPUInfo(curCPUInfo_->irqTime, cpuStates[CPU_STAT_IRQ_TIME_INDEX]);
225 SetCPUInfo(curCPUInfo_->sirqTime, cpuStates[CPU_STAT_SIRQ_TIME_INDEX]);
226 return DumpStatus::DUMP_OK;
227 }
228
SetCPUInfo(long unsigned & info,const std::string & strInfo)229 void CPUDumper::SetCPUInfo(long unsigned &info, const std::string &strInfo)
230 {
231 if (IsNumericStr(strInfo)) {
232 const int base = 10;
233 info = strtoul(strInfo.c_str(), nullptr, base);
234 } else {
235 DUMPER_HILOGE(MODULE_COMMON, "info is %{public}s not Numeric.", strInfo.c_str());
236 info = 0;
237 }
238 }
239
AddStrLineToDumpInfo(const std::string & strLine)240 void CPUDumper::AddStrLineToDumpInfo(const std::string &strLine)
241 {
242 std::vector<std::string> vec;
243 vec.push_back(strLine);
244 dumpCPUDatas_->push_back(vec);
245 }
246
CreateCPUStatString(std::string & str)247 void CPUDumper::CreateCPUStatString(std::string &str)
248 {
249 long unsigned totalDeltaTime = (curCPUInfo_->uTime + curCPUInfo_->nTime + curCPUInfo_->sTime + curCPUInfo_->iTime
250 + curCPUInfo_->iowTime + curCPUInfo_->irqTime + curCPUInfo_->sirqTime)
251 - (oldCPUInfo_->uTime + oldCPUInfo_->nTime + oldCPUInfo_->sTime + oldCPUInfo_->iTime
252 + oldCPUInfo_->iowTime + oldCPUInfo_->irqTime + oldCPUInfo_->sirqTime);
253
254 for (size_t i = 0; i < curProcs_.size(); i++) {
255 std::shared_ptr<ProcInfo> oldProc = GetOldProc(curProcs_[i]->pid);
256 if (oldProc) {
257 curProcs_[i]->userSpaceUsage =
258 (curProcs_[i]->uTime - oldProc->uTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
259 curProcs_[i]->sysSpaceUsage =
260 (curProcs_[i]->sTime - oldProc->sTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
261 curProcs_[i]->totalUsage = curProcs_[i]->userSpaceUsage + curProcs_[i]->sysSpaceUsage;
262 } else {
263 curProcs_[i]->userSpaceUsage = 0;
264 curProcs_[i]->sysSpaceUsage = 0;
265 curProcs_[i]->totalUsage = 0;
266 }
267 }
268
269 long unsigned userSpaceUsage =
270 ((curCPUInfo_->uTime + curCPUInfo_->nTime) - (oldCPUInfo_->uTime + oldCPUInfo_->nTime)) * HUNDRED_PERCENT_VALUE
271 / totalDeltaTime;
272 long unsigned sysSpaceUsage = (curCPUInfo_->sTime - oldCPUInfo_->sTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
273 long unsigned iowUsage = (curCPUInfo_->iowTime - oldCPUInfo_->iowTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
274 long unsigned irqUsage =
275 ((curCPUInfo_->irqTime + curCPUInfo_->sirqTime) - (oldCPUInfo_->irqTime + oldCPUInfo_->sirqTime))
276 * HUNDRED_PERCENT_VALUE / totalDeltaTime;
277 long unsigned idleUsage = (curCPUInfo_->iTime - oldCPUInfo_->iTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
278 long unsigned totalUsage = userSpaceUsage + sysSpaceUsage;
279
280 str = "Total: ";
281 str.append(std::to_string(totalUsage));
282 str.append("%; ");
283 str.append("User Space: ");
284 str.append(std::to_string(userSpaceUsage));
285 str.append("%; ");
286 str.append("Kernel Space: ");
287 str.append(std::to_string(sysSpaceUsage));
288 str.append("%; ");
289 str.append("iowait: ");
290 str.append(std::to_string(iowUsage));
291 str.append("%; ");
292 str.append("irq: ");
293 str.append(std::to_string(irqUsage));
294 str.append("%; ");
295 str.append("idle: ");
296 str.append(std::to_string(idleUsage));
297 str.append("%");
298 }
299
GetOldProc(const std::string & pid)300 std::shared_ptr<ProcInfo> CPUDumper::GetOldProc(const std::string &pid)
301 {
302 for (size_t i = 0; i < oldProcs_.size(); i++) {
303 if (StringUtils::GetInstance().IsSameStr(oldProcs_[i]->pid, pid)) {
304 return oldProcs_[i];
305 }
306 }
307 return nullptr;
308 }
309
ReadProcInfo()310 DumpStatus CPUDumper::ReadProcInfo()
311 {
312 std::vector<std::string> procFiles;
313 GetProcessDirFiles("/proc", "stat", procFiles);
314 if (procFiles.size() < 1) {
315 return DumpStatus::DUMP_FAIL;
316 }
317 for (size_t i = 0; i < procFiles.size(); i++) {
318 std::string rawData;
319 if (!LoadStringFromFile(procFiles[i], rawData)) {
320 continue;
321 }
322
323 std::shared_ptr<ProcInfo> ptrProcInfo = std::make_shared<ProcInfo>();
324 // Get comm name, (xxx) in stat file.
325 std::vector<std::string> comms;
326 GetSubStrBetween(rawData, "(", ")", comms);
327 ptrProcInfo->comm = comms[0];
328
329 /**
330 * @brief (xxx xxx xx) contain ' ' will effect function SplitStr,
331 * there will be wrong string for infos, so replace '()' instead of (xxx xxx xx)
332 */
333 rawData = ReplaceStr(rawData, comms[0], "()");
334 std::vector<std::string> procInfos;
335 SplitStr(rawData, DumpUtils::SPACE, procInfos);
336
337 ptrProcInfo->pid = procInfos[0];
338 SetCPUInfo(ptrProcInfo->uTime, procInfos[PROC_INFO_USER_TIME_INDEX]);
339 SetCPUInfo(ptrProcInfo->sTime, procInfos[PROC_INFO_SYS_TIME_INDEX]);
340 ptrProcInfo->minflt = procInfos[PROC_INFO_MINOR_FAULT_INDEX];
341 ptrProcInfo->majflt = procInfos[PROC_INFO_MAJOR_FAULT_INDEX];
342 curProcs_.push_back(ptrProcInfo);
343 }
344 if (curProcs_.size() < 1) {
345 return DumpStatus::DUMP_FAIL;
346 }
347 return DumpStatus::DUMP_OK;
348 }
349
GetProcessDirFiles(const std::string & path,const std::string & file,std::vector<std::string> & files)350 void CPUDumper::GetProcessDirFiles(const std::string &path, const std::string &file, std::vector<std::string> &files)
351 {
352 DIR *dir = opendir(path.c_str());
353 if (dir == nullptr) {
354 return;
355 }
356
357 if (cpuUsagePid_ != -1) {
358 /**
359 * @brief Dump cpu info of specific pid.
360 *
361 */
362 std::string filePath(path + "/" + std::to_string(cpuUsagePid_) + "/" + file);
363 files.push_back(filePath);
364 closedir(dir);
365 return;
366 }
367
368 while (true) {
369 struct dirent *pidDir = readdir(dir);
370 if (pidDir == nullptr) {
371 break;
372 }
373
374 if (!IsNumericStr(std::string(pidDir->d_name))) {
375 continue;
376 }
377
378 std::string filePath(path + "/" + std::string(pidDir->d_name) + "/" + file);
379 files.push_back(std::move(filePath));
380 }
381 closedir(dir);
382 }
383
DumpProcInfo()384 void CPUDumper::DumpProcInfo()
385 {
386 std::vector<std::shared_ptr<ProcInfo>> sortedInfos;
387 sortedInfos.assign(curProcs_.begin(), curProcs_.end());
388 std::sort(sortedInfos.begin(), sortedInfos.end(), SortProcInfo);
389
390 AddStrLineToDumpInfo("Details of Processes:");
391 AddStrLineToDumpInfo(" PID Total Usage User Space Kernel Space Page Fault Minor"
392 " Page Fault Major Name");
393 for (size_t i = 0; i < sortedInfos.size(); i++) {
394 char format[PROC_CPU_LENGTH] = {0};
395 int ret = sprintf_s(format, PROC_CPU_LENGTH,
396 " %-5s %3d%% %3d%%"
397 " %3d%% %8s %8s %-15s",
398 (sortedInfos[i]->pid).c_str(), sortedInfos[i]->totalUsage,
399 sortedInfos[i]->userSpaceUsage, sortedInfos[i]->sysSpaceUsage,
400 (sortedInfos[i]->minflt).c_str(), (sortedInfos[i]->majflt).c_str(),
401 (sortedInfos[i]->comm).c_str());
402 if (ret < 0) {
403 continue;
404 }
405 AddStrLineToDumpInfo(std::string(format));
406 }
407 }
408
SortProcInfo(std::shared_ptr<ProcInfo> & left,std::shared_ptr<ProcInfo> & right)409 bool CPUDumper::SortProcInfo(std::shared_ptr<ProcInfo> &left, std::shared_ptr<ProcInfo> &right)
410 {
411 if (right->totalUsage != left->totalUsage) {
412 return right->totalUsage < left->totalUsage;
413 }
414 if (right->userSpaceUsage != left->userSpaceUsage) {
415 return right->userSpaceUsage < left->userSpaceUsage;
416 }
417 if (right->sysSpaceUsage != left->sysSpaceUsage) {
418 return right->sysSpaceUsage < left->sysSpaceUsage;
419 }
420 if (right->pid.length() != left->pid.length()) {
421 return right->pid.length() < left->pid.length();
422 }
423 return (right->pid.compare(left->pid) < 0);
424 }
425
GetCpuUsage(int pid)426 float CPUDumper::GetCpuUsage(int pid)
427 {
428 DumpStatus ret = ReadCPUInfo();
429 if (ret != DumpStatus::DUMP_OK) {
430 return 0;
431 }
432 ret = ReadSpecProcInfo(pid);
433 if (ret != DumpStatus::DUMP_OK) {
434 return 0;
435 }
436
437 usleep(DUMP_DELAY_MICROSECOND);
438 oldCPUInfo_ = std::move(curCPUInfo_);
439 oldSpecProc_ = std::move(curSpecProc_);
440
441 ret = ReadCPUInfo();
442 if (ret != DumpStatus::DUMP_OK) {
443 return 0;
444 }
445 ret = ReadSpecProcInfo(pid);
446 if (ret != DumpStatus::DUMP_OK) {
447 return 0;
448 }
449
450 long unsigned totalDeltaTime = (curCPUInfo_->uTime + curCPUInfo_->nTime + curCPUInfo_->sTime + curCPUInfo_->iTime
451 + curCPUInfo_->iowTime + curCPUInfo_->irqTime + curCPUInfo_->sirqTime)
452 - (oldCPUInfo_->uTime + oldCPUInfo_->nTime + oldCPUInfo_->sTime + oldCPUInfo_->iTime
453 + oldCPUInfo_->iowTime + oldCPUInfo_->irqTime + oldCPUInfo_->sirqTime);
454
455 if (totalDeltaTime != 0) {
456 return (curSpecProc_->uTime - oldSpecProc_->uTime)
457 + (curSpecProc_->sTime - oldSpecProc_->sTime) / totalDeltaTime;
458 } else {
459 return -1;
460 }
461 }
462
ReadSpecProcInfo(int pid)463 DumpStatus CPUDumper::ReadSpecProcInfo(int pid)
464 {
465 std::string filePath = "/proc/" + std::to_string(pid) + "/stat";
466 std::string rawData;
467 if (!LoadStringFromFile(filePath, rawData)) {
468 return DumpStatus::DUMP_FAIL;
469 }
470
471 curSpecProc_ = std::make_shared<ProcInfo>();
472 // Get comm name, (xxx) in stat file.
473 std::vector<std::string> comms;
474 GetSubStrBetween(rawData, "(", ")", comms);
475 curSpecProc_->comm = comms[0];
476
477 /**
478 * @brief (xxx xxx xx) contain ' ' will effect function SplitStr,
479 * there will be wrong string for infos, so replace '()' instead of (xxx xxx xx)
480 */
481 rawData = ReplaceStr(rawData, comms[0], "()");
482 std::vector<std::string> procInfos;
483 SplitStr(rawData, DumpUtils::SPACE, procInfos);
484
485 curSpecProc_->pid = procInfos[0];
486 SetCPUInfo(curSpecProc_->uTime, procInfos[PROC_INFO_USER_TIME_INDEX]);
487 SetCPUInfo(curSpecProc_->sTime, procInfos[PROC_INFO_SYS_TIME_INDEX]);
488 curSpecProc_->minflt = procInfos[PROC_INFO_MINOR_FAULT_INDEX];
489 curSpecProc_->majflt = procInfos[PROC_INFO_MAJOR_FAULT_INDEX];
490
491 return DumpStatus::DUMP_OK;
492 }
493 } // namespace HiviewDFX
494 } // namespace OHOS
495