• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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