1 /*
2 * Copyright (C) 2023-2024 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 #ifdef HAS_HIPERF
16 #include "cpu_perf_dump.h"
17 #include "file_util.h"
18 #include "time_util.h"
19 #include "hiview_logger.h"
20 #include "parameter_ex.h"
21
22 namespace OHOS {
23 namespace HiviewDFX {
24 DEFINE_LOG_TAG("HiView-CpuPerfDump");
25 namespace {
26 const double CPU_MONITOR_THRESHOLD_CPULOAD = 0.6;
27 const double CPU_MONITOR_THRESHOLD_CPUUSAGE = 0.8;
28 const double CPU_MONITOR_THRESHOLD_PRECISION = 0.00001;
29 const int TOP_N_PROCESS = 3;
30 const int PERF_COLLECT_TIME = 5;
31 constexpr char HIPERF_LOG_PATH[] = "/data/log/hiperf";
32 constexpr uint32_t MAX_NUM_OF_PERF_FILES = 7; // save files for one week
33 constexpr int64_t DUMP_HIPERF_INTERVAL = 15 * 60 * 1000; //ms
34 constexpr int64_t DUMP_HIPERF_DELAY_TIME = 2 * 60 * 1000; //ms
35 }
36
CpuPerfDump()37 CpuPerfDump::CpuPerfDump()
38 {
39 perfCollector_ = UCollectUtil::PerfCollector::Create();
40 if (!FileUtil::FileExists(HIPERF_LOG_PATH)) {
41 FileUtil::ForceCreateDirectory(HIPERF_LOG_PATH, FileUtil::FILE_PERM_770);
42 }
43 systemUpTime_ = static_cast<int64_t>(TimeUtil::GetMilliseconds());
44 lastRecordTime_ = systemUpTime_;
45 isBetaVersion_ = Parameter::IsBetaVersion();
46 }
47
DumpTopNCpuProcessPerfData()48 void CpuPerfDump::DumpTopNCpuProcessPerfData()
49 {
50 if (!topNProcs_.empty()) {
51 std::string filename = "hiperf-top3-";
52 filename += TimeUtil::TimestampFormatToDate(TimeUtil::GetMilliseconds() / TimeUtil::SEC_TO_MILLISEC,
53 "%Y%m%d%H%M%S");
54 filename += ".data";
55 perfCollector_->SetOutputFilename(filename);
56 perfCollector_->SetSelectPids(topNProcs_);
57 perfCollector_->SetTimeStopSec(PERF_COLLECT_TIME);
58 perfCollector_->StartPerf(HIPERF_LOG_PATH);
59 }
60
61 lastRecordTime_ = static_cast<int64_t>(TimeUtil::GetMilliseconds());
62 TryToAgePerfFiles();
63 }
64
CompareCpuLoad(const ProcessCpuStatInfo &info1, const ProcessCpuStatInfo &info2)65 bool CpuPerfDump::CompareCpuLoad(const ProcessCpuStatInfo &info1, const ProcessCpuStatInfo &info2)
66 {
67 return info1.cpuLoad > info2.cpuLoad;
68 }
69
CompareCpuUsage(const ProcessCpuStatInfo &info1, const ProcessCpuStatInfo &info2)70 bool CpuPerfDump::CompareCpuUsage(const ProcessCpuStatInfo &info1, const ProcessCpuStatInfo &info2)
71 {
72 return info1.cpuUsage > info2.cpuUsage;
73 }
74
CheckRecordInterval()75 bool CpuPerfDump::CheckRecordInterval()
76 {
77 if (!isBetaVersion_) {
78 return false;
79 }
80 int64_t nowTime = static_cast<int64_t>(TimeUtil::GetMilliseconds());
81 if (abs(nowTime - systemUpTime_) < DUMP_HIPERF_DELAY_TIME) {
82 return false;
83 }
84 if (systemUpTime_ != lastRecordTime_ && abs(nowTime - lastRecordTime_) < DUMP_HIPERF_INTERVAL) {
85 return false;
86 }
87 return true;
88 }
89
CheckAndDumpPerfData(std::vector<ProcessCpuStatInfo> &cpuCollectionInfos)90 void CpuPerfDump::CheckAndDumpPerfData(std::vector<ProcessCpuStatInfo> &cpuCollectionInfos)
91 {
92 if (!CheckRecordInterval()) {
93 return;
94 }
95 if (cpuCollectionInfos.empty()) {
96 return;
97 }
98 topNProcs_.clear();
99 double sumCpuLoad = 0.0;
100 double sumCpuUsage = 0.0;
101 for (const auto &info : cpuCollectionInfos) {
102 sumCpuLoad += info.cpuLoad;
103 sumCpuUsage += info.cpuUsage;
104 }
105
106 size_t middlePos = (cpuCollectionInfos.size() >= TOP_N_PROCESS) ? TOP_N_PROCESS : cpuCollectionInfos.size();
107 if (sumCpuLoad >= CPU_MONITOR_THRESHOLD_CPULOAD + CPU_MONITOR_THRESHOLD_PRECISION) {
108 std::partial_sort(cpuCollectionInfos.begin(), cpuCollectionInfos.begin() + middlePos,
109 cpuCollectionInfos.end(), CompareCpuLoad);
110 } else if (sumCpuLoad < CPU_MONITOR_THRESHOLD_PRECISION &&
111 sumCpuUsage >= CPU_MONITOR_THRESHOLD_CPUUSAGE + CPU_MONITOR_THRESHOLD_PRECISION) {
112 std::partial_sort(cpuCollectionInfos.begin(), cpuCollectionInfos.begin() + middlePos,
113 cpuCollectionInfos.end(), CompareCpuUsage);
114 } else {
115 return;
116 }
117
118 for (const auto &info : cpuCollectionInfos) {
119 topNProcs_.emplace_back(info.pid);
120 if (topNProcs_.size() >= TOP_N_PROCESS) {
121 break;
122 }
123 }
124
125 DumpTopNCpuProcessPerfData();
126 }
127
NeedCleanPerfFiles(size_t size)128 bool CpuPerfDump::NeedCleanPerfFiles(size_t size)
129 {
130 return size > MAX_NUM_OF_PERF_FILES;
131 }
132
GetTimestamp(const std::string& fileName)133 std::string CpuPerfDump::GetTimestamp(const std::string& fileName)
134 {
135 auto startPos = fileName.find_last_of('-');
136 if (startPos == std::string::npos) {
137 return "";
138 }
139 auto endPos = fileName.find_last_of('.');
140 if (endPos == std::string::npos) {
141 return "";
142 }
143 if (endPos <= startPos + 1) {
144 return "";
145 }
146 return fileName.substr(startPos + 1, endPos - startPos - 1);
147 }
148
CompareFilenames(const std::string &name1, const std::string &name2)149 bool CpuPerfDump::CompareFilenames(const std::string &name1, const std::string &name2)
150 {
151 std::string timestamp1 = GetTimestamp(name1);
152 std::string timestamp2 = GetTimestamp(name2);
153 uint64_t time1 = std::stoull(timestamp1);
154 uint64_t time2 = std::stoull(timestamp2);
155 return time1 < time2;
156 }
157
TryToAgePerfFiles()158 void CpuPerfDump::TryToAgePerfFiles()
159 {
160 std::vector<std::string> perfFiles;
161 FileUtil::GetDirFiles(HIPERF_LOG_PATH, perfFiles);
162 if (!NeedCleanPerfFiles(perfFiles.size())) {
163 return;
164 }
165 std::sort(perfFiles.begin(), perfFiles.end(), CompareFilenames);
166 uint32_t numOfCleanFiles = perfFiles.size() - MAX_NUM_OF_PERF_FILES;
167 for (size_t i = 0; i < numOfCleanFiles; i++) {
168 if (!FileUtil::RemoveFile(perfFiles[i])) {
169 HIVIEW_LOGW("failed to delete perf file: %{public}s", perfFiles[i].c_str());
170 }
171 }
172 }
173 } // namespace HiviewDFX
174 } // namespace OHOS
175 #endif // HAS_HIPERF
176