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