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 #include "thread_state_info_collector.h"
16
17 #include <sys/types.h>
18 #include <unistd.h>
19
20 #include "cpu_decorator.h"
21 #include "hiview_logger.h"
22 #include "time_util.h"
23
24 namespace OHOS {
25 namespace HiviewDFX {
26 namespace UCollectUtil {
27 DEFINE_LOG_TAG("CpuCollector");
28
Create(int32_t pid,bool isSingleton)29 std::shared_ptr<ThreadCpuCollector> ThreadCpuCollector::Create(int32_t pid, bool isSingleton)
30 {
31 if (!isSingleton) {
32 return std::make_shared<CpuDecorator>(nullptr, std::make_shared<ThreadStateInfoCollector>(pid));
33 }
34 static std::shared_ptr<ThreadCpuCollector> instance_ =
35 std::make_shared<CpuDecorator>(nullptr, std::make_shared<ThreadStateInfoCollector>(pid));
36 return instance_;
37 }
38
ThreadStateInfoCollector(std::shared_ptr<CollectDeviceClient> deviceClient,std::shared_ptr<CpuCalculator> cpuCalculator,int collectPid)39 ThreadStateInfoCollector::ThreadStateInfoCollector(std::shared_ptr<CollectDeviceClient> deviceClient,
40 std::shared_ptr<CpuCalculator> cpuCalculator, int collectPid): deviceClient_(deviceClient),
41 cpuCalculator_(cpuCalculator), collectPid_(collectPid)
42 {
43 InitLastThreadCpuTimeInfos();
44 }
45
ThreadStateInfoCollector(int collectPid)46 ThreadStateInfoCollector::ThreadStateInfoCollector(int collectPid): collectPid_(collectPid)
47 {
48 InitDeviceClient();
49 if (deviceClient_ == nullptr) {
50 return;
51 }
52 cpuCalculator_ = std::make_shared<CpuCalculator>();
53 InitLastThreadCpuTimeInfos();
54 }
55
InitDeviceClient()56 void ThreadStateInfoCollector::InitDeviceClient()
57 {
58 deviceClient_ = std::make_shared<CollectDeviceClient>();
59 if (deviceClient_->Open() != 0) {
60 HIVIEW_LOGE("failed to open device client");
61 deviceClient_ = nullptr;
62 }
63 }
64
InitLastThreadCpuTimeInfos()65 void ThreadStateInfoCollector::InitLastThreadCpuTimeInfos()
66 {
67 auto threadCpuData = FetchThreadCpuData();
68 if (threadCpuData == nullptr) {
69 return;
70 }
71
72 // init cpu time information for each thread in the system
73 CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo();
74 auto threadCpuItem = threadCpuData->GetNextThread();
75 while (threadCpuItem != nullptr) {
76 UpdateLastThreadTimeInfo(threadCpuItem, calcTimeInfo);
77 threadCpuItem = threadCpuData->GetNextThread();
78 }
79 UpdateCollectionTime(calcTimeInfo);
80 }
81
InitCalculationTimeInfo()82 CalculationTimeInfo ThreadStateInfoCollector::InitCalculationTimeInfo()
83 {
84 CalculationTimeInfo calcTimeInfo = {
85 .startTime = lastCollectionTime_,
86 .endTime = TimeUtil::GetMilliseconds(),
87 .startBootTime = lastCollectionBootTime_,
88 .endBootTime = TimeUtil::GetBootTimeMs(),
89 };
90 calcTimeInfo.period = calcTimeInfo.endBootTime > calcTimeInfo.startBootTime
91 ? (calcTimeInfo.endBootTime - calcTimeInfo.startBootTime) : 0;
92 return calcTimeInfo;
93 }
94
UpdateLastThreadTimeInfo(const ucollection_thread_cpu_item * threadCpuItem,const CalculationTimeInfo & calcTimeInfo)95 void ThreadStateInfoCollector::UpdateLastThreadTimeInfo(const ucollection_thread_cpu_item* threadCpuItem,
96 const CalculationTimeInfo& calcTimeInfo)
97 {
98 lastThreadCpuTimeInfos_[threadCpuItem->tid] = {
99 .uUsageTime = threadCpuItem->cpu_usage_utime,
100 .sUsageTime = threadCpuItem->cpu_usage_stime,
101 .loadTime = threadCpuItem->cpu_load_time,
102 .collectionTime = calcTimeInfo.endTime,
103 .collectionBootTime = calcTimeInfo.endBootTime,
104 };
105 }
106
UpdateCollectionTime(const CalculationTimeInfo & calcTimeInfo)107 void ThreadStateInfoCollector::UpdateCollectionTime(const CalculationTimeInfo& calcTimeInfo)
108 {
109 lastCollectionTime_ = calcTimeInfo.endTime;
110 lastCollectionBootTime_ = calcTimeInfo.endBootTime;
111 }
112
CollectThreadStatInfos(bool isNeedUpdate)113 CollectResult<std::vector<ThreadCpuStatInfo>> ThreadStateInfoCollector::CollectThreadStatInfos(bool isNeedUpdate)
114 {
115 CollectResult<std::vector<ThreadCpuStatInfo>> threadCollectResult;
116 std::unique_lock<std::mutex> lock(collectMutex_);
117 auto threadCpuData = FetchThreadCpuData();
118 if (threadCpuData == nullptr) {
119 return threadCollectResult;
120 }
121 CalculateThreadCpuStatInfos(threadCollectResult.data, threadCpuData, isNeedUpdate);
122 HIVIEW_LOGD("collect thread cpu statistics information size=%{public}zu, isNeedUpdate=%{public}d",
123 threadCollectResult.data.size(), isNeedUpdate);
124 if (!threadCollectResult.data.empty()) {
125 threadCollectResult.retCode = UCollect::UcError::SUCCESS;
126 TryToDeleteDeadThreadInfo();
127 }
128 return threadCollectResult;
129 }
130
GetCollectPid()131 int ThreadStateInfoCollector::GetCollectPid()
132 {
133 return collectPid_;
134 }
135
FetchThreadCpuData()136 std::shared_ptr<ThreadCpuData> ThreadStateInfoCollector::FetchThreadCpuData()
137 {
138 if (deviceClient_ == nullptr) {
139 HIVIEW_LOGW("device client is null");
140 return nullptr;
141 }
142 return (collectPid_ == getprocpid()) ? deviceClient_->FetchSelfThreadCpuData(collectPid_)
143 : deviceClient_->FetchThreadCpuData(collectPid_);
144 }
145
CalculateThreadCpuStatInfos(std::vector<ThreadCpuStatInfo> & threadCpuStatInfos,std::shared_ptr<ThreadCpuData> threadCpuData,bool isNeedUpdate)146 void ThreadStateInfoCollector::CalculateThreadCpuStatInfos(
147 std::vector<ThreadCpuStatInfo>& threadCpuStatInfos,
148 std::shared_ptr<ThreadCpuData> threadCpuData,
149 bool isNeedUpdate)
150 {
151 CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo();
152 HIVIEW_LOGD("startTime=%{public}" PRIu64 ", endTime=%{public}" PRIu64 ", startBootTime=%{public}" PRIu64
153 ", endBootTime=%{public}" PRIu64 ", period=%{public}" PRIu64, calcTimeInfo.startTime,
154 calcTimeInfo.endTime, calcTimeInfo.startBootTime, calcTimeInfo.endBootTime, calcTimeInfo.period);
155 auto procCpuItem = threadCpuData->GetNextThread();
156 while (procCpuItem != nullptr) {
157 auto threadCpuStatInfo = CalculateThreadCpuStatInfo(procCpuItem, calcTimeInfo);
158 if (threadCpuStatInfo.has_value()) {
159 threadCpuStatInfos.emplace_back(threadCpuStatInfo.value());
160 }
161 if (isNeedUpdate) {
162 UpdateLastThreadTimeInfo(procCpuItem, calcTimeInfo);
163 }
164 procCpuItem = threadCpuData->GetNextThread();
165 }
166
167 if (isNeedUpdate) {
168 UpdateCollectionTime(calcTimeInfo);
169 }
170 }
171
TryToDeleteDeadThreadInfo()172 void ThreadStateInfoCollector::TryToDeleteDeadThreadInfo()
173 {
174 for (auto it = lastThreadCpuTimeInfos_.begin(); it != lastThreadCpuTimeInfos_.end();) {
175 // if the latest collection operation does not update the process collection time, delete it
176 if (it->second.collectionTime != lastCollectionTime_) {
177 it = lastThreadCpuTimeInfos_.erase(it);
178 } else {
179 it++;
180 }
181 }
182 HIVIEW_LOGD("end to delete dead thread, size=%{public}zu", lastThreadCpuTimeInfos_.size());
183 }
184
CalculateThreadCpuStatInfo(const ucollection_thread_cpu_item * threadCpuItem,const CalculationTimeInfo & calcTimeInfo)185 std::optional<ThreadCpuStatInfo> ThreadStateInfoCollector::CalculateThreadCpuStatInfo(
186 const ucollection_thread_cpu_item* threadCpuItem, const CalculationTimeInfo& calcTimeInfo)
187 {
188 if (cpuCalculator_ == nullptr) {
189 return std::nullopt;
190 }
191 if (lastThreadCpuTimeInfos_.find(threadCpuItem->tid) == lastThreadCpuTimeInfos_.end()) {
192 return std::nullopt;
193 }
194 ThreadCpuStatInfo threadCpuStatInfo;
195 threadCpuStatInfo.startTime = calcTimeInfo.startTime;
196 threadCpuStatInfo.endTime = calcTimeInfo.endTime;
197 threadCpuStatInfo.tid = threadCpuItem->tid;
198 threadCpuStatInfo.cpuLoad = cpuCalculator_->CalculateCpuLoad(threadCpuItem->cpu_load_time,
199 lastThreadCpuTimeInfos_[threadCpuItem->tid].loadTime, calcTimeInfo.period);
200 threadCpuStatInfo.uCpuUsage = cpuCalculator_->CalculateCpuUsage(threadCpuItem->cpu_usage_utime,
201 lastThreadCpuTimeInfos_[threadCpuItem->tid].uUsageTime, calcTimeInfo.period);
202 threadCpuStatInfo.sCpuUsage = cpuCalculator_->CalculateCpuUsage(threadCpuItem->cpu_usage_stime,
203 lastThreadCpuTimeInfos_[threadCpuItem->tid].sUsageTime, calcTimeInfo.period);
204 threadCpuStatInfo.cpuUsage = threadCpuStatInfo.uCpuUsage + threadCpuStatInfo.sCpuUsage;
205 if (threadCpuStatInfo.cpuLoad >= 1) { // 1: max cpu load
206 HIVIEW_LOGI("invalid cpu load=%{public}f, last_load=%{public}" PRIu64
207 ", curr_load=%{public}" PRIu64, threadCpuStatInfo.cpuLoad,
208 lastThreadCpuTimeInfos_[threadCpuItem->tid].loadTime, static_cast<uint64_t>(threadCpuItem->cpu_load_time));
209 }
210 return std::make_optional<ThreadCpuStatInfo>(threadCpuStatInfo);
211 }
212 } // UCollectUtil
213 } // HiViewDFX
214 } // OHOS