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