1 /*
2 * Copyright (c) 2022-2024 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 "ecmascript/dfx/vmstat/opt_code_profiler.h"
17 #include <iomanip>
18
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/jspandafile/js_pandafile.h"
21
22 namespace panda::ecmascript {
23 using EcmaOpcode = kungfu::EcmaOpcode;
24
PrintAndReset()25 void OptCodeProfiler::PrintAndReset()
26 {
27 #if ECMASCRIPT_ENABLE_OPT_CODE_PROFILER
28 std::vector<std::pair<EcmaOpcode, Value>> profVec;
29 for (auto it = profMap_.begin(); it != profMap_.end(); it++) {
30 profVec.emplace_back(std::make_pair(it->first, it->second));
31 it->second.ResetStat();
32 }
33 std::sort(profVec.begin(), profVec.end(),
34 [](std::pair<EcmaOpcode, Value> &x, std::pair<EcmaOpcode, Value> &y) -> bool {
35 return x.second.Count() > y.second.Count();
36 });
37
38 LOG_ECMA(INFO) << "Runtime Statistics of optimized code path:";
39 static constexpr int nameRightAdjustment = 46;
40 static constexpr int numberRightAdjustment = 15;
41 static constexpr int hundred = 100;
42 LOG_ECMA(INFO) << std::right << std::setw(nameRightAdjustment) << "Bytecode"
43 << std::setw(numberRightAdjustment) << "bcIndex"
44 << std::setw(numberRightAdjustment) << "Count"
45 << std::setw(numberRightAdjustment) << "TypedPathCount"
46 << std::setw(numberRightAdjustment) << "SlowPathCount"
47 << std::setw(numberRightAdjustment + 1) << "TypedPathRate";
48 LOG_ECMA(INFO) << "============================================================"
49 << "=========================================================";
50
51 uint64_t totalCount = 0;
52 uint64_t totalTypedPathCount = 0;
53 uint64_t totalSlowPathCount = 0;
54
55 for (auto it = profVec.begin(); it != profVec.end(); it++) {
56 Value val = it->second;
57 if (val.Count() == 0) {
58 break;
59 }
60
61 LOG_ECMA(INFO) << std::right << std::setw(nameRightAdjustment) << kungfu::GetEcmaOpcodeStr(it->first)
62 << std::setw(numberRightAdjustment) << "NA"
63 << std::setw(numberRightAdjustment) << val.Count()
64 << std::setw(numberRightAdjustment) << val.TypedPathCount()
65 << std::setw(numberRightAdjustment) << val.SlowPathCount()
66 << std::setw(numberRightAdjustment) << val.TypedPathCount() * hundred / val.Count() << "%";
67
68 totalCount += val.Count();
69 totalTypedPathCount += val.TypedPathCount();
70 totalSlowPathCount += val.SlowPathCount();
71 }
72
73 if (totalCount != 0) {
74 LOG_ECMA(INFO) << "------------------------------------------------------------"
75 << "---------------------------------------------------------";
76 LOG_ECMA(INFO) << std::right << std::setw(nameRightAdjustment) << "Total"
77 << std::setw(numberRightAdjustment) << "NA"
78 << std::setw(numberRightAdjustment) << totalCount
79 << std::setw(numberRightAdjustment) << totalTypedPathCount
80 << std::setw(numberRightAdjustment) << totalSlowPathCount
81 << std::setw(numberRightAdjustment) << totalTypedPathCount * hundred / totalCount << "%";
82 }
83
84 FilterMethodToPrint();
85 ResetMethodInfo();
86 #endif
87 }
88
FilterMethodToPrint()89 void OptCodeProfiler::FilterMethodToPrint()
90 {
91 #if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER
92 std::vector<CString> methods;
93 auto &profMap = JitWarmupProfiler::GetInstance()->profMap_;
94 for (auto it = profMap.begin(); it != profMap.end();) {
95 if (it->second == false) {
96 methods.push_back(it->first);
97 profMap.erase(it++);
98 } else {
99 it++;
100 }
101 }
102 for (auto &methodName : methods) {
103 if (methodName.find("func_main_") != methodName.npos) {
104 continue;
105 }
106 LOG_ECMA(ERROR) << methodName << " has not been fully jit warmed up.";
107 }
108 #endif
109 std::vector<std::pair<uint64_t, Name>> profVec;
110 for (auto it = methodIdToName_.begin(); it != methodIdToName_.end(); it++) {
111 profVec.emplace_back(std::make_pair(it->first, it->second));
112 }
113 std::sort(profVec.begin(), profVec.end(),
114 [](std::pair<uint64_t, Name> &x, std::pair<uint64_t, Name> &y) -> bool {
115 return x.second.Count() > y.second.Count();
116 });
117
118 auto itr = profVec.begin();
119 #if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER
120 while (itr != profVec.end()) {
121 #else
122 for (int i = 0; i < printMehodCount_ && itr != profVec.end(); i++) {
123 #endif
124 PrintMethodRecord(itr->first, itr->second.GetName());
125 itr++;
126 }
127 #if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER
128 if (profMap.size() != 0) {
129 for (auto it = profMap.begin(); it != profMap.end(); it++) {
130 if (it->first.find("func_main_") != it->first.npos) {
131 continue;
132 }
133 LOG_ECMA(ERROR) << "There exists compiled function " << it->first
134 << ", but it has not been jit executed, please "
135 "warm up strongly.";
136 }
137 }
138 #endif
139 }
140
141 void OptCodeProfiler::PrintMethodRecord(Key key, std::string methodName)
142 {
143 #if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER
144 CString methodInfo = abcNames_[key.GetAbcId()] + ":" + CString(methodName);
145 auto &profMap = JitWarmupProfiler::GetInstance()->profMap_;
146 if (profMap.find(methodInfo) != profMap.end()) {
147 profMap.erase(methodInfo);
148 }
149 #endif
150 LOG_ECMA(INFO) << "==== methodId: " << key.GetMethodId()
151 << ", methodName: " << methodName.c_str()
152 << ", abcName: " << abcNames_[key.GetAbcId()] << " ====";
153
154 static constexpr int nameRightAdjustment = 46;
155 static constexpr int numberRightAdjustment = 15;
156 static constexpr int hundred = 100;
157 BcRecord& bcRecord = methodIdToRecord_[key.Value()];
158 for (auto it = bcRecord.begin(); it != bcRecord.end(); it++) {
159 Record record = it->second;
160 #if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER == 0
161 if (record.Count() == 0) {
162 break;
163 }
164 #endif
165
166 LOG_ECMA(INFO) << std::right << std::setw(nameRightAdjustment) << kungfu::GetEcmaOpcodeStr(record.GetOpCode())
167 << std::setw(numberRightAdjustment) << it->first
168 << std::setw(numberRightAdjustment) << record.Count()
169 << std::setw(numberRightAdjustment) << record.GetFast()
170 << std::setw(numberRightAdjustment) << record.GetSlow()
171 << std::setw(numberRightAdjustment) << record.GetFast() * hundred / record.Count() << "%";
172 }
173 }
174
175 void OptCodeProfiler::Update(JSHandle<JSTaggedValue> &func, int bcIndex, EcmaOpcode opcode, Mode mode)
176 {
177 auto it = profMap_.find(opcode);
178 if (it != profMap_.end()) {
179 (mode == Mode::TYPED_PATH) ? (it->second.typedPathValue++) : (it->second.slowPathValue++);
180 }
181
182 if (func->IsUndefined()) {
183 return;
184 }
185
186 // methodId & methodName
187 auto funcPoint = JSFunction::Cast(func->GetTaggedObject());
188 auto method = funcPoint->GetMethod();
189 if (!method.IsMethod()) {
190 return;
191 }
192 auto methodPoint = Method::Cast(method);
193 auto methodId = methodPoint->GetMethodId().GetOffset();
194 auto methodName = ConvertToStdString(methodPoint->GetRecordNameStr()) + "." + methodPoint->GetMethodName();
195
196 const auto *pf = methodPoint->GetJSPandaFile();
197 ASSERT(pf != nullptr);
198 auto pfName = pf->GetJSPandaFileDesc();
199 auto itr = std::find(abcNames_.begin(), abcNames_.end(), pfName);
200 uint32_t index = 0;
201 if (itr != abcNames_.end()) {
202 index = static_cast<uint32_t>(std::distance(abcNames_.begin(), itr));
203 } else {
204 index = abcNames_.size();
205 abcNames_.emplace_back(pfName);
206 }
207
208 Key key(index, methodId);
209 // deal methodIdToName
210 auto result = methodIdToName_.find(key.Value());
211 if (result != methodIdToName_.end()) {
212 result->second.Inc();
213 } else {
214 methodIdToName_.emplace(key.Value(), Name(methodName));
215 }
216
217 // deal methodIdToRecord_
218 auto result2 = methodIdToRecord_.find(key.Value());
219 if (result2 == methodIdToRecord_.end()) {
220 BcRecord bcRecord;
221 bcRecord.emplace(bcIndex, Record(opcode));
222 methodIdToRecord_.emplace(key.Value(), bcRecord);
223 }
224 result2 = methodIdToRecord_.find(key.Value());
225
226 auto result3 = result2->second.find(bcIndex);
227 if (result3 != result2->second.end()) {
228 (mode == Mode::TYPED_PATH) ? (result3->second.IncFast()) : (result3->second.IncSlow());
229 } else {
230 auto record = Record(opcode);
231 (mode == Mode::TYPED_PATH) ? (record.IncFast()) : (record.IncSlow());
232 result2->second.emplace(bcIndex, record);
233 }
234 }
235
236 OptCodeProfiler::~OptCodeProfiler()
237 {
238 PrintAndReset();
239 }
240 } // namespace panda::ecmascript
241