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