1 /*
2  * Copyright (c) 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 
16 #include "process_state_info_collector.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 #include <memory>
21 #include <optional>
22 #include <unordered_map>
23 #include <unordered_set>
24 
25 #include "common_utils.h"
26 #include "cpu_decorator.h"
27 #include "hiview_logger.h"
28 #include "process_status.h"
29 #include "time_util.h"
30 
31 namespace OHOS {
32 namespace HiviewDFX {
33 namespace UCollectUtil {
34 DEFINE_LOG_TAG("CpuCollector");
ProcessStatInfoCollector(std::shared_ptr<CollectDeviceClient> deviceClient,std::shared_ptr<CpuCalculator> cpuCalculator)35 ProcessStatInfoCollector::ProcessStatInfoCollector(std::shared_ptr<CollectDeviceClient> deviceClient,
36     std::shared_ptr<CpuCalculator> cpuCalculator): deviceClient_(deviceClient), cpuCalculator_(cpuCalculator)
37 {
38     InitLastProcCpuTimeInfos();
39 }
40 
InitLastProcCpuTimeInfos()41 void ProcessStatInfoCollector::InitLastProcCpuTimeInfos()
42 {
43     auto processCpuData = FetchProcessCpuData();
44     if (processCpuData == nullptr) {
45         return;
46     }
47 
48     // init cpu time information for each process in the system
49     CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo();
50     auto procCpuItem = processCpuData->GetNextProcess();
51     while (procCpuItem != nullptr) {
52         UpdateLastProcCpuTimeInfo(procCpuItem, calcTimeInfo);
53         procCpuItem = processCpuData->GetNextProcess();
54     }
55     UpdateCollectionTime(calcTimeInfo);
56 }
57 
FetchProcessCpuData(int32_t pid)58 std::shared_ptr<ProcessCpuData> ProcessStatInfoCollector::FetchProcessCpuData(int32_t pid)
59 {
60     if (deviceClient_ == nullptr) {
61         HIVIEW_LOGW("device client is null");
62         return nullptr;
63     }
64     return (pid == INVALID_PID)
65         ? deviceClient_->FetchProcessCpuData()
66         : deviceClient_->FetchProcessCpuData(pid);
67 }
68 
UpdateCollectionTime(const CalculationTimeInfo & calcTimeInfo)69 void ProcessStatInfoCollector::UpdateCollectionTime(const CalculationTimeInfo& calcTimeInfo)
70 {
71     lastCollectionTime_ = calcTimeInfo.endTime;
72     lastCollectionBootTime_ = calcTimeInfo.endBootTime;
73 }
74 
UpdateLastProcCpuTimeInfo(const ucollection_process_cpu_item * procCpuItem,const CalculationTimeInfo & calcTimeInfo)75 void ProcessStatInfoCollector::UpdateLastProcCpuTimeInfo(const ucollection_process_cpu_item* procCpuItem,
76     const CalculationTimeInfo& calcTimeInfo)
77 {
78     lastProcCpuTimeInfos_[procCpuItem->pid] = {
79         .minFlt = procCpuItem->min_flt,
80         .majFlt = procCpuItem->maj_flt,
81         .uUsageTime = procCpuItem->cpu_usage_utime,
82         .sUsageTime = procCpuItem->cpu_usage_stime,
83         .loadTime = procCpuItem->cpu_load_time,
84         .collectionTime = calcTimeInfo.endTime,
85         .collectionBootTime = calcTimeInfo.endBootTime,
86     };
87 }
88 
CollectProcessCpuStatInfos(bool isNeedUpdate)89 CollectResult<std::vector<ProcessCpuStatInfo>> ProcessStatInfoCollector::CollectProcessCpuStatInfos(
90     bool isNeedUpdate)
91 {
92     CollectResult<std::vector<ProcessCpuStatInfo>> cpuCollectResult;
93     std::unique_lock<std::mutex> lock(collectMutex_);
94     auto processCpuData = FetchProcessCpuData();
95     if (processCpuData == nullptr) {
96         return cpuCollectResult;
97     }
98 
99     CalculateProcessCpuStatInfos(cpuCollectResult.data, processCpuData, isNeedUpdate);
100     HIVIEW_LOGI("collect process cpu statistics information size=%{public}zu, isNeedUpdate=%{public}d",
101         cpuCollectResult.data.size(), isNeedUpdate);
102     if (!cpuCollectResult.data.empty()) {
103         cpuCollectResult.retCode = UCollect::UcError::SUCCESS;
104         TryToDeleteDeadProcessInfo();
105     }
106     return cpuCollectResult;
107 }
108 
CalculateProcessCpuStatInfos(std::vector<ProcessCpuStatInfo> & processCpuStatInfos,std::shared_ptr<ProcessCpuData> processCpuData,bool isNeedUpdate)109 void ProcessStatInfoCollector::CalculateProcessCpuStatInfos(
110     std::vector<ProcessCpuStatInfo>& processCpuStatInfos,
111     std::shared_ptr<ProcessCpuData> processCpuData,
112     bool isNeedUpdate)
113 {
114     CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo();
115     HIVIEW_LOGI("startTime=%{public}" PRIu64 ", endTime=%{public}" PRIu64 ", startBootTime=%{public}" PRIu64
116         ", endBootTime=%{public}" PRIu64 ", period=%{public}" PRIu64, calcTimeInfo.startTime,
117         calcTimeInfo.endTime, calcTimeInfo.startBootTime, calcTimeInfo.endBootTime, calcTimeInfo.period);
118     auto procCpuItem = processCpuData->GetNextProcess();
119     while (procCpuItem != nullptr) {
120         auto processCpuStatInfo = CalculateProcessCpuStatInfo(procCpuItem, calcTimeInfo);
121         if (processCpuStatInfo.has_value()) {
122             processCpuStatInfos.emplace_back(processCpuStatInfo.value());
123         }
124         if (isNeedUpdate) {
125             UpdateLastProcCpuTimeInfo(procCpuItem, calcTimeInfo);
126         }
127         procCpuItem = processCpuData->GetNextProcess();
128     }
129 
130     if (isNeedUpdate) {
131         UpdateCollectionTime(calcTimeInfo);
132     }
133 }
134 
InitCalculationTimeInfo()135 CalculationTimeInfo ProcessStatInfoCollector::InitCalculationTimeInfo()
136 {
137     CalculationTimeInfo calcTimeInfo = {
138         .startTime = lastCollectionTime_,
139         .endTime = TimeUtil::GetMilliseconds(),
140         .startBootTime = lastCollectionBootTime_,
141         .endBootTime = TimeUtil::GetBootTimeMs(),
142     };
143     calcTimeInfo.period = calcTimeInfo.endBootTime > calcTimeInfo.startBootTime
144         ? (calcTimeInfo.endBootTime - calcTimeInfo.startBootTime) : 0;
145     return calcTimeInfo;
146 }
147 
CalculateProcessCpuStatInfo(const ucollection_process_cpu_item * procCpuItem,const CalculationTimeInfo & calcTimeInfo)148 std::optional<ProcessCpuStatInfo> ProcessStatInfoCollector::CalculateProcessCpuStatInfo(
149     const ucollection_process_cpu_item* procCpuItem, const CalculationTimeInfo& calcTimeInfo)
150 {
151     if (cpuCalculator_ == nullptr) {
152         return std::nullopt;
153     }
154     if (lastProcCpuTimeInfos_.find(procCpuItem->pid) == lastProcCpuTimeInfos_.end()) {
155         HIVIEW_LOGD("lastProcCpuTimeInfos do not have pid:%{public}d", procCpuItem->pid);
156         return std::nullopt;
157     }
158     ProcessCpuStatInfo processCpuStatInfo;
159     processCpuStatInfo.startTime = calcTimeInfo.startTime;
160     processCpuStatInfo.endTime = calcTimeInfo.endTime;
161     processCpuStatInfo.pid = procCpuItem->pid;
162     processCpuStatInfo.minFlt = procCpuItem->min_flt;
163     processCpuStatInfo.majFlt = procCpuItem->maj_flt;
164     processCpuStatInfo.procName = ProcessStatus::GetInstance().GetProcessName(procCpuItem->pid);
165     processCpuStatInfo.cpuLoad = cpuCalculator_->CalculateCpuLoad(procCpuItem->cpu_load_time,
166         lastProcCpuTimeInfos_[procCpuItem->pid].loadTime, calcTimeInfo.period);
167     processCpuStatInfo.uCpuUsage = cpuCalculator_->CalculateCpuUsage(procCpuItem->cpu_usage_utime,
168         lastProcCpuTimeInfos_[procCpuItem->pid].uUsageTime, calcTimeInfo.period);
169     processCpuStatInfo.sCpuUsage = cpuCalculator_->CalculateCpuUsage(procCpuItem->cpu_usage_stime,
170         lastProcCpuTimeInfos_[procCpuItem->pid].sUsageTime, calcTimeInfo.period);
171     processCpuStatInfo.cpuUsage = processCpuStatInfo.uCpuUsage + processCpuStatInfo.sCpuUsage;
172     processCpuStatInfo.threadCount = procCpuItem->thread_total;
173     if (processCpuStatInfo.cpuLoad >= 1) { // 1: max cpu load
174         HIVIEW_LOGI("invalid cpu load=%{public}f, name=%{public}s, last_load=%{public}" PRIu64
175             ", curr_load=%{public}" PRIu64, processCpuStatInfo.cpuLoad, processCpuStatInfo.procName.c_str(),
176             lastProcCpuTimeInfos_[procCpuItem->pid].loadTime, static_cast<uint64_t>(procCpuItem->cpu_load_time));
177     }
178     return std::make_optional<ProcessCpuStatInfo>(processCpuStatInfo);
179 }
180 
TryToDeleteDeadProcessInfo()181 void ProcessStatInfoCollector::TryToDeleteDeadProcessInfo()
182 {
183     for (auto it = lastProcCpuTimeInfos_.begin(); it != lastProcCpuTimeInfos_.end();) {
184         // if the latest collection operation does not update the process collection time, delete it
185         if (it->second.collectionTime != lastCollectionTime_) {
186             ProcessStatus::GetInstance().NotifyProcessState(it->first, DIED);
187             it = lastProcCpuTimeInfos_.erase(it);
188         } else {
189             it++;
190         }
191     }
192     HIVIEW_LOGD("end to delete dead process, size=%{public}zu", lastProcCpuTimeInfos_.size());
193 }
194 
CollectProcessCpuStatInfo(int32_t pid,bool isNeedUpdate)195 CollectResult<ProcessCpuStatInfo> ProcessStatInfoCollector::CollectProcessCpuStatInfo(int32_t pid, bool isNeedUpdate)
196 {
197     CollectResult<ProcessCpuStatInfo> cpuCollectResult;
198 
199     std::unique_lock<std::mutex> lock(collectMutex_);
200     if (!CommonUtils::IsPidExist(pid)) {
201         HIVIEW_LOGD("pid:%{public}d not exist", pid);
202         TryToDeleteDeadProcessInfoByPid(pid);
203         return cpuCollectResult;
204     }
205     auto processCpuData = FetchProcessCpuData(pid);
206     if (processCpuData == nullptr) {
207         return cpuCollectResult;
208     }
209     auto procCpuItem = processCpuData->GetNextProcess();
210     if (procCpuItem == nullptr) {
211         return cpuCollectResult;
212     }
213 
214     CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo(pid);
215     auto processCpuStatInfo = CalculateProcessCpuStatInfo(procCpuItem, calcTimeInfo);
216     if (processCpuStatInfo.has_value()) {
217         cpuCollectResult.retCode = UCollect::UcError::SUCCESS;
218         cpuCollectResult.data = processCpuStatInfo.value();
219     }
220     if (isNeedUpdate) {
221         UpdateLastProcCpuTimeInfo(procCpuItem, calcTimeInfo);
222         TryToDeleteDeadProcessInfoByTime(calcTimeInfo.endBootTime);
223     }
224     return cpuCollectResult;
225 }
226 
InitCalculationTimeInfo(int32_t pid)227 CalculationTimeInfo ProcessStatInfoCollector::InitCalculationTimeInfo(int32_t pid)
228 {
229     CalculationTimeInfo calcTimeInfo = {
230         .startTime = lastProcCpuTimeInfos_.find(pid) != lastProcCpuTimeInfos_.end()
231             ? lastProcCpuTimeInfos_[pid].collectionTime : 0,
232         .endTime = TimeUtil::GetMilliseconds(),
233         .startBootTime = lastProcCpuTimeInfos_.find(pid) != lastProcCpuTimeInfos_.end()
234             ? lastProcCpuTimeInfos_[pid].collectionBootTime : 0,
235         .endBootTime = TimeUtil::GetBootTimeMs(),
236     };
237     calcTimeInfo.period = calcTimeInfo.endBootTime > calcTimeInfo.startBootTime
238         ? (calcTimeInfo.endBootTime - calcTimeInfo.startBootTime) : 0;
239     return calcTimeInfo;
240 }
241 
TryToDeleteDeadProcessInfoByPid(int32_t pid)242 void ProcessStatInfoCollector::TryToDeleteDeadProcessInfoByPid(int32_t pid)
243 {
244     if (lastProcCpuTimeInfos_.find(pid) != lastProcCpuTimeInfos_.end()) {
245         lastProcCpuTimeInfos_.erase(pid);
246         HIVIEW_LOGD("end to delete dead process=%{public}d", pid);
247     }
248 }
249 
TryToDeleteDeadProcessInfoByTime(uint64_t collectionBootTime)250 void ProcessStatInfoCollector::TryToDeleteDeadProcessInfoByTime(uint64_t collectionBootTime)
251 {
252     static uint64_t lastClearTime = TimeUtil::GetBootTimeMs();
253     uint64_t interval = collectionBootTime > lastClearTime ? (collectionBootTime - lastClearTime) : 0;
254     constexpr uint32_t clearInterval = 600 * 1000; // 10min
255     if (interval < clearInterval) {
256         return;
257     }
258     lastClearTime = collectionBootTime;
259     HIVIEW_LOGD("start to delete dead processes, size=%{public}zu", lastProcCpuTimeInfos_.size());
260     for (auto it = lastProcCpuTimeInfos_.begin(); it != lastProcCpuTimeInfos_.end();) {
261         if (!CommonUtils::IsPidExist(it->first)) {
262             it = lastProcCpuTimeInfos_.erase(it);
263         } else {
264             it++;
265         }
266     }
267     HIVIEW_LOGD("end to delete dead processes, size=%{public}zu", lastProcCpuTimeInfos_.size());
268 }
269 } // UCollectUtil
270 } // HiViewDFX
271 } // OHOS
272