1 /*
2 * Copyright (c) 2025 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 "gfx/performance/rs_perfmonitor_reporter.h"
16 #include "common/rs_background_thread.h"
17 #ifdef ROSEN_OHOS
18 #include "hisysevent.h"
19 #endif
20 #include "rs_trace.h"
21
22 namespace OHOS {
23 namespace Rosen {
24 #ifdef ROSEN_OHOS
25 const int32_t COUNTER_SIZE = 4;
26 const int32_t REASON_SIZE = 7;
27 const char* CPU_ONDRAW = "CPU_ONDRAW";
28 const char* CPU_FLUSH = "CPU_FLUSH";
29 //for rendergroup subhealth
30 constexpr int32_t DRAWING_CACHE_UPDATE_TIME_THRESHOLD = 3;
31 constexpr int32_t INTERVAL_THRESHOLD = 1000000;
32 constexpr int32_t DRAWING_CACHE_DURATION_TIMEOUT_THRESHOLD = 1000;
33 constexpr int32_t REPORT_INTERVAL = 120000000;
34 constexpr int32_t DRAWING_CACHE_MAX_CONTINUOUS_UPDATE_TIME = 7;
35 constexpr int32_t STORED_TIMESTAMP_COUNT = DRAWING_CACHE_UPDATE_TIME_THRESHOLD - 1;
36 constexpr int32_t PRINT_SUBHEALTH_TRACE_INTERVAL = 5000;
37 #endif
38
GetInstance()39 RSPerfMonitorReporter& RSPerfMonitorReporter::GetInstance()
40 {
41 static RSPerfMonitorReporter instance;
42 return instance;
43 }
44
SetFocusAppInfo(const char * bundleName)45 void RSPerfMonitorReporter::SetFocusAppInfo(const char* bundleName)
46 {
47 #ifdef ROSEN_OHOS
48 SetCurrentBundleName(bundleName);
49 if (!IsOpenPerf()) {
50 return;
51 }
52 auto task = [this]() {
53 this->ReportBlurStatEvent();
54 this->ReportTextureStatEvent();
55 this->ReportCacheReasonEvent();
56 Drawing::PerfmonitorReporter::ResetStatsData();
57 };
58 RSBackgroundThread::Instance().PostTask(task);
59 #endif
60 }
61
ReportAtRsFrameEnd()62 void RSPerfMonitorReporter::ReportAtRsFrameEnd()
63 {
64 #ifdef ROSEN_OHOS
65 if (!IsOpenPerf()) {
66 return;
67 }
68 auto task = [this]() {
69 this->ReportBlurPerfEvent();
70 this->ReportTexturePerfEvent();
71 Drawing::PerfmonitorReporter::ResetPerfEventData();
72 };
73 RSBackgroundThread::Instance().PostTask(task);
74 #endif
75 }
76
ReportBlurStatEvent()77 void RSPerfMonitorReporter::ReportBlurStatEvent()
78 {
79 #ifdef ROSEN_OHOS
80 std::string bundleName = GetCurrentBundleName();
81 std::map<std::string, std::vector<uint16_t>> uploadBlur;
82 {
83 std::lock_guard<std::mutex> guard(mtx_);
84 uploadBlur = statsBlur_;
85 statsBlur_.clear();
86 }
87 for (const auto& node : uploadBlur) {
88 RS_TRACE_NAME_FMT("SubHealthEvent RS_STATS_BLUR [%s], [%s]", bundleName.c_str(),
89 node.first.c_str());
90 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_STATS_BLUR",
91 HiviewDFX::HiSysEvent::EventType::STATISTIC, "BUNDLE_NAME", bundleName,
92 "NODE_NAME", node.first, "BLUR_TIME", node.second, "BLUR_STATUS", CPU_ONDRAW);
93 }
94 uploadBlur.clear();
95
96 uploadBlur = Drawing::PerfmonitorReporter::GetBlurStatsData();
97 for (const auto& node : uploadBlur) {
98 RS_TRACE_NAME_FMT("SubHealthEvent RS_STATS_BLUR [%s], [%s]", bundleName.c_str(),
99 node.first.c_str());
100 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_STATS_BLUR",
101 HiviewDFX::HiSysEvent::EventType::STATISTIC, "BUNDLE_NAME", bundleName,
102 "NODE_NAME", node.first, "BLUR_TIME", node.second, "BLUR_STATUS", CPU_FLUSH);
103 }
104 uploadBlur.clear();
105 #endif
106 }
107
ReportBlurPerfEvent()108 void RSPerfMonitorReporter::ReportBlurPerfEvent()
109 {
110 #ifdef ROSEN_OHOS
111 std::map<std::string, Drawing::RsBlurEvent> uploadEvents;
112 {
113 std::lock_guard<std::mutex> guard(mtx_);
114 uploadEvents = eventBlur_;
115 eventBlur_.clear();
116 }
117
118 std::string bundleName = GetCurrentBundleName();
119 for (const auto& [nodeName, node] : uploadEvents) {
120 RS_TRACE_NAME_FMT("SubHealthEvent RS_NODE_BLUR [%s], [%s], [%lld]", bundleName.c_str(),
121 nodeName.c_str(), node.fBlurTime);
122 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_NODE_BLUR",
123 HiviewDFX::HiSysEvent::EventType::FAULT, "PID", node.fPid, "NODE_NAME", nodeName,
124 "BUNDLE_NAME", bundleName, "FILTER_TYPE", node.fFilterType, "BLUR_RADIUS", node.fBlurRadius,
125 "BLUR_WIDTH", node.fWidth, "BLUR_HEIGHT", node.fHeight, "BLUR_TIME", node.fBlurTime,
126 "BLUR_STATUS", CPU_ONDRAW);
127 }
128 uploadEvents.clear();
129
130 uploadEvents = Drawing::PerfmonitorReporter::GetBlurPerfEventData();
131 for (const auto& [nodeName, node] : uploadEvents) {
132 RS_TRACE_NAME_FMT("SubHealthEvent RS_NODE_BLUR [%s], [%s], [%lld]", bundleName.c_str(),
133 nodeName.c_str(), node.fBlurTime);
134 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_NODE_BLUR",
135 HiviewDFX::HiSysEvent::EventType::FAULT, "PID", node.fPid, "NODE_NAME", nodeName,
136 "BUNDLE_NAME", bundleName, "FILTER_TYPE", node.fFilterType, "BLUR_RADIUS", node.fBlurRadius,
137 "BLUR_WIDTH", node.fWidth, "BLUR_HEIGHT", node.fHeight, "BLUR_TIME", node.fBlurTime,
138 "BLUR_STATUS", CPU_FLUSH);
139 }
140 uploadEvents.clear();
141 #endif
142 }
143
ReportCacheReasonEvent()144 void RSPerfMonitorReporter::ReportCacheReasonEvent()
145 {
146 #ifdef ROSEN_OHOS
147 std::string bundleName = GetCurrentBundleName();
148 std::map<std::string, std::vector<uint16_t>> uploadReason;
149 {
150 std::lock_guard<std::mutex> guard(mtx_);
151 uploadReason = statsReason_;
152 statsReason_.clear();
153 }
154 for (const auto& node : uploadReason) {
155 RS_TRACE_NAME_FMT("SubHealthEvent RS_STATS_CAHCHE [%s], [%s]", bundleName.c_str(),
156 node.first.c_str());
157 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_STATS_CAHCHE",
158 HiviewDFX::HiSysEvent::EventType::STATISTIC, "BUNDLE_NAME", bundleName,
159 "NODE_NAME", node.first, "CACHE_REASON", node.second);
160 }
161 uploadReason.clear();
162 #endif
163 }
164
ReportTextureStatEvent()165 void RSPerfMonitorReporter::ReportTextureStatEvent()
166 {
167 #ifdef ROSEN_OHOS
168 std::string bundleName = GetCurrentBundleName();
169 std::map<std::string, std::vector<uint16_t>> uploadTexture =
170 Drawing::PerfmonitorReporter::GetTextureStatsData();
171 for (const auto& node : uploadTexture) {
172 RS_TRACE_NAME_FMT("SubHealthEvent RS_STATS_TEXTURE [%s], [%s]",
173 bundleName.c_str(), node.first.c_str());
174 HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_STATS_TEXTURE",
175 OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, "BUNDLE_NAME", bundleName,
176 "NODE_NAME", node.first, "TEXTURE_TIME", node.second, "TEXTURE_STATUS", CPU_FLUSH);
177 }
178 #endif
179 }
180
ReportTexturePerfEvent()181 void RSPerfMonitorReporter::ReportTexturePerfEvent()
182 {
183 #ifdef ROSEN_OHOS
184 std::string bundleName = GetCurrentBundleName();
185 std::map<std::string, Drawing::RsTextureEvent> uploadEvents =
186 Drawing::PerfmonitorReporter::GetTexturePerfEventData();
187 for (const auto& [nodeName, node] : uploadEvents) {
188 RS_TRACE_NAME_FMT("SubHealthEvent RS_NODE_TEXTURE [%s], [%s], [%lld]",
189 bundleName.c_str(), nodeName.c_str(), node.fAllocTime);
190 HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::GRAPHIC, "RS_NODE_TEXTURE",
191 OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, "PID", node.fPid, "NODE_NAME", nodeName,
192 "BUNDLE_NAME", bundleName, "TEXTURE_TIME", node.fAllocTime, "FMAX_BYTES", node.fMaxBytes,
193 "FBUDGETED_BYTES", node.fBudgetedBytes, "CLEAR_CACHE", node.fClearCache,
194 "TEXTURE_STATUS", CPU_FLUSH);
195 }
196 #endif
197 }
198
RecordBlurPerfEvent(NodeId nodeId,const std::string & nodeName,uint16_t filterType,float blurRadius,int32_t width,int32_t height,int64_t blurTime,bool isBlurType)199 void RSPerfMonitorReporter::RecordBlurPerfEvent(NodeId nodeId, const std::string& nodeName,
200 uint16_t filterType, float blurRadius, int32_t width, int32_t height, int64_t blurTime,
201 bool isBlurType)
202 {
203 #ifdef ROSEN_OHOS
204 if (!IsOpenPerf() || !isBlurType || nodeName.empty() ||
205 blurTime < Drawing::COUNTER_MS_THIRD_TYPE) {
206 return;
207 }
208 int32_t pid = ExtractPid(nodeId);
209 Drawing::RsBlurEvent bEvent;
210 bEvent.initEvent(pid, filterType, blurRadius, width, height, blurTime);
211 std::lock_guard<std::mutex> guard(mtx_);
212 eventBlur_[nodeName] = bEvent;
213 #endif
214 }
215
RecordBlurNode(const std::string & nodeName,int64_t duration,bool isBlurType)216 void RSPerfMonitorReporter::RecordBlurNode(const std::string& nodeName, int64_t duration,
217 bool isBlurType)
218 {
219 #ifdef ROSEN_OHOS
220 int16_t cType = Drawing::PerfmonitorReporter::GetSplitRange(duration);
221 if (!IsOpenPerf() || !isBlurType || nodeName.empty() ||
222 cType <= Drawing::COUNTER_INVALID_TYPE) {
223 return;
224 }
225 std::lock_guard<std::mutex> guard(mtx_);
226 if (statsBlur_.find(nodeName) == statsBlur_.end()) {
227 statsBlur_[nodeName] = std::vector<uint16_t>(COUNTER_SIZE, 0);
228 }
229 statsBlur_[nodeName][cType]++;
230 #endif
231 }
232
RecordBlurCacheReason(const std::string & nodeName,BLUR_CLEAR_CACHE_REASON reason,bool isBlurType)233 void RSPerfMonitorReporter::RecordBlurCacheReason(const std::string& nodeName, BLUR_CLEAR_CACHE_REASON reason,
234 bool isBlurType)
235 {
236 #ifdef ROSEN_OHOS
237 if (!IsOpenPerf() || !isBlurType || nodeName.empty()) {
238 return;
239 }
240 std::lock_guard<std::mutex> guard(mtx_);
241 if (statsReason_.find(nodeName) == statsReason_.end()) {
242 statsReason_[nodeName] = std::vector<uint16_t>(REASON_SIZE, 0);
243 }
244 statsReason_[nodeName][reason]++;
245 #endif
246 }
247
SetCurrentBundleName(const char * bundleName)248 void RSPerfMonitorReporter::SetCurrentBundleName(const char* bundleName)
249 {
250 std::lock_guard<std::mutex> guard(mtx_);
251 currentBundleName_ = bundleName;
252 }
253
GetCurrentBundleName()254 std::string RSPerfMonitorReporter::GetCurrentBundleName()
255 {
256 std::lock_guard<std::mutex> guard(mtx_);
257 return currentBundleName_;
258 }
259
IsOpenPerf()260 bool RSPerfMonitorReporter::IsOpenPerf()
261 {
262 #ifdef ROSEN_OHOS
263 static bool isOpenPerf = Drawing::PerfmonitorReporter::IsOpenPerf();
264 return isOpenPerf;
265 #else
266 return false;
267 #endif
268 }
269
StartRendergroupMonitor()270 std::chrono::time_point<high_resolution_clock> RSPerfMonitorReporter::StartRendergroupMonitor()
271 {
272 return high_resolution_clock::now();
273 }
274
EndRendergroupMonitor(std::chrono::time_point<high_resolution_clock> & startTime,NodeId & nodeId,int updateTimes)275 void RSPerfMonitorReporter::EndRendergroupMonitor(std::chrono::time_point<high_resolution_clock>& startTime,
276 NodeId& nodeId, int updateTimes)
277 {
278 #ifdef ROSEN_OHOS
279 auto endTime = high_resolution_clock::now();
280 auto interval = std::chrono::duration_cast<microseconds>(endTime - startTime);
281 bool needTrace = interval.count() > PRINT_SUBHEALTH_TRACE_INTERVAL;
282 if (needTrace) {
283 RS_TRACE_BEGIN("SubHealthEvent Rendergroup, updateCache interval:" + std::to_string(interval.count()));
284 }
285 ProcessRendergroupSubhealth(nodeId, updateTimes, interval.count(), startTime);
286 if (needTrace) {
287 RS_TRACE_END();
288 }
289 #endif
290 }
291
ClearRendergroupDataMap(NodeId & nodeId)292 void RSPerfMonitorReporter::ClearRendergroupDataMap(NodeId& nodeId)
293 {
294 #ifdef ROSEN_OHOS
295 {
296 std::lock_guard<std::mutex> lock(drawingCacheTimeTakenMapMutex_);
297 drawingCacheTimeTakenMap_.erase(nodeId);
298 }
299 {
300 std::lock_guard<std::mutex> lock(drawingCacheLastTwoTimestampMapMutex_);
301 drawingCacheLastTwoTimestampMap_.erase(nodeId);
302 }
303 #endif
304 }
305
ProcessRendergroupSubhealth(NodeId & nodeId,int updateTimes,int interval,std::chrono::time_point<high_resolution_clock> & startTime)306 void RSPerfMonitorReporter::ProcessRendergroupSubhealth(NodeId& nodeId, int updateTimes, int interval,
307 std::chrono::time_point<high_resolution_clock>& startTime)
308 {
309 #ifdef ROSEN_OHOS
310 {
311 std::lock_guard<std::mutex> lock(drawingCacheTimeTakenMapMutex_);
312 drawingCacheTimeTakenMap_[nodeId].emplace_back(interval);
313 if (drawingCacheTimeTakenMap_[nodeId].size() > DRAWING_CACHE_UPDATE_TIME_THRESHOLD) {
314 drawingCacheTimeTakenMap_[nodeId].erase(drawingCacheTimeTakenMap_[nodeId].begin());
315 }
316 }
317 // check if need hisysevent report
318 if (NeedReportSubHealth(nodeId, updateTimes, startTime)) {
319 auto reportTime = high_resolution_clock::now();
320 std::string bundleName = GetCurrentBundleName();
321 std::string timeTaken = GetUpdateCacheTimeTaken(nodeId);
322 RSBackgroundThread::Instance().PostTask([nodeId, bundleName, updateTimes, timeTaken]() {
323 RS_TRACE_NAME("RSPerfMonitorReporter::ProcessRendergroupSubhealth HiSysEventWrite in RSBackgroundThread");
324 HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::GRAPHIC, RENDERGROUP_SUBHEALTH_EVENT_NAME,
325 OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
326 "NODE_ID", nodeId,
327 "BUNDLE_NAME", bundleName,
328 "CONTINUOUS_UPDATE_CACHE_TIMES", updateTimes,
329 "UPDATE_CACHE_TIME_TAKEN", timeTaken);
330 });
331 {
332 std::lock_guard<std::mutex> lock(drawingCacheLastReportTimeMapMutex_);
333 drawingCacheLastReportTimeMap_[nodeId] = reportTime;
334 }
335 }
336 {
337 std::lock_guard<std::mutex> lock(drawingCacheLastTwoTimestampMapMutex_);
338 drawingCacheLastTwoTimestampMap_[nodeId].push(startTime);
339 if (drawingCacheLastTwoTimestampMap_[nodeId].size() > STORED_TIMESTAMP_COUNT) {
340 drawingCacheLastTwoTimestampMap_[nodeId].pop();
341 }
342 }
343 #endif
344 }
345
NeedReportSubHealth(NodeId & nodeId,int updateTimes,std::chrono::time_point<high_resolution_clock> & startTime)346 bool RSPerfMonitorReporter::NeedReportSubHealth(NodeId& nodeId, int updateTimes,
347 std::chrono::time_point<high_resolution_clock>& startTime)
348 {
349 #ifdef ROSEN_OHOS
350 if (!MeetReportFrequencyControl(nodeId, startTime)) {
351 return false;
352 }
353 {
354 std::lock_guard<std::mutex> lock(drawingCacheLastTwoTimestampMapMutex_);
355 size_t timestampCounts = drawingCacheLastTwoTimestampMap_[nodeId].size();
356 if (timestampCounts != STORED_TIMESTAMP_COUNT) {
357 return false;
358 }
359 auto firstTimestamp = drawingCacheLastTwoTimestampMap_[nodeId].front();
360 auto interval = std::chrono::duration_cast<microseconds>(startTime - firstTimestamp);
361 if (interval.count() > INTERVAL_THRESHOLD) {
362 return false;
363 }
364 }
365 if (CheckAllDrawingCacheDurationTimeout(nodeId)) {
366 return true;
367 }
368 if (updateTimes >= DRAWING_CACHE_MAX_CONTINUOUS_UPDATE_TIME) {
369 return true;
370 }
371 return false;
372 #else
373 return false;
374 #endif
375 }
376
CheckAllDrawingCacheDurationTimeout(NodeId & nodeId)377 bool RSPerfMonitorReporter::CheckAllDrawingCacheDurationTimeout(NodeId& nodeId)
378 {
379 #ifdef ROSEN_OHOS
380 std::lock_guard<std::mutex> lock(drawingCacheTimeTakenMapMutex_);
381 for (auto& it: drawingCacheTimeTakenMap_[nodeId]) {
382 if (it < DRAWING_CACHE_DURATION_TIMEOUT_THRESHOLD) {
383 return false;
384 }
385 }
386 return true;
387 #else
388 return false;
389 #endif
390 }
391
MeetReportFrequencyControl(NodeId & nodeId,std::chrono::time_point<high_resolution_clock> & startTime)392 bool RSPerfMonitorReporter::MeetReportFrequencyControl(NodeId& nodeId,
393 std::chrono::time_point<high_resolution_clock>& startTime)
394 {
395 #ifdef ROSEN_OHOS
396 std::lock_guard<std::mutex> lock(drawingCacheLastReportTimeMapMutex_);
397 if (drawingCacheLastReportTimeMap_.find(nodeId) == drawingCacheLastReportTimeMap_.end()) {
398 return true;
399 } else {
400 auto lastTime = drawingCacheLastReportTimeMap_[nodeId];
401 if (std::chrono::duration_cast<microseconds>(startTime - lastTime).count() > REPORT_INTERVAL) {
402 return true;
403 }
404 }
405 return false;
406 #else
407 return false;
408 #endif
409 }
410
GetUpdateCacheTimeTaken(NodeId & nodeId)411 std::string RSPerfMonitorReporter::GetUpdateCacheTimeTaken(NodeId& nodeId)
412 {
413 std::string result;
414 #ifdef ROSEN_OHOS
415 std::lock_guard<std::mutex> lock(drawingCacheTimeTakenMapMutex_);
416 if (drawingCacheTimeTakenMap_.find(nodeId) != drawingCacheTimeTakenMap_.end()) {
417 for (auto& it: drawingCacheTimeTakenMap_[nodeId]) {
418 result += std::to_string(it) + ",";
419 }
420 }
421 if (result.size() > 0 && result.back() == ',') {
422 result.pop_back();
423 }
424 return result;
425 #else
426 return result;
427 #endif
428 }
429
430 } // namespace Rosen
431 } // namespace OHOS
432