• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 
16 #include "base/log/ace_performance_check.h"
17 #include "base/i18n/localization.h"
18 #include "base/log/ace_checker.h"
19 #include "base/log/dump_log.h"
20 #include "base/log/event_report.h"
21 #include "bridge/common/utils/utils.h"
22 #include "core/common/container.h"
23 #include "base/websocket/websocket_manager.h"
24 namespace OHOS::Ace {
25 namespace {
26 constexpr int32_t BASE_YEAR = 1900;
27 constexpr char DATE_FORMAT[] = "MM-dd HH:mm:ss";
28 constexpr int32_t CONVERT_NANOSECONDS = 1000000;
29 constexpr int32_t FUNCTION_TIMEOUT = 150;
30 constexpr char ETS_PATH[] = "/src/main/ets/";
31 constexpr char DEBUG_PATH[] = "entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/";
32 constexpr char NEW_PATH[] = "entry|entry|1.0.0|src/main/ets/";
33 constexpr char TS_SUFFIX[] = ".ts";
34 constexpr char ETS_SUFFIX[] = ".ets";
35 constexpr char CHECK_RESULT[] = "{\"message_type\": \"SendArkPerformanceCheckResult\", \"performance_check_result\": ";
36 } // namespace
37 
38 // ============================== survival interval of JSON files ============================================
39 
40 std::unique_ptr<JsonValue> AcePerformanceCheck::performanceInfo_ = nullptr;
41 std::string AceScopedPerformanceCheck::currentPath_;
42 std::string AceScopedPerformanceCheck::recordPath_;
43 std::vector<std::pair<int64_t, std::string>> AceScopedPerformanceCheck::records_;
Start()44 void AcePerformanceCheck::Start()
45 {
46     if (AceChecker::IsPerformanceCheckEnabled()) {
47         LOGI("performance check start");
48         performanceInfo_ = JsonUtil::Create(true);
49     }
50 }
51 
Stop()52 void AcePerformanceCheck::Stop()
53 {
54     if (performanceInfo_) {
55         LOGI("performance check stop");
56         auto info = performanceInfo_->ToString();
57         if (AceChecker::IsWebSocketCheckEnabled()) {
58             info = CHECK_RESULT + info + "}";
59             WebSocketManager::SendMessage(info);
60         } else {
61             // output info to json file
62             auto filePath = AceApplicationInfo::GetInstance().GetDataFileDirPath() + "/arkui_bestpractice.json";
63             std::unique_ptr<std::ostream> ss = std::make_unique<std::ofstream>(filePath);
64             CHECK_NULL_VOID(ss);
65             DumpLog::GetInstance().SetDumpFile(std::move(ss));
66             DumpLog::GetInstance().Print(info);
67             DumpLog::GetInstance().Reset();
68             AceChecker::NotifyCaution("AcePerformanceCheck::Stop, json data generated, store in " + filePath);
69         }
70         performanceInfo_.reset(nullptr);
71     }
72 }
73 
74 // ============================== specific implementation ======================================================
75 
AceScopedPerformanceCheck(const std::string & name)76 AceScopedPerformanceCheck::AceScopedPerformanceCheck(const std::string& name)
77 {
78     // micro time.
79     markTime_ = GetSysTimestamp();
80     name_ = name;
81 }
82 
~AceScopedPerformanceCheck()83 AceScopedPerformanceCheck::~AceScopedPerformanceCheck()
84 {
85     auto time = static_cast<int64_t>((GetSysTimestamp() - markTime_) / CONVERT_NANOSECONDS);
86     if (time >= FUNCTION_TIMEOUT) {
87         EventReport::ReportFunctionTimeout(name_, time, FUNCTION_TIMEOUT);
88     }
89     if (AcePerformanceCheck::performanceInfo_) {
90         // convert micro time to ms with 1000.
91         std::pair recordInfo { time, name_ };
92         records_.push_back(recordInfo);
93     }
94 }
95 
CheckIsRuleContainsPage(const std::string & ruleType,const std::string & pagePath)96 bool AceScopedPerformanceCheck::CheckIsRuleContainsPage(const std::string& ruleType, const std::string& pagePath)
97 {
98     // check for the presence of rule json
99     CHECK_NULL_RETURN(AcePerformanceCheck::performanceInfo_, false);
100     if (AceChecker::IsWebSocketCheckEnabled()) {
101         if (!CheckIsRuleWebsocket(ruleType)) {
102             return true;
103         }
104     }
105     if (!AcePerformanceCheck::performanceInfo_->Contains(ruleType)) {
106         AcePerformanceCheck::performanceInfo_->Put(ruleType.c_str(), JsonUtil::CreateArray(true));
107         return false;
108     }
109     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue(ruleType);
110     auto size = ruleJson->GetArraySize();
111     for (int32_t i = 0; i < size; i++) {
112         auto indexJson = ruleJson->GetArrayItem(i);
113         auto value = indexJson->GetString("pagePath", {});
114         if (value == pagePath) {
115             return true;
116         }
117     }
118     return false;
119 }
120 
GetCurrentTime()121 std::string AceScopedPerformanceCheck::GetCurrentTime()
122 {
123     // get system date and time
124     auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
125     auto* local = std::localtime(&now);
126     if (!local) {
127         return {};
128     }
129 
130     // this is for i18n date time
131     DateTime dateTime;
132     dateTime.year = static_cast<uint32_t>(local->tm_year + BASE_YEAR);
133     dateTime.month = static_cast<uint32_t>(local->tm_mon);
134     dateTime.day = static_cast<uint32_t>(local->tm_mday);
135     dateTime.hour = static_cast<uint32_t>(local->tm_hour);
136     dateTime.minute = static_cast<uint32_t>(local->tm_min);
137     dateTime.second = static_cast<uint32_t>(local->tm_sec);
138     auto time = Localization::GetInstance()->FormatDateTime(dateTime, DATE_FORMAT);
139     return time;
140 }
141 
GetCodeInfo(int32_t row,int32_t col)142 CodeInfo AceScopedPerformanceCheck::GetCodeInfo(int32_t row, int32_t col)
143 {
144     auto sourceMap = GetCurrentSourceMap();
145     CHECK_NULL_RETURN(sourceMap, {});
146     // There is no same row and column info of viewPU in sourcemap, but the row info is correct.
147     auto codeInfo = sourceMap->Find(row, col, false);
148     return { codeInfo.row, codeInfo.col, codeInfo.sources };
149 }
150 
CheckPage(const CodeInfo & codeInfo,const std::string & rule)151 bool AceScopedPerformanceCheck::CheckPage(const CodeInfo& codeInfo, const std::string& rule)
152 {
153     if (!codeInfo.sources.empty() && CheckIsRuleContainsPage(rule, codeInfo.sources)) {
154         return true;
155     }
156     return false;
157 }
158 
UpdateRecordPath(const std::string & path)159 void AceScopedPerformanceCheck::UpdateRecordPath(const std::string& path)
160 {
161     recordPath_ = path;
162 }
163 
ReportAllRecord()164 void AceScopedPerformanceCheck::ReportAllRecord()
165 {
166     RecordFunctionTimeout();
167 }
168 
RecordPerformanceCheckData(const PerformanceCheckNodeMap & nodeMap,int64_t vsyncTimeout,std::string path,std::string fromPath,std::string moduleName,bool isNavigation)169 void AceScopedPerformanceCheck::RecordPerformanceCheckData(const PerformanceCheckNodeMap& nodeMap, int64_t vsyncTimeout,
170     std::string path, std::string fromPath, std::string moduleName, bool isNavigation)
171 {
172     currentPath_ = path;
173     auto codeInfo = GetCodeInfo(1, 1);
174     std::vector<PerformanceCheckNode> pageNodeList;
175     std::vector<PerformanceCheckNode> flexNodeList;
176     std::unordered_map<int32_t, PerformanceCheckNode> foreachNodeMap;
177     int32_t itemCount = 0;
178     int32_t maxDepth = 0;
179     for (const auto& node : nodeMap) {
180         if (node.second.childrenSize >= AceChecker::GetNodeChildren()) {
181             pageNodeList.emplace_back(node.second);
182         }
183         if (node.second.pageDepth > maxDepth) {
184             maxDepth = node.second.pageDepth;
185         }
186         if (node.second.flexLayouts != 0 && node.second.flexLayouts >= AceChecker::GetFlexLayouts()) {
187             flexNodeList.emplace_back(node.second);
188         }
189         if (node.second.isForEachItem) {
190             itemCount++;
191             auto iter = foreachNodeMap.find(node.second.codeRow);
192             if (iter != foreachNodeMap.end()) {
193                 iter->second.foreachItems++;
194             } else {
195                 foreachNodeMap.insert(std::make_pair(node.second.codeRow, node.second));
196             }
197         }
198     }
199     std::string pageRoute;
200     if (isNavigation) {
201         pageRoute = "H:NavDestination Page from " + fromPath + " to " + path + ", navDestinationName: " + moduleName;
202     } else {
203         pageRoute = "H:Router Page to " + path;
204     }
205     RecordFunctionTimeout();
206     RecordPageNodeCountAndDepth(nodeMap.size(), maxDepth, pageNodeList, codeInfo, pageRoute);
207     RecordForEachItemsCount(itemCount, foreachNodeMap, codeInfo, pageRoute);
208     RecordFlexLayoutsCount(flexNodeList, codeInfo, pageRoute);
209     RecordVsyncTimeout(nodeMap, vsyncTimeout / CONVERT_NANOSECONDS, codeInfo, pageRoute);
210 }
211 
RecordPageNodeCountAndDepth(int32_t pageNodeCount,int32_t pageDepth,std::vector<PerformanceCheckNode> & pageNodeList,const CodeInfo & codeInfo,const std::string & pageRoute)212 void AceScopedPerformanceCheck::RecordPageNodeCountAndDepth(int32_t pageNodeCount, int32_t pageDepth,
213     std::vector<PerformanceCheckNode>& pageNodeList, const CodeInfo& codeInfo, const std::string& pageRoute)
214 {
215     if ((pageNodeCount < AceChecker::GetPageNodes() && pageDepth < AceChecker::GetPageDepth()) ||
216         CheckPage(codeInfo, "9901")) {
217         return;
218     }
219     auto eventTime = GetCurrentTime();
220     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
221     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9901");
222     auto pageJson = JsonUtil::Create(true);
223     pageJson->Put("eventTime", eventTime.c_str());
224     pageJson->Put("pagePath", codeInfo.sources.c_str());
225     pageJson->Put("nodeCount", pageNodeCount);
226     pageJson->Put("depth", pageDepth);
227     pageJson->Put("pageRoute", pageRoute.c_str());
228     // add children size > 100 of component to pageJson
229     for (const auto& iter : pageNodeList) {
230         auto componentJson = JsonUtil::Create(true);
231         componentJson->Put("name", iter.nodeTag.c_str());
232         componentJson->Put("items", iter.childrenSize);
233         componentJson->Put("sourceLine", GetCodeInfo(iter.codeRow, iter.codeCol).row);
234         componentJson->Put("pagePath", iter.pagePath.c_str());
235         std::unique_ptr<JsonValue> componentsJson;
236         if (pageJson->Contains("components")) {
237             componentsJson = pageJson->GetValue("components");
238             componentsJson->Put(componentJson);
239         } else {
240             componentsJson = JsonUtil::CreateArray(true);
241             componentsJson->Put(componentJson);
242             pageJson->Put("components", componentsJson);
243         }
244     }
245     LOGI("pageJson 9901: %{public}s", pageJson->ToString().c_str());
246     ruleJson->Put(pageJson);
247 }
248 
RecordFunctionTimeout()249 void AceScopedPerformanceCheck::RecordFunctionTimeout()
250 {
251     for (auto record : records_) {
252         if (record.first < AceChecker::GetFunctionTimeout()) {
253             continue;
254         }
255         auto codeInfo = GetCodeInfo(1, 1);
256         CheckIsRuleContainsPage("9902", codeInfo.sources);
257         auto eventTime = GetCurrentTime();
258         CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
259         auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9902");
260         auto pageJson = JsonUtil::Create(true);
261         pageJson->Put("eventTime", eventTime.c_str());
262         pageJson->Put("pagePath", recordPath_.c_str());
263         pageJson->Put("functionName", record.second.c_str());
264         pageJson->Put("costTime", record.first);
265         ruleJson->Put(pageJson);
266         LOGI("pageJson 9902: %{public}s", pageJson->ToString().c_str());
267     }
268     records_.clear();
269 }
270 
RecordVsyncTimeout(const PerformanceCheckNodeMap & nodeMap,int64_t vsyncTimeout,const CodeInfo & codeInfo,const std::string & pageRoute)271 void AceScopedPerformanceCheck::RecordVsyncTimeout(
272     const PerformanceCheckNodeMap& nodeMap, int64_t vsyncTimeout,
273     const CodeInfo& codeInfo, const std::string& pageRoute)
274 {
275     if (vsyncTimeout < AceChecker::GetVsyncTimeout() || CheckPage(codeInfo, "9903")) {
276         return;
277     }
278     auto eventTime = GetCurrentTime();
279     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
280     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9903");
281     auto pageJson = JsonUtil::Create(true);
282     pageJson->Put("eventTime", eventTime.c_str());
283     pageJson->Put("pagePath", codeInfo.sources.c_str());
284     pageJson->Put("costTime", vsyncTimeout);
285     pageJson->Put("pageRoute", pageRoute.c_str());
286     for (const auto& node : nodeMap) {
287         int64_t layoutTime = node.second.layoutTime / CONVERT_NANOSECONDS;
288         if (layoutTime != 0 && layoutTime >= AceChecker::GetNodeTimeout() && node.second.nodeTag != "page" &&
289             node.second.nodeTag != "ContainerModal" && node.second.nodeTag != "JsView") {
290             auto componentJson = JsonUtil::Create(true);
291             componentJson->Put("name", node.second.nodeTag.c_str());
292             componentJson->Put("costTime", layoutTime);
293             componentJson->Put("sourceLine", GetCodeInfo(node.second.codeRow, node.second.codeCol).row);
294             componentJson->Put("pagePath", node.second.pagePath.c_str());
295             std::unique_ptr<JsonValue> componentsJson;
296             if (pageJson->Contains("components")) {
297                 componentsJson = pageJson->GetValue("components");
298                 componentsJson->Put(componentJson);
299             } else {
300                 componentsJson = JsonUtil::CreateArray(true);
301                 componentsJson->Put(componentJson);
302                 pageJson->Put("components", componentsJson);
303             }
304         }
305     }
306     LOGI("pageJson 9903: %{public}s", pageJson->ToString().c_str());
307     ruleJson->Put(pageJson);
308 }
309 
RecordForEachItemsCount(int32_t count,std::unordered_map<int32_t,PerformanceCheckNode> & foreachNodeMap,const CodeInfo & codeInfo,const std::string & pageRoute)310 void AceScopedPerformanceCheck::RecordForEachItemsCount(int32_t count,
311     std::unordered_map<int32_t, PerformanceCheckNode>& foreachNodeMap,
312     const CodeInfo& codeInfo, const std::string& pageRoute)
313 {
314     if (count == 0 || count < AceChecker::GetForeachItems() || CheckPage(codeInfo, "9904")) {
315         return;
316     }
317     auto eventTime = GetCurrentTime();
318     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
319     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9904");
320     auto pageJson = JsonUtil::Create(true);
321     pageJson->Put("eventTime", eventTime.c_str());
322     pageJson->Put("pagePath", codeInfo.sources.c_str());
323     pageJson->Put("pageRoute", pageRoute.c_str());
324     for (const auto& iter : foreachNodeMap) {
325         auto componentJson = JsonUtil::Create(true);
326         componentJson->Put("name", iter.second.nodeTag.c_str());
327         componentJson->Put("items", iter.second.foreachItems + 1);
328         componentJson->Put("sourceLine", GetCodeInfo(iter.second.codeRow, iter.second.codeCol).row);
329         componentJson->Put("pagePath", iter.second.pagePath.c_str());
330         std::unique_ptr<JsonValue> componentsJson;
331         if (pageJson->Contains("components")) {
332             componentsJson = pageJson->GetValue("components");
333             componentsJson->Put(componentJson);
334         } else {
335             componentsJson = JsonUtil::CreateArray(true);
336             componentsJson->Put(componentJson);
337             pageJson->Put("components", componentsJson);
338         }
339     }
340     LOGI("pageJson 9904: %{public}s", pageJson->ToString().c_str());
341     ruleJson->Put(pageJson);
342 }
343 
RecordFlexLayoutsCount(const std::vector<PerformanceCheckNode> & flexNodeList,const CodeInfo & codeInfo,const std::string & pageRoute)344 void AceScopedPerformanceCheck::RecordFlexLayoutsCount(
345     const std::vector<PerformanceCheckNode>& flexNodeList, const CodeInfo& codeInfo, const std::string& pageRoute)
346 {
347     if (flexNodeList.empty() || CheckPage(codeInfo, "9905")) {
348         return;
349     }
350     auto eventTime = GetCurrentTime();
351     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
352     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9905");
353     auto pageJson = JsonUtil::Create(true);
354     pageJson->Put("eventTime", eventTime.c_str());
355     pageJson->Put("pagePath", codeInfo.sources.c_str());
356     pageJson->Put("pageRoute", pageRoute.c_str());
357     for (auto& node : flexNodeList) {
358         auto componentJson = JsonUtil::Create(true);
359         componentJson->Put("name", node.nodeTag.c_str());
360         componentJson->Put("flexTime", node.flexLayouts);
361         componentJson->Put("sourceLine", GetCodeInfo(node.codeRow, node.codeCol).row);
362         componentJson->Put("pagePath", node.pagePath.c_str());
363         std::unique_ptr<JsonValue> componentsJson;
364         if (pageJson->Contains("components")) {
365             componentsJson = pageJson->GetValue("components");
366             componentsJson->Put(componentJson);
367         } else {
368             componentsJson = JsonUtil::CreateArray(true);
369             componentsJson->Put(componentJson);
370             pageJson->Put("components", componentsJson);
371         }
372     }
373     LOGI("pageJson 9905: %{public}s", pageJson->ToString().c_str());
374     ruleJson->Put(pageJson);
375 }
376 
GetCurrentSourceMap()377 RefPtr<Framework::RevSourceMap> AceScopedPerformanceCheck::GetCurrentSourceMap()
378 {
379     std::string jsSourceMap;
380     auto sourceMap = AceType::MakeRefPtr<Framework::RevSourceMap>();
381     auto container = Container::Current();
382     CHECK_NULL_RETURN(container, nullptr);
383     auto pos = currentPath_.find("pages");
384     if (pos != std::string::npos) {
385         currentPath_ = currentPath_.substr(pos, currentPath_.size());
386     }
387     pos = currentPath_.find(".js");
388     if (pos != std::string::npos) {
389         currentPath_ = currentPath_.substr(0, pos);
390     }
391     if (container->IsUseStageModel()) {
392         auto pagePath = currentPath_;
393         auto moduleName = container->GetModuleName();
394         std::string judgePath = "";
395         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
396             judgePath = DEBUG_PATH + moduleName + ETS_PATH + pagePath + TS_SUFFIX;
397         } else {
398             judgePath = moduleName + ETS_PATH + pagePath + ETS_SUFFIX;
399         }
400         if (Framework::GetAssetContentImpl(container->GetAssetManager(), "sourceMaps.map", jsSourceMap)) {
401             auto jsonPages = JsonUtil::ParseJsonString(jsSourceMap);
402             auto child = jsonPages->GetChild();
403             if (!child->GetValue("entry-package-info")->IsNull()) {
404                 judgePath = NEW_PATH + pagePath + TS_SUFFIX;
405             }
406             auto jsonPage = jsonPages->GetValue(judgePath)->ToString();
407             sourceMap = AceType::MakeRefPtr<Framework::RevSourceMap>();
408             sourceMap->Init(jsonPage);
409             return sourceMap;
410         }
411     } else {
412         if (Framework::GetAssetContentImpl(container->GetAssetManager(), currentPath_ + ".map", jsSourceMap)) {
413             auto faPageMap = AceType::MakeRefPtr<Framework::RevSourceMap>();
414             faPageMap->Init(jsSourceMap);
415             return faPageMap;
416         }
417     }
418     return nullptr;
419 }
420 
CheckIsRuleWebsocket(const std::string & ruleType)421 bool AceScopedPerformanceCheck::CheckIsRuleWebsocket(const std::string& ruleType)
422 {
423     if (AceChecker::GetCheckMessge().empty()) {
424         return false;
425     }
426 
427     if (AceChecker::GetCheckMessge().find(ruleType, 0) != std::string::npos) {
428         return true;
429     }
430     return false;
431 }
432 } // namespace OHOS::Ace
433