• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &parameter, 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