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 #include "frameworks/bridge/declarative_frontend/jsview/js_state_mgmt_profiler.h"
16 #include "base/log/dump_log.h"
17 #include "base/utils/utils.h"
18 
19 namespace OHOS::Ace::Framework {
JSStateMgmtProfiler(std::string profilerPackage)20 JSStateMgmtProfiler::JSStateMgmtProfiler(std::string profilerPackage) : profilerPackage_(std::move(profilerPackage))
21 {
22 }
23 
JSBind(BindingTarget globalObj)24 void JSStateMgmtProfiler::JSBind(BindingTarget globalObj)
25 {
26     JSClass<JSStateMgmtProfiler>::Declare("StateMgmtProfiler");
27     JSClass<JSStateMgmtProfiler>::Method("begin", &JSStateMgmtProfiler::Begin);
28     JSClass<JSStateMgmtProfiler>::Method("end", &JSStateMgmtProfiler::End);
29     JSClass<JSStateMgmtProfiler>::Method("report", &JSStateMgmtProfiler::Report);
30     JSClass<JSStateMgmtProfiler>::Method("clear", &JSStateMgmtProfiler::Clear);
31     JSClass<JSStateMgmtProfiler>::Bind(globalObj, JSStateMgmtProfiler::ConstructorCallback);
32 }
33 
ConstructorCallback(const JSCallbackInfo & info)34 void JSStateMgmtProfiler::ConstructorCallback(const JSCallbackInfo& info)
35 {
36     if (info[0]->IsString()) {
37         auto instance = new JSStateMgmtProfiler(info[0]->ToString());
38         info.SetReturnValue(instance);
39     } else {
40         LOGE("JSStateMgmtProfiler invalid arguments! Expected a string");
41     }
42 }
43 
Begin(const std::string & blockName)44 void JSStateMgmtProfiler::Begin(const std::string& blockName)
45 {
46     auto then = GetNanoseconds();
47     std::shared_ptr<ProfileBlock> block = nullptr;
48     if (currentBlocks_.empty()) {
49         for (const auto& rootBlock : rootBlocks_) {
50             if (blockName == rootBlock->Name()) {
51                 block = rootBlock;
52                 break;
53             }
54         }
55         if (!block) {
56             block = std::make_shared<ProfileBlock>(blockName);
57             rootBlocks_.push_back(block);
58         }
59     } else {
60         block = currentBlocks_.top()->GetOrCreateChild(blockName);
61     }
62 
63     currentBlocks_.push(block);
64     block->IncreaseCalls();
65     block->SetOwnLookupTime(GetNanoseconds() - then);
66     block->SetStartTime(GetNanoseconds());
67 }
68 
End()69 void JSStateMgmtProfiler::End()
70 {
71     auto now = GetNanoseconds();
72     auto then = currentBlocks_.top();
73     then->SetTotalTime(then->TotalTime() + (now - then->StartTime()));
74     currentBlocks_.pop();
75     if (!currentBlocks_.empty()) {
76         auto parent = currentBlocks_.top();
77         parent->SetOwnLookupTime(parent->OwnLookupTime() + then->OwnLookupTime());
78     }
79 }
80 
Report()81 void JSStateMgmtProfiler::Report()
82 {
83     if (rootBlocks_.empty()) {
84         DumpLog::GetInstance().Print(0, "StateMgmtProfiler: nothing to report.");
85         return;
86     }
87     const int nameWidth = 70;
88     const int itemWidth = 14;
89 
90     DumpLog::GetInstance().Reset();
91     std::unique_ptr<std::ostringstream> stdOut = std::make_unique<std::ostringstream>();
92     DumpLog::GetInstance().SetDumpFile(std::move(stdOut));
93     std::ostringstream ss;
94     ss << std::left << "Block name" << std::setw(nameWidth) << std::right << "#Calls" << std::setw(itemWidth) << "Self"
95        << std::setw(itemWidth) << "Total";
96     std::string out = ss.str();
97     std::string dashes(out.size(), '=');
98     DumpLog::GetInstance().Print(0, dashes);
99     DumpLog::GetInstance().Print(0, out);
100     DumpLog::GetInstance().Print(0, dashes);
101     DumpLog::GetInstance().Print(0, profilerPackage_);
102     for (const auto& blocks : rootBlocks_) {
103         blocks->Report(1);
104     }
105     DumpLog::GetInstance().Print(0, dashes);
106 
107     Clear();
108 }
109 
Report(int32_t depth) const110 void JSStateMgmtProfiler::ProfileBlock::Report(int32_t depth) const
111 {
112     std::ostringstream ss;
113     uint64_t childrenTime = 0ULL;
114     for (const auto& childrenBlocks : childrenBlocks_) {
115         childrenTime += childrenBlocks->TotalTime();
116     }
117     const int nameAdjustWidth = 80;
118     const int itemAdjustWidth = 12;
119     const int itemPrecision = 3;
120     const int nanoSecsInMilliSec = 1000000;
121 
122     ss << std::left << std::string(depth, ' ');
123     ss << name_;
124     ss << std::setw(nameAdjustWidth - static_cast<int32_t>(name_.size()) - depth);
125     ss << std::right << numberOfCalls_;
126     ss << std::setw(itemAdjustWidth);
127     ss << std::fixed << std::setprecision(itemPrecision)
128        << (static_cast<double>(totalTime_ - childrenTime - ownLookupTime_) / nanoSecsInMilliSec) << "ms";
129     ss << std::setw(itemAdjustWidth);
130     ss << (static_cast<double>(totalTime_ - ownLookupTime_) / nanoSecsInMilliSec) << "ms";
131 
132     DumpLog::GetInstance().Print(0, ss.str());
133     for (const auto& childrenBlocks : childrenBlocks_) {
134         childrenBlocks->Report(depth + 1);
135     }
136 }
137 
Clear()138 void JSStateMgmtProfiler::Clear()
139 {
140     rootBlocks_.clear();
141     std::stack<std::shared_ptr<ProfileBlock>> empty;
142     currentBlocks_.swap(empty);
143 }
144 } // namespace OHOS::Ace::Framework