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