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
22namespace panda::ecmascript {
23using EcmaOpcode = kungfu::EcmaOpcode;
24
25void 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
89void 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
141void 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
175void 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
236OptCodeProfiler::~OptCodeProfiler()
237{
238    PrintAndReset();
239}
240} // namespace panda::ecmascript
241