• 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(false));
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         LOGE("Get localtime failed");
122         return {};
123     }
124 
125     // this is for i18n date time
126     DateTime dateTime;
127     dateTime.year = static_cast<uint32_t>(local->tm_year + BASE_YEAR);
128     dateTime.month = static_cast<uint32_t>(local->tm_mon);
129     dateTime.day = static_cast<uint32_t>(local->tm_mday);
130     dateTime.hour = static_cast<uint32_t>(local->tm_hour);
131     dateTime.minute = static_cast<uint32_t>(local->tm_min);
132     dateTime.second = static_cast<uint32_t>(local->tm_sec);
133     auto time = Localization::GetInstance()->FormatDateTime(dateTime, DATE_FORMAT);
134     return time;
135 }
136 
GetCodeInfo(int32_t row,int32_t col)137 CodeInfo AceScopedPerformanceCheck::GetCodeInfo(int32_t row, int32_t col)
138 {
139     auto container = Container::Current();
140     CHECK_NULL_RETURN(container, {});
141     auto frontend = container->GetFrontend();
142     CHECK_NULL_RETURN(frontend, {});
143     auto sourceMap = frontend->GetCurrentPageSourceMap();
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)158 void AceScopedPerformanceCheck::RecordPerformanceCheckData(const PerformanceCheckNodeMap& nodeMap, int64_t vsyncTimeout)
159 {
160     auto codeInfo = GetCodeInfo(1, 1);
161     std::vector<PerformanceCheckNode> pageNodeList;
162     std::vector<PerformanceCheckNode> flexNodeList;
163     std::unordered_map<int32_t, PerformanceCheckNode> foreachNodeMap;
164     int32_t itemCount = 0;
165     int32_t maxDepth = 0;
166     for (const auto& node : nodeMap) {
167         if (node.second.childrenSize >= AceChecker::GetNodeChildren()) {
168             pageNodeList.emplace_back(node.second);
169         }
170         if (node.second.pageDepth > maxDepth) {
171             maxDepth = node.second.pageDepth;
172         }
173         if (node.second.flexLayouts != 0 && node.second.flexLayouts >= AceChecker::GetFlexLayouts()) {
174             flexNodeList.emplace_back(node.second);
175         }
176         if (node.second.isForEachItem) {
177             itemCount++;
178             auto iter = foreachNodeMap.find(node.second.codeRow);
179             if (iter != foreachNodeMap.end()) {
180                 iter->second.foreachItems++;
181             } else {
182                 foreachNodeMap.insert(std::make_pair(node.second.codeRow, node.second));
183             }
184         }
185     }
186     RecordPageNodeCountAndDepth(nodeMap.size(), maxDepth, pageNodeList, codeInfo);
187     RecordForEachItemsCount(itemCount, foreachNodeMap, codeInfo);
188     RecordFlexLayoutsCount(flexNodeList, codeInfo);
189     RecordVsyncTimeout(nodeMap, vsyncTimeout / CONVERT_NANOSECONDS, codeInfo);
190 }
191 
RecordPageNodeCountAndDepth(int32_t pageNodeCount,int32_t pageDepth,std::vector<PerformanceCheckNode> & pageNodeList,const CodeInfo & codeInfo)192 void AceScopedPerformanceCheck::RecordPageNodeCountAndDepth(
193     int32_t pageNodeCount, int32_t pageDepth, std::vector<PerformanceCheckNode>& pageNodeList, const CodeInfo& codeInfo)
194 {
195     if ((pageNodeCount < AceChecker::GetPageNodes() && pageDepth < AceChecker::GetPageDepth()) ||
196         CheckPage(codeInfo, "9901")) {
197         return;
198     }
199     auto eventTime = GetCurrentTime();
200     CHECK_NULL_VOID_NOLOG(AcePerformanceCheck::performanceInfo_);
201     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9901");
202     auto pageJson = JsonUtil::Create(false);
203     pageJson->Put("eventTime", eventTime.c_str());
204     pageJson->Put("pagePath", codeInfo.sources.c_str());
205     pageJson->Put("nodeCount", pageNodeCount);
206     pageJson->Put("depth", pageDepth);
207     // add children size > 100 of component to pageJson
208     for (const auto& iter : pageNodeList) {
209         auto componentJson = JsonUtil::Create(false);
210         componentJson->Put("name", iter.nodeTag.c_str());
211         componentJson->Put("items", iter.childrenSize);
212         componentJson->Put("sourceLine", GetCodeInfo(iter.codeRow, iter.codeCol).row);
213         std::unique_ptr<JsonValue> componentsJson;
214         if (pageJson->Contains("components")) {
215             componentsJson = pageJson->GetValue("components");
216             componentsJson->Put(componentJson);
217         } else {
218             componentsJson = JsonUtil::CreateArray(false);
219             componentsJson->Put(componentJson);
220             pageJson->Put("components", componentsJson);
221         }
222     }
223     ruleJson->Put(pageJson);
224 }
225 
RecordFunctionTimeout(int64_t time,const std::string & functionName)226 void AceScopedPerformanceCheck::RecordFunctionTimeout(int64_t time, const std::string& functionName)
227 {
228     if (time < AceChecker::GetFunctionTimeout()) {
229         return;
230     }
231     auto codeInfo = GetCodeInfo(1, 1);
232     if (!codeInfo.sources.empty() && CheckIsRuleContainsPage("9902", codeInfo.sources)) {
233         return;
234     }
235     auto eventTime = GetCurrentTime();
236     CHECK_NULL_VOID_NOLOG(AcePerformanceCheck::performanceInfo_);
237     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9902");
238     auto pageJson = JsonUtil::Create(false);
239     pageJson->Put("eventTime", eventTime.c_str());
240     pageJson->Put("pagePath", codeInfo.sources.c_str());
241     pageJson->Put("functionName", functionName.c_str());
242     pageJson->Put("costTime", time);
243     ruleJson->Put(pageJson);
244 }
245 
RecordVsyncTimeout(const PerformanceCheckNodeMap & nodeMap,int64_t vsyncTimeout,const CodeInfo & codeInfo)246 void AceScopedPerformanceCheck::RecordVsyncTimeout(
247     const PerformanceCheckNodeMap& nodeMap, int64_t vsyncTimeout, const CodeInfo& codeInfo)
248 {
249     if (vsyncTimeout < AceChecker::GetVsyncTimeout() || CheckPage(codeInfo, "9903")) {
250         return;
251     }
252     auto eventTime = GetCurrentTime();
253     CHECK_NULL_VOID_NOLOG(AcePerformanceCheck::performanceInfo_);
254     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9903");
255     auto pageJson = JsonUtil::Create(false);
256     pageJson->Put("eventTime", eventTime.c_str());
257     pageJson->Put("pagePath", codeInfo.sources.c_str());
258     pageJson->Put("costTime", vsyncTimeout);
259     for (const auto& node : nodeMap) {
260         int64_t layoutTime = node.second.layoutTime / CONVERT_NANOSECONDS;
261         if (layoutTime != 0 && layoutTime >= AceChecker::GetNodeTimeout() && node.second.nodeTag != "page" &&
262             node.second.nodeTag != "ContainerModal" && node.second.nodeTag != "JsView") {
263             auto componentJson = JsonUtil::Create(false);
264             componentJson->Put("name", node.second.nodeTag.c_str());
265             componentJson->Put("costTime", layoutTime);
266             componentJson->Put("sourceLine", GetCodeInfo(node.second.codeRow, node.second.codeCol).row);
267             std::unique_ptr<JsonValue> componentsJson;
268             if (pageJson->Contains("components")) {
269                 componentsJson = pageJson->GetValue("components");
270                 componentsJson->Put(componentJson);
271             } else {
272                 componentsJson = JsonUtil::CreateArray(false);
273                 componentsJson->Put(componentJson);
274                 pageJson->Put("components", componentsJson);
275             }
276         }
277     }
278     ruleJson->Put(pageJson);
279 }
280 
RecordForEachItemsCount(int32_t count,std::unordered_map<int32_t,PerformanceCheckNode> & foreachNodeMap,const CodeInfo & codeInfo)281 void AceScopedPerformanceCheck::RecordForEachItemsCount(
282     int32_t count, std::unordered_map<int32_t, PerformanceCheckNode>& foreachNodeMap, const CodeInfo& codeInfo)
283 {
284     if (count == 0 || count < AceChecker::GetForeachItems() || CheckPage(codeInfo, "9904")) {
285         return;
286     }
287     auto eventTime = GetCurrentTime();
288     CHECK_NULL_VOID_NOLOG(AcePerformanceCheck::performanceInfo_);
289     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9904");
290     auto pageJson = JsonUtil::Create(false);
291     pageJson->Put("eventTime", eventTime.c_str());
292     pageJson->Put("pagePath", codeInfo.sources.c_str());
293     for (const auto& iter : foreachNodeMap) {
294         auto componentJson = JsonUtil::Create(false);
295         componentJson->Put("name", iter.second.nodeTag.c_str());
296         componentJson->Put("items", iter.second.foreachItems + 1);
297         componentJson->Put("sourceLine", GetCodeInfo(iter.second.codeRow, iter.second.codeCol).row);
298         std::unique_ptr<JsonValue> componentsJson;
299         if (pageJson->Contains("components")) {
300             componentsJson = pageJson->GetValue("components");
301             componentsJson->Put(componentJson);
302         } else {
303             componentsJson = JsonUtil::CreateArray(false);
304             componentsJson->Put(componentJson);
305             pageJson->Put("components", componentsJson);
306         }
307     }
308     ruleJson->Put(pageJson);
309 }
310 
RecordFlexLayoutsCount(const std::vector<PerformanceCheckNode> & flexNodeList,const CodeInfo & codeInfo)311 void AceScopedPerformanceCheck::RecordFlexLayoutsCount(
312     const std::vector<PerformanceCheckNode>& flexNodeList, const CodeInfo& codeInfo)
313 {
314     if (flexNodeList.empty() || CheckPage(codeInfo, "9905")) {
315         return;
316     }
317     auto eventTime = GetCurrentTime();
318     CHECK_NULL_VOID_NOLOG(AcePerformanceCheck::performanceInfo_);
319     auto ruleJson = AcePerformanceCheck::performanceInfo_->GetValue("9905");
320     auto pageJson = JsonUtil::Create(false);
321     pageJson->Put("eventTime", eventTime.c_str());
322     pageJson->Put("pagePath", codeInfo.sources.c_str());
323     for (auto& node : flexNodeList) {
324         auto componentJson = JsonUtil::Create(false);
325         componentJson->Put("name", node.nodeTag.c_str());
326         componentJson->Put("flexTime", node.flexLayouts);
327         componentJson->Put("sourceLine", GetCodeInfo(node.codeRow, node.codeCol).row);
328         std::unique_ptr<JsonValue> componentsJson;
329         if (pageJson->Contains("components")) {
330             componentsJson = pageJson->GetValue("components");
331             componentsJson->Put(componentJson);
332         } else {
333             componentsJson = JsonUtil::CreateArray(false);
334             componentsJson->Put(componentJson);
335             pageJson->Put("components", componentsJson);
336         }
337     }
338     ruleJson->Put(pageJson);
339 }
340 } // namespace OHOS::Ace
341