• 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 
18 #include <chrono>
19 #include <cstdint>
20 #include <ctime>
21 #include <fstream>
22 #include <memory>
23 #include <numeric>
24 #include <ostream>
25 #include <string>
26 #include <unordered_map>
27 #include <utility>
28 #include <vector>
29 
30 #include "base/i18n/localization.h"
31 #include "base/json/json_util.h"
32 #include "base/log/ace_checker.h"
33 #include "base/log/dump_log.h"
34 #include "base/utils/time_util.h"
35 #include "base/utils/utils.h"
36 #include "bridge/common/utils/engine_helper.h"
37 #include "core/common/container.h"
38 
39 namespace OHOS::Ace {
40 namespace {
41 constexpr int32_t BASE_YEAR = 1900;
42 constexpr char DATE_FORMAT[] = "MM-dd HH:mm:ss";
43 constexpr int32_t CONVERT_NANOSECONDS = 1000000;
44 } // namespace
45 
46 // ============================== survival interval of JSON files ============================================
47 
48 std::unique_ptr<JsonValue> AcePerformanceCheck::performanceInfo_ = nullptr;
49 
Start()50 void AcePerformanceCheck::Start()
51 {
52     if (AceChecker::IsPerformanceCheckEnabled()) {
53         LOGI("performance check start");
54         performanceInfo_ = JsonUtil::Create(true);
55     }
56 }
57 
Stop()58 void AcePerformanceCheck::Stop()
59 {
60     if (performanceInfo_) {
61         LOGI("performance check stop");
62         auto info = performanceInfo_->ToString();
63         // output info to json file
64         auto filePath = AceApplicationInfo::GetInstance().GetDataFileDirPath() + "/arkui_bestpractice.json";
65         std::unique_ptr<std::ostream> ss = std::make_unique<std::ofstream>(filePath);
66         CHECK_NULL_VOID(ss);
67         DumpLog::GetInstance().SetDumpFile(std::move(ss));
68         DumpLog::GetInstance().Print(info);
69         DumpLog::GetInstance().Reset();
70         AceChecker::NotifyCaution("AcePerformanceCheck::Stop, json data generated, store in " + filePath);
71         performanceInfo_.reset(nullptr);
72     }
73 }
74 
75 // ============================== specific implementation ======================================================
76 
AceScopedPerformanceCheck(const std::string & name)77 AceScopedPerformanceCheck::AceScopedPerformanceCheck(const std::string& name)
78 {
79     if (AcePerformanceCheck::performanceInfo_) {
80         // micro time.
81         markTime_ = GetSysTimestamp();
82         name_ = name;
83     }
84 }
85 
~AceScopedPerformanceCheck()86 AceScopedPerformanceCheck::~AceScopedPerformanceCheck()
87 {
88     if (AcePerformanceCheck::performanceInfo_) {
89         // convert micro time to ms with 1000.
90         auto time = static_cast<int64_t>((GetSysTimestamp() - markTime_) / CONVERT_NANOSECONDS);
91         RecordFunctionTimeout(time, name_);
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 (!AcePerformanceCheck::performanceInfo_->Contains(ruleType)) {
100         AcePerformanceCheck::performanceInfo_->Put(ruleType.c_str(), JsonUtil::CreateArray(true));
101         return false;
102     }
103     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue(ruleType);
104     auto size = ruleJson->GetArraySize();
105     for (int32_t i = 0; i < size; i++) {
106         auto indexJson = ruleJson->GetArrayItem(i);
107         auto value = indexJson->GetString("pagePath", {});
108         if (value == pagePath) {
109             return true;
110         }
111     }
112     return false;
113 }
114 
GetCurrentTime()115 std::string AceScopedPerformanceCheck::GetCurrentTime()
116 {
117     // get system date and time
118     auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
119     auto* local = std::localtime(&now);
120     if (!local) {
121         return {};
122     }
123 
124     // this is for i18n date time
125     DateTime dateTime;
126     dateTime.year = static_cast<uint32_t>(local->tm_year + BASE_YEAR);
127     dateTime.month = static_cast<uint32_t>(local->tm_mon);
128     dateTime.day = static_cast<uint32_t>(local->tm_mday);
129     dateTime.hour = static_cast<uint32_t>(local->tm_hour);
130     dateTime.minute = static_cast<uint32_t>(local->tm_min);
131     dateTime.second = static_cast<uint32_t>(local->tm_sec);
132     auto time = Localization::GetInstance()->FormatDateTime(dateTime, DATE_FORMAT);
133     return time;
134 }
135 
GetCodeInfo(int32_t row,int32_t col)136 CodeInfo AceScopedPerformanceCheck::GetCodeInfo(int32_t row, int32_t col)
137 {
138     auto container = Container::Current();
139     CHECK_NULL_RETURN(container, {});
140     auto frontend = container->GetFrontend();
141     CHECK_NULL_RETURN(frontend, {});
142     auto sourceMap = frontend->GetCurrentPageSourceMap();
143     CHECK_NULL_RETURN(sourceMap, {});
144     // There is no same row and column info of viewPU in sourcemap, but the row info is correct.
145     auto codeInfo = sourceMap->Find(row, col, false);
146     return { codeInfo.row, codeInfo.col, codeInfo.sources };
147 }
148 
CheckPage(const CodeInfo & codeInfo,const std::string & rule)149 bool AceScopedPerformanceCheck::CheckPage(const CodeInfo& codeInfo, const std::string& rule)
150 {
151     if (!codeInfo.sources.empty() && CheckIsRuleContainsPage(rule, codeInfo.sources)) {
152         return true;
153     }
154     return false;
155 }
156 
RecordPerformanceCheckData(const PerformanceCheckNodeMap & nodeMap,int64_t vsyncTimeout)157 void AceScopedPerformanceCheck::RecordPerformanceCheckData(const PerformanceCheckNodeMap& nodeMap, int64_t vsyncTimeout)
158 {
159     auto codeInfo = GetCodeInfo(1, 1);
160     std::vector<PerformanceCheckNode> pageNodeList;
161     std::vector<PerformanceCheckNode> flexNodeList;
162     std::unordered_map<int32_t, PerformanceCheckNode> foreachNodeMap;
163     int32_t itemCount = 0;
164     int32_t maxDepth = 0;
165     for (const auto& node : nodeMap) {
166         if (node.second.childrenSize >= AceChecker::GetNodeChildren()) {
167             pageNodeList.emplace_back(node.second);
168         }
169         if (node.second.pageDepth > maxDepth) {
170             maxDepth = node.second.pageDepth;
171         }
172         if (node.second.flexLayouts != 0 && node.second.flexLayouts >= AceChecker::GetFlexLayouts()) {
173             flexNodeList.emplace_back(node.second);
174         }
175         if (node.second.isForEachItem) {
176             itemCount++;
177             auto iter = foreachNodeMap.find(node.second.codeRow);
178             if (iter != foreachNodeMap.end()) {
179                 iter->second.foreachItems++;
180             } else {
181                 foreachNodeMap.insert(std::make_pair(node.second.codeRow, node.second));
182             }
183         }
184     }
185     RecordPageNodeCountAndDepth(nodeMap.size(), maxDepth, pageNodeList, codeInfo);
186     RecordForEachItemsCount(itemCount, foreachNodeMap, codeInfo);
187     RecordFlexLayoutsCount(flexNodeList, codeInfo);
188     RecordVsyncTimeout(nodeMap, vsyncTimeout / CONVERT_NANOSECONDS, codeInfo);
189 }
190 
RecordPageNodeCountAndDepth(int32_t pageNodeCount,int32_t pageDepth,std::vector<PerformanceCheckNode> & pageNodeList,const CodeInfo & codeInfo)191 void AceScopedPerformanceCheck::RecordPageNodeCountAndDepth(
192     int32_t pageNodeCount, int32_t pageDepth, std::vector<PerformanceCheckNode>& pageNodeList, const CodeInfo& codeInfo)
193 {
194     if ((pageNodeCount < AceChecker::GetPageNodes() && pageDepth < AceChecker::GetPageDepth()) ||
195         CheckPage(codeInfo, "9901")) {
196         return;
197     }
198     auto eventTime = GetCurrentTime();
199     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
200     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9901");
201     auto pageJson = JsonUtil::Create(true);
202     pageJson->Put("eventTime", eventTime.c_str());
203     pageJson->Put("pagePath", codeInfo.sources.c_str());
204     pageJson->Put("nodeCount", pageNodeCount);
205     pageJson->Put("depth", pageDepth);
206     // add children size > 100 of component to pageJson
207     for (const auto& iter : pageNodeList) {
208         auto componentJson = JsonUtil::Create(true);
209         componentJson->Put("name", iter.nodeTag.c_str());
210         componentJson->Put("items", iter.childrenSize);
211         componentJson->Put("sourceLine", GetCodeInfo(iter.codeRow, iter.codeCol).row);
212         std::unique_ptr<JsonValue> componentsJson;
213         if (pageJson->Contains("components")) {
214             componentsJson = pageJson->GetValue("components");
215             componentsJson->Put(componentJson);
216         } else {
217             componentsJson = JsonUtil::CreateArray(true);
218             componentsJson->Put(componentJson);
219             pageJson->Put("components", componentsJson);
220         }
221     }
222     ruleJson->Put(pageJson);
223 }
224 
RecordFunctionTimeout(int64_t time,const std::string & functionName)225 void AceScopedPerformanceCheck::RecordFunctionTimeout(int64_t time, const std::string& functionName)
226 {
227     if (time < AceChecker::GetFunctionTimeout()) {
228         return;
229     }
230     auto codeInfo = GetCodeInfo(1, 1);
231     if (!codeInfo.sources.empty() && CheckIsRuleContainsPage("9902", codeInfo.sources)) {
232         return;
233     }
234     auto eventTime = GetCurrentTime();
235     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
236     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9902");
237     auto pageJson = JsonUtil::Create(true);
238     pageJson->Put("eventTime", eventTime.c_str());
239     pageJson->Put("pagePath", codeInfo.sources.c_str());
240     pageJson->Put("functionName", functionName.c_str());
241     pageJson->Put("costTime", time);
242     ruleJson->Put(pageJson);
243 }
244 
RecordVsyncTimeout(const PerformanceCheckNodeMap & nodeMap,int64_t vsyncTimeout,const CodeInfo & codeInfo)245 void AceScopedPerformanceCheck::RecordVsyncTimeout(
246     const PerformanceCheckNodeMap& nodeMap, int64_t vsyncTimeout, const CodeInfo& codeInfo)
247 {
248     if (vsyncTimeout < AceChecker::GetVsyncTimeout() || CheckPage(codeInfo, "9903")) {
249         return;
250     }
251     auto eventTime = GetCurrentTime();
252     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
253     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9903");
254     auto pageJson = JsonUtil::Create(true);
255     pageJson->Put("eventTime", eventTime.c_str());
256     pageJson->Put("pagePath", codeInfo.sources.c_str());
257     pageJson->Put("costTime", vsyncTimeout);
258     for (const auto& node : nodeMap) {
259         int64_t layoutTime = node.second.layoutTime / CONVERT_NANOSECONDS;
260         if (layoutTime != 0 && layoutTime >= AceChecker::GetNodeTimeout() && node.second.nodeTag != "page" &&
261             node.second.nodeTag != "ContainerModal" && node.second.nodeTag != "JsView") {
262             auto componentJson = JsonUtil::Create(true);
263             componentJson->Put("name", node.second.nodeTag.c_str());
264             componentJson->Put("costTime", layoutTime);
265             componentJson->Put("sourceLine", GetCodeInfo(node.second.codeRow, node.second.codeCol).row);
266             std::unique_ptr<JsonValue> componentsJson;
267             if (pageJson->Contains("components")) {
268                 componentsJson = pageJson->GetValue("components");
269                 componentsJson->Put(componentJson);
270             } else {
271                 componentsJson = JsonUtil::CreateArray(true);
272                 componentsJson->Put(componentJson);
273                 pageJson->Put("components", componentsJson);
274             }
275         }
276     }
277     ruleJson->Put(pageJson);
278 }
279 
RecordForEachItemsCount(int32_t count,std::unordered_map<int32_t,PerformanceCheckNode> & foreachNodeMap,const CodeInfo & codeInfo)280 void AceScopedPerformanceCheck::RecordForEachItemsCount(
281     int32_t count, std::unordered_map<int32_t, PerformanceCheckNode>& foreachNodeMap, const CodeInfo& codeInfo)
282 {
283     if (count == 0 || count < AceChecker::GetForeachItems() || CheckPage(codeInfo, "9904")) {
284         return;
285     }
286     auto eventTime = GetCurrentTime();
287     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
288     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9904");
289     auto pageJson = JsonUtil::Create(true);
290     pageJson->Put("eventTime", eventTime.c_str());
291     pageJson->Put("pagePath", codeInfo.sources.c_str());
292     for (const auto& iter : foreachNodeMap) {
293         auto componentJson = JsonUtil::Create(true);
294         componentJson->Put("name", iter.second.nodeTag.c_str());
295         componentJson->Put("items", iter.second.foreachItems + 1);
296         componentJson->Put("sourceLine", GetCodeInfo(iter.second.codeRow, iter.second.codeCol).row);
297         std::unique_ptr<JsonValue> componentsJson;
298         if (pageJson->Contains("components")) {
299             componentsJson = pageJson->GetValue("components");
300             componentsJson->Put(componentJson);
301         } else {
302             componentsJson = JsonUtil::CreateArray(true);
303             componentsJson->Put(componentJson);
304             pageJson->Put("components", componentsJson);
305         }
306     }
307     ruleJson->Put(pageJson);
308 }
309 
RecordFlexLayoutsCount(const std::vector<PerformanceCheckNode> & flexNodeList,const CodeInfo & codeInfo)310 void AceScopedPerformanceCheck::RecordFlexLayoutsCount(
311     const std::vector<PerformanceCheckNode>& flexNodeList, const CodeInfo& codeInfo)
312 {
313     if (flexNodeList.empty() || CheckPage(codeInfo, "9905")) {
314         return;
315     }
316     auto eventTime = GetCurrentTime();
317     CHECK_NULL_VOID(AcePerformanceCheck::performanceInfo_);
318     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9905");
319     auto pageJson = JsonUtil::Create(true);
320     pageJson->Put("eventTime", eventTime.c_str());
321     pageJson->Put("pagePath", codeInfo.sources.c_str());
322     for (auto& node : flexNodeList) {
323         auto componentJson = JsonUtil::Create(true);
324         componentJson->Put("name", node.nodeTag.c_str());
325         componentJson->Put("flexTime", node.flexLayouts);
326         componentJson->Put("sourceLine", GetCodeInfo(node.codeRow, node.codeCol).row);
327         std::unique_ptr<JsonValue> componentsJson;
328         if (pageJson->Contains("components")) {
329             componentsJson = pageJson->GetValue("components");
330             componentsJson->Put(componentJson);
331         } else {
332             componentsJson = JsonUtil::CreateArray(true);
333             componentsJson->Put(componentJson);
334             pageJson->Put("components", componentsJson);
335         }
336     }
337     ruleJson->Put(pageJson);
338 }
339 } // namespace OHOS::Ace
340