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