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