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 <unistd.h>
16 #include "executor/cpu_dumper.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 int CPUDumper::DEC_SYSTEM_VALUE = 10;
28 const int CPUDumper::PROC_CPU_LENGTH = 256;
29 const long unsigned CPUDumper::HUNDRED_PERCENT_VALUE = 100;
30
CPUDumper()31 CPUDumper::CPUDumper()
32 {
33 }
34
~CPUDumper()35 CPUDumper::~CPUDumper()
36 {
37 }
38
PreExecute(const std::shared_ptr<DumperParameter> & parameter,StringMatrix dumpDatas)39 DumpStatus CPUDumper::PreExecute(const std::shared_ptr<DumperParameter> ¶meter, StringMatrix dumpDatas)
40 {
41 DUMPER_HILOGD(MODULE_COMMON, "debug|CPUDumper PreExecute");
42 dumpCPUDatas_ = dumpDatas;
43 isDumpCpuUsage_ = (parameter->GetOpts()).isDumpCpuUsage_;
44 cpuUsagePid_ = (parameter->GetOpts()).cpuUsagePid_;
45 if (cpuUsagePid_ != -1) {
46 curSpecProc_ = std::make_shared<ProcInfo>();
47 oldSpecProc_ = std::make_shared<ProcInfo>();
48 }
49 curCPUInfo_ = std::make_shared<CPUInfo>();
50 oldCPUInfo_ = std::make_shared<CPUInfo>();
51 return DumpStatus::DUMP_OK;
52 }
53
Execute()54 DumpStatus CPUDumper::Execute()
55 {
56 DUMPER_HILOGD(MODULE_COMMON, "debug|CPUDumper Execute");
57 DumpStatus ret = DumpStatus::DUMP_FAIL;
58 if (isDumpCpuUsage_) {
59 ret = DumpCpuUsageData();
60 } else {
61 return DumpStatus::DUMP_FAIL;
62 }
63 return ret;
64 }
65
AfterExecute()66 DumpStatus CPUDumper::AfterExecute()
67 {
68 curCPUInfo_.reset();
69 oldCPUInfo_.reset();
70 curProcs_.clear();
71 oldProcs_.clear();
72 if (cpuUsagePid_ != -1) {
73 curSpecProc_.reset();
74 oldSpecProc_.reset();
75 }
76 return DumpStatus::DUMP_OK;
77 }
78
DumpCpuUsageData()79 DumpStatus CPUDumper::DumpCpuUsageData()
80 {
81 GetDateAndTime(startTime_);
82 if (!DumpCpuInfoUtil::GetInstance().GetCurCPUInfo(curCPUInfo_)) {
83 DUMPER_HILOGE(MODULE_COMMON, "Get current cpu info failed!.");
84 return DumpStatus::DUMP_FAIL;
85 }
86 if (!DumpCpuInfoUtil::GetInstance().GetOldCPUInfo(oldCPUInfo_)) {
87 DUMPER_HILOGE(MODULE_COMMON, "Get old cpu info failed!.");
88 return DumpStatus::DUMP_FAIL;
89 }
90
91 if (cpuUsagePid_ != -1) {
92 if (!GetProcCPUInfo()) {
93 return DumpStatus::DUMP_FAIL;
94 }
95 } else {
96 if (!DumpCpuInfoUtil::GetInstance().GetCurProcInfo(curProcs_)) {
97 DUMPER_HILOGE(MODULE_COMMON, "Get current process info failed!.");
98 return DumpStatus::DUMP_FAIL;
99 }
100 if (!DumpCpuInfoUtil::GetInstance().GetOldProcInfo(oldProcs_)) {
101 DUMPER_HILOGE(MODULE_COMMON, "Get old process info failed!.");
102 return DumpStatus::DUMP_FAIL;
103 }
104 }
105 std::string avgInfo;
106 DumpStatus ret = ReadLoadAvgInfo(LOAD_AVG_FILE_PATH, avgInfo);
107 if (ret != DumpStatus::DUMP_OK) {
108 DUMPER_HILOGD(MODULE_COMMON, "Get LoadAvgInfo failed!");
109 return DumpStatus::DUMP_FAIL;
110 }
111 AddStrLineToDumpInfo(avgInfo);
112
113 GetDateAndTime(endTime_);
114 std::string dumpTimeStr;
115 CreateDumpTimeString(startTime_, endTime_, dumpTimeStr);
116 AddStrLineToDumpInfo(dumpTimeStr);
117
118 std::string cpuStatStr;
119 CreateCPUStatString(cpuStatStr);
120 AddStrLineToDumpInfo(cpuStatStr);
121
122 DumpProcInfo();
123 DumpCpuInfoUtil::GetInstance().UpdateCpuInfo();
124 return DumpStatus::DUMP_OK;
125 }
126
GetProcCPUInfo()127 bool CPUDumper::GetProcCPUInfo()
128 {
129 bool ret = false;
130 if (!DumpCpuInfoUtil::GetInstance().GetOldSpecProcInfo(cpuUsagePid_, oldSpecProc_)) {
131 DumpCpuInfoUtil::GetInstance().UpdateCpuInfo();
132 if (!DumpCpuInfoUtil::GetInstance().GetOldSpecProcInfo(cpuUsagePid_, oldSpecProc_)) {
133 DUMPER_HILOGE(MODULE_COMMON, "Get old process %{public}d info failed!.", cpuUsagePid_);
134 return ret;
135 }
136
137 GetInitOldCPUInfo(oldCPUInfo_, curCPUInfo_);
138 usleep(DELAY_VALUE);
139 if (!DumpCpuInfoUtil::GetInstance().GetCurCPUInfo(curCPUInfo_)) {
140 DUMPER_HILOGE(MODULE_COMMON, "Get current cpu info failed!.");
141 return ret;
142 }
143 }
144
145 if (!DumpCpuInfoUtil::GetInstance().GetCurSpecProcInfo(cpuUsagePid_, curSpecProc_)) {
146 DUMPER_HILOGE(MODULE_COMMON, "Get current process %{public}d info failed!.", cpuUsagePid_);
147 return ret;
148 }
149 ret = true;
150 return ret;
151 }
152
GetInitOldCPUInfo(std::shared_ptr<CPUInfo> tar,const std::shared_ptr<CPUInfo> source)153 void CPUDumper::GetInitOldCPUInfo(std::shared_ptr<CPUInfo> tar, const std::shared_ptr<CPUInfo> source)
154 {
155 if ((tar == nullptr) || (source == nullptr)) {
156 return;
157 }
158
159 tar->uTime = source->uTime;
160 tar->nTime = source->nTime;
161 tar->sTime = source->sTime;
162 tar->iTime = source->iTime;
163 tar->iowTime = source->iowTime;
164 tar->irqTime = source->irqTime;
165 tar->sirqTime = source->sirqTime;
166 }
167
ReadLoadAvgInfo(const std::string & filePath,std::string & info)168 DumpStatus CPUDumper::ReadLoadAvgInfo(const std::string &filePath, std::string &info)
169 {
170 if (!FileExists(filePath)) {
171 return DumpStatus::DUMP_FAIL;
172 }
173
174 std::string rawData;
175 if (!LoadStringFromFile(filePath, rawData)) {
176 return DumpStatus::DUMP_FAIL;
177 }
178 DUMPER_HILOGD(MODULE_COMMON, "debug|rawData is %{public}s", rawData.c_str());
179 std::vector<std::string> vec;
180 SplitStr(rawData, DumpUtils::SPACE, vec);
181 info = "Load average:";
182 for (size_t i = 0; i < LOAD_AVG_INFO_COUNT; i++) {
183 info.append(" ");
184 info.append(vec[i]);
185 if (i == LOAD_AVG_INFO_COUNT - 1) {
186 info.append(";");
187 } else {
188 info.append(" /");
189 }
190 }
191 info.append(" the cpu load average in 1 min, 5 min and 15 min");
192 DUMPER_HILOGD(MODULE_COMMON, "debug|info is %{public}s", info.c_str());
193 return DumpStatus::DUMP_OK;
194 }
195
GetDateAndTime(std::string & dateTime)196 bool CPUDumper::GetDateAndTime(std::string &dateTime)
197 {
198 struct tm timeData = {0};
199 if (!GetSystemCurrentTime(&timeData)) {
200 return false;
201 }
202
203 dateTime = " ";
204 dateTime.append(std::to_string(TM_START_YEAR + timeData.tm_year));
205 dateTime.append("-");
206 if (1 + timeData.tm_mon < DEC_SYSTEM_VALUE) {
207 dateTime.append(std::to_string(0));
208 }
209 dateTime.append(std::to_string(1 + timeData.tm_mon));
210 dateTime.append("-");
211 if (timeData.tm_mday < DEC_SYSTEM_VALUE) {
212 dateTime.append(std::to_string(0));
213 }
214 dateTime.append(std::to_string(timeData.tm_mday));
215 dateTime.append(" ");
216 if (timeData.tm_hour < DEC_SYSTEM_VALUE) {
217 dateTime.append(std::to_string(0));
218 }
219 dateTime.append(std::to_string(timeData.tm_hour));
220 dateTime.append(":");
221 if (timeData.tm_min < DEC_SYSTEM_VALUE) {
222 dateTime.append(std::to_string(0));
223 }
224 dateTime.append(std::to_string(timeData.tm_min));
225 dateTime.append(":");
226 if (timeData.tm_sec < DEC_SYSTEM_VALUE) {
227 dateTime.append(std::to_string(0));
228 }
229 dateTime.append(std::to_string(timeData.tm_sec));
230 return true;
231 }
232
CreateDumpTimeString(const std::string & startTime,const std::string & endTime,std::string & timeStr)233 void CPUDumper::CreateDumpTimeString(const std::string &startTime, const std::string &endTime, std::string &timeStr)
234 {
235 timeStr = "CPU usage from";
236 timeStr.append(startTime);
237 timeStr.append(" to");
238 timeStr.append(endTime);
239 }
240
AddStrLineToDumpInfo(const std::string & strLine)241 void CPUDumper::AddStrLineToDumpInfo(const std::string &strLine)
242 {
243 std::vector<std::string> vec;
244 vec.push_back(strLine);
245 dumpCPUDatas_->push_back(vec);
246 }
247
CreateCPUStatString(std::string & str)248 void CPUDumper::CreateCPUStatString(std::string &str)
249 {
250 long unsigned totalDeltaTime = (curCPUInfo_->uTime + curCPUInfo_->nTime + curCPUInfo_->sTime + curCPUInfo_->iTime
251 + curCPUInfo_->iowTime + curCPUInfo_->irqTime + curCPUInfo_->sirqTime)
252 - (oldCPUInfo_->uTime + oldCPUInfo_->nTime + oldCPUInfo_->sTime + oldCPUInfo_->iTime
253 + oldCPUInfo_->iowTime + oldCPUInfo_->irqTime + oldCPUInfo_->sirqTime);
254 if (cpuUsagePid_ != -1) {
255 curSpecProc_->userSpaceUsage =
256 (curSpecProc_->uTime - oldSpecProc_->uTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
257 curSpecProc_->sysSpaceUsage =
258 (curSpecProc_->sTime - oldSpecProc_->sTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
259 curSpecProc_->totalUsage = curSpecProc_->userSpaceUsage + curSpecProc_->sysSpaceUsage;
260 } else {
261 for (size_t i = 0; i < curProcs_.size(); i++) {
262 if (curProcs_[i] == nullptr) {
263 continue;
264 }
265 std::shared_ptr<ProcInfo> oldProc = GetOldProc(curProcs_[i]->pid);
266 if (oldProc) {
267 curProcs_[i]->userSpaceUsage =
268 (curProcs_[i]->uTime - oldProc->uTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
269 curProcs_[i]->sysSpaceUsage =
270 (curProcs_[i]->sTime - oldProc->sTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
271 curProcs_[i]->totalUsage = curProcs_[i]->userSpaceUsage + curProcs_[i]->sysSpaceUsage;
272 } else {
273 curProcs_[i]->userSpaceUsage = 0;
274 curProcs_[i]->sysSpaceUsage = 0;
275 curProcs_[i]->totalUsage = 0;
276 }
277 }
278 }
279
280 long unsigned userSpaceUsage =
281 ((curCPUInfo_->uTime + curCPUInfo_->nTime) - (oldCPUInfo_->uTime + oldCPUInfo_->nTime)) * HUNDRED_PERCENT_VALUE
282 / totalDeltaTime;
283 long unsigned sysSpaceUsage = (curCPUInfo_->sTime - oldCPUInfo_->sTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
284 long unsigned iowUsage = (curCPUInfo_->iowTime - oldCPUInfo_->iowTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
285 long unsigned irqUsage =
286 ((curCPUInfo_->irqTime + curCPUInfo_->sirqTime) - (oldCPUInfo_->irqTime + oldCPUInfo_->sirqTime))
287 * HUNDRED_PERCENT_VALUE / totalDeltaTime;
288 long unsigned idleUsage = (curCPUInfo_->iTime - oldCPUInfo_->iTime) * HUNDRED_PERCENT_VALUE / totalDeltaTime;
289 long unsigned totalUsage = userSpaceUsage + sysSpaceUsage;
290
291 str = "Total: ";
292 str.append(std::to_string(totalUsage)).append("%; ");
293 str.append("User Space: ").append(std::to_string(userSpaceUsage)).append("%; ");
294 str.append("Kernel Space: ").append(std::to_string(sysSpaceUsage)).append("%; ");
295 str.append("iowait: ").append(std::to_string(iowUsage)).append("%; ");
296 str.append("irq: ").append(std::to_string(irqUsage)).append("%; ");
297 str.append("idle: ").append(std::to_string(idleUsage)).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
DumpProcInfo()310 void CPUDumper::DumpProcInfo()
311 {
312 std::vector<std::shared_ptr<ProcInfo>> sortedInfos;
313 sortedInfos.assign(curProcs_.begin(), curProcs_.end());
314 std::sort(sortedInfos.begin(), sortedInfos.end(), SortProcInfo);
315
316 AddStrLineToDumpInfo("Details of Processes:");
317 AddStrLineToDumpInfo(" PID Total Usage User Space Kernel Space Page Fault Minor"
318 " Page Fault Major Name");
319 if (cpuUsagePid_ != -1) {
320 char format[PROC_CPU_LENGTH] = {0};
321 int ret = sprintf_s(format, PROC_CPU_LENGTH,
322 " %-5s %3lu%% %3lu%%"
323 " %3lu%% %8s %8s %-15s",
324 (curSpecProc_->pid).c_str(), curSpecProc_->totalUsage,
325 curSpecProc_->userSpaceUsage, curSpecProc_->sysSpaceUsage,
326 (curSpecProc_->minflt).c_str(), (curSpecProc_->majflt).c_str(),
327 (curSpecProc_->comm).c_str());
328 AddStrLineToDumpInfo(std::string(format));
329 if (ret < 0) {
330 DUMPER_HILOGE(MODULE_COMMON, "Dump process %{public}d cpu info failed!.", cpuUsagePid_);
331 }
332 return;
333 }
334 for (size_t i = 0; i < sortedInfos.size(); i++) {
335 char format[PROC_CPU_LENGTH] = {0};
336 int ret = sprintf_s(format, PROC_CPU_LENGTH,
337 " %-5s %3lu%% %3lu%%"
338 " %3lu%% %8s %8s %-15s",
339 (sortedInfos[i]->pid).c_str(), sortedInfos[i]->totalUsage,
340 sortedInfos[i]->userSpaceUsage, sortedInfos[i]->sysSpaceUsage,
341 (sortedInfos[i]->minflt).c_str(), (sortedInfos[i]->majflt).c_str(),
342 (sortedInfos[i]->comm).c_str());
343 if (ret < 0) {
344 continue;
345 }
346 AddStrLineToDumpInfo(std::string(format));
347 }
348 }
349
SortProcInfo(std::shared_ptr<ProcInfo> & left,std::shared_ptr<ProcInfo> & right)350 bool CPUDumper::SortProcInfo(std::shared_ptr<ProcInfo> &left, std::shared_ptr<ProcInfo> &right)
351 {
352 if (right->totalUsage != left->totalUsage) {
353 return right->totalUsage < left->totalUsage;
354 }
355 if (right->userSpaceUsage != left->userSpaceUsage) {
356 return right->userSpaceUsage < left->userSpaceUsage;
357 }
358 if (right->sysSpaceUsage != left->sysSpaceUsage) {
359 return right->sysSpaceUsage < left->sysSpaceUsage;
360 }
361 if (right->pid.length() != left->pid.length()) {
362 return right->pid.length() < left->pid.length();
363 }
364 return (right->pid.compare(left->pid) < 0);
365 }
366 } // namespace HiviewDFX
367 } // namespace OHOS
368