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