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