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 HIVIEW_LOGD("init thread info");
68 auto threadCpuData = FetchThreadCpuData();
69 if (threadCpuData == nullptr) {
70 return;
71 }
72
73 // init cpu time information for each thread in the system
74 CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo();
75 auto threadCpuItem = threadCpuData->GetNextThread();
76 while (threadCpuItem != nullptr) {
77 UpdateLastThreadTimeInfo(threadCpuItem, calcTimeInfo);
78 threadCpuItem = threadCpuData->GetNextThread();
79 }
80 UpdateCollectionTime(calcTimeInfo);
81 }
82
InitCalculationTimeInfo()83 CalculationTimeInfo ThreadStateInfoCollector::InitCalculationTimeInfo()
84 {
85 CalculationTimeInfo calcTimeInfo = {
86 .startTime = lastCollectionTime_,
87 .endTime = TimeUtil::GetMilliseconds(),
88 .startBootTime = lastCollectionBootTime_,
89 .endBootTime = TimeUtil::GetBootTimeMs(),
90 };
91 calcTimeInfo.period = calcTimeInfo.endBootTime > calcTimeInfo.startBootTime
92 ? (calcTimeInfo.endBootTime - calcTimeInfo.startBootTime) : 0;
93 return calcTimeInfo;
94 }
95
UpdateLastThreadTimeInfo(const ucollection_thread_cpu_item * threadCpuItem,const CalculationTimeInfo & calcTimeInfo)96 void ThreadStateInfoCollector::UpdateLastThreadTimeInfo(const ucollection_thread_cpu_item* threadCpuItem,
97 const CalculationTimeInfo& calcTimeInfo)
98 {
99 lastThreadCpuTimeInfos_[threadCpuItem->tid] = {
100 .uUsageTime = threadCpuItem->cpu_usage_utime,
101 .sUsageTime = threadCpuItem->cpu_usage_stime,
102 .loadTime = threadCpuItem->cpu_load_time,
103 .collectionTime = calcTimeInfo.endTime,
104 .collectionBootTime = calcTimeInfo.endBootTime,
105 };
106 }
107
UpdateCollectionTime(const CalculationTimeInfo & calcTimeInfo)108 void ThreadStateInfoCollector::UpdateCollectionTime(const CalculationTimeInfo& calcTimeInfo)
109 {
110 lastCollectionTime_ = calcTimeInfo.endTime;
111 lastCollectionBootTime_ = calcTimeInfo.endBootTime;
112 }
113
CollectThreadStatInfos(bool isNeedUpdate)114 CollectResult<std::vector<ThreadCpuStatInfo>> ThreadStateInfoCollector::CollectThreadStatInfos(bool isNeedUpdate)
115 {
116 CollectResult<std::vector<ThreadCpuStatInfo>> threadCollectResult;
117 std::unique_lock<std::mutex> lock(collectMutex_);
118 auto threadCpuData = FetchThreadCpuData();
119 if (threadCpuData == nullptr) {
120 return threadCollectResult;
121 }
122 CalculateThreadCpuStatInfos(threadCollectResult.data, threadCpuData, isNeedUpdate);
123 HIVIEW_LOGD("collect thread cpu statistics information size=%{public}zu, isNeedUpdate=%{public}d",
124 threadCollectResult.data.size(), isNeedUpdate);
125 if (!threadCollectResult.data.empty()) {
126 threadCollectResult.retCode = UCollect::UcError::SUCCESS;
127 TryToDeleteDeadThreadInfo();
128 }
129 return threadCollectResult;
130 }
131
GetCollectPid()132 int ThreadStateInfoCollector::GetCollectPid()
133 {
134 return collectPid_;
135 }
136
FetchThreadCpuData()137 std::shared_ptr<ThreadCpuData> ThreadStateInfoCollector::FetchThreadCpuData()
138 {
139 if (deviceClient_ == nullptr) {
140 HIVIEW_LOGW("device client is null");
141 return nullptr;
142 }
143 return (collectPid_ == getprocpid()) ? deviceClient_->FetchSelfThreadCpuData(collectPid_)
144 : deviceClient_->FetchThreadCpuData(collectPid_);
145 }
146
CalculateThreadCpuStatInfos(std::vector<ThreadCpuStatInfo> & threadCpuStatInfos,std::shared_ptr<ThreadCpuData> threadCpuData,bool isNeedUpdate)147 void ThreadStateInfoCollector::CalculateThreadCpuStatInfos(
148 std::vector<ThreadCpuStatInfo>& threadCpuStatInfos,
149 std::shared_ptr<ThreadCpuData> threadCpuData,
150 bool isNeedUpdate)
151 {
152 CalculationTimeInfo calcTimeInfo = InitCalculationTimeInfo();
153 HIVIEW_LOGD("startTime=%{public}" PRIu64 ", endTime=%{public}" PRIu64 ", startBootTime=%{public}" PRIu64
154 ", endBootTime=%{public}" PRIu64 ", period=%{public}" PRIu64, calcTimeInfo.startTime,
155 calcTimeInfo.endTime, calcTimeInfo.startBootTime, calcTimeInfo.endBootTime, calcTimeInfo.period);
156 auto procCpuItem = threadCpuData->GetNextThread();
157 while (procCpuItem != nullptr) {
158 auto threadCpuStatInfo = CalculateThreadCpuStatInfo(procCpuItem, calcTimeInfo);
159 if (threadCpuStatInfo.has_value()) {
160 threadCpuStatInfos.emplace_back(threadCpuStatInfo.value());
161 }
162 if (isNeedUpdate) {
163 UpdateLastThreadTimeInfo(procCpuItem, calcTimeInfo);
164 }
165 procCpuItem = threadCpuData->GetNextThread();
166 }
167
168 if (isNeedUpdate) {
169 UpdateCollectionTime(calcTimeInfo);
170 }
171 }
172
TryToDeleteDeadThreadInfo()173 void ThreadStateInfoCollector::TryToDeleteDeadThreadInfo()
174 {
175 for (auto it = lastThreadCpuTimeInfos_.begin(); it != lastThreadCpuTimeInfos_.end();) {
176 // if the latest collection operation does not update the process collection time, delete it
177 if (it->second.collectionTime != lastCollectionTime_) {
178 it = lastThreadCpuTimeInfos_.erase(it);
179 } else {
180 it++;
181 }
182 }
183 HIVIEW_LOGD("end to delete dead thread, size=%{public}zu", lastThreadCpuTimeInfos_.size());
184 }
185
CalculateThreadCpuStatInfo(const ucollection_thread_cpu_item * threadCpuItem,const CalculationTimeInfo & calcTimeInfo)186 std::optional<ThreadCpuStatInfo> ThreadStateInfoCollector::CalculateThreadCpuStatInfo(
187 const ucollection_thread_cpu_item* threadCpuItem, const CalculationTimeInfo& calcTimeInfo)
188 {
189 if (cpuCalculator_ == nullptr) {
190 return std::nullopt;
191 }
192 if (lastThreadCpuTimeInfos_.find(threadCpuItem->tid) == lastThreadCpuTimeInfos_.end()) {
193 return std::nullopt;
194 }
195 ThreadCpuStatInfo threadCpuStatInfo;
196 threadCpuStatInfo.startTime = calcTimeInfo.startTime;
197 threadCpuStatInfo.endTime = calcTimeInfo.endTime;
198 threadCpuStatInfo.tid = threadCpuItem->tid;
199 threadCpuStatInfo.threadName = threadCpuItem->name;
200 threadCpuStatInfo.cpuLoad = cpuCalculator_->CalculateCpuLoad(threadCpuItem->cpu_load_time,
201 lastThreadCpuTimeInfos_[threadCpuItem->tid].loadTime, calcTimeInfo.period);
202 threadCpuStatInfo.uCpuUsage = cpuCalculator_->CalculateCpuUsage(threadCpuItem->cpu_usage_utime,
203 lastThreadCpuTimeInfos_[threadCpuItem->tid].uUsageTime, calcTimeInfo.period);
204 threadCpuStatInfo.sCpuUsage = cpuCalculator_->CalculateCpuUsage(threadCpuItem->cpu_usage_stime,
205 lastThreadCpuTimeInfos_[threadCpuItem->tid].sUsageTime, calcTimeInfo.period);
206 threadCpuStatInfo.cpuUsage = threadCpuStatInfo.uCpuUsage + threadCpuStatInfo.sCpuUsage;
207 if (threadCpuStatInfo.cpuLoad >= 1) { // 1: max cpu load
208 HIVIEW_LOGI("invalid cpu load=%{public}f, last_load=%{public}" PRIu64
209 ", curr_load=%{public}" PRIu64, threadCpuStatInfo.cpuLoad,
210 lastThreadCpuTimeInfos_[threadCpuItem->tid].loadTime, static_cast<uint64_t>(threadCpuItem->cpu_load_time));
211 }
212 return std::make_optional<ThreadCpuStatInfo>(threadCpuStatInfo);
213 }
214 } // UCollectUtil
215 } // HiViewDFX
216 } // OHOS
217
218 #ifdef __cplusplus
219 extern "C" {
220 #endif
GetThreadCpuLoad(int32_t pid)221 __attribute__((visibility ("default"))) double GetThreadCpuLoad(int32_t pid)
222 {
223 OHOS::HiviewDFX::CollectResult<std::vector<OHOS::HiviewDFX::ThreadCpuStatInfo>> threadCollectResult;
224 threadCollectResult = OHOS::HiviewDFX::UCollectUtil::ThreadCpuCollector::Create(pid)->CollectThreadStatInfos();
225 if (threadCollectResult.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
226 return -1;
227 }
228 std::vector<OHOS::HiviewDFX::ThreadCpuStatInfo> threadCpuStatInfo = threadCollectResult.data;
229 for (const auto& info : threadCpuStatInfo) {
230 if (info.tid == pid) {
231 return info.cpuLoad;
232 }
233 }
234 return -1;
235 }
236 #ifdef __cplusplus
237 }
238 #endif
239