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