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