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(JSThread *thread, JSHandle<JSTaggedValue> &func, int bcIndex,
175 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(thread);
189 if (!method.IsMethod()) {
190 return;
191 }
192 auto methodPoint = Method::Cast(method);
193 auto methodId = methodPoint->GetMethodId().GetOffset();
194 auto methodName =
195 ConvertToStdString(methodPoint->GetRecordNameStr(thread)) + "." + methodPoint->GetMethodName(thread);
196
197 const auto *pf = methodPoint->GetJSPandaFile(thread);
198 ASSERT(pf != nullptr);
199 auto pfName = pf->GetJSPandaFileDesc();
200 auto itr = std::find(abcNames_.begin(), abcNames_.end(), pfName);
201 uint32_t index = 0;
202 if (itr != abcNames_.end()) {
203 index = static_cast<uint32_t>(std::distance(abcNames_.begin(), itr));
204 } else {
205 index = abcNames_.size();
206 abcNames_.emplace_back(pfName);
207 }
208
209 Key key(index, methodId);
210 // deal methodIdToName
211 auto result = methodIdToName_.find(key.Value());
212 if (result != methodIdToName_.end()) {
213 result->second.Inc();
214 } else {
215 methodIdToName_.emplace(key.Value(), Name(methodName));
216 }
217
218 // deal methodIdToRecord_
219 auto result2 = methodIdToRecord_.find(key.Value());
220 if (result2 == methodIdToRecord_.end()) {
221 BcRecord bcRecord;
222 bcRecord.emplace(bcIndex, Record(opcode));
223 methodIdToRecord_.emplace(key.Value(), bcRecord);
224 }
225 result2 = methodIdToRecord_.find(key.Value());
226
227 auto result3 = result2->second.find(bcIndex);
228 if (result3 != result2->second.end()) {
229 (mode == Mode::TYPED_PATH) ? (result3->second.IncFast()) : (result3->second.IncSlow());
230 } else {
231 auto record = Record(opcode);
232 (mode == Mode::TYPED_PATH) ? (record.IncFast()) : (record.IncSlow());
233 result2->second.emplace(bcIndex, record);
234 }
235 }
236
237 OptCodeProfiler::~OptCodeProfiler()
238 {
239 PrintAndReset();
240 }
241 } // namespace panda::ecmascript
242