1/*
2 * Copyright (c) 2023 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/function_call_timer.h"
17
18#include <iomanip>
19
20namespace panda::ecmascript {
21void FunctionCallTimer::StartCount(size_t id, bool isAot)
22{
23    PandaRuntimeTimer *callerTimer = currentTimer_;
24    auto calleeTimer = &callTimer_[id];
25    if (callerTimer != nullptr) {
26        calleeTimer->SetParent(callerTimer);
27    }
28    currentTimer_ = calleeTimer;
29    FunctionCallStat *calleeStat = nullptr;
30    if (isAot) {
31        calleeStat = &aotCallStat_[id];
32    } else {
33        calleeStat = &intCallStat_[id];
34    }
35    calleeTimer->Start(calleeStat, callerTimer);
36}
37
38void FunctionCallTimer::StopCount(Method *method)
39{
40    size_t id = method->GetMethodId().GetOffset();
41    auto callee = &callTimer_[id];
42    if (callee != currentTimer_) {
43        LOG_ECMA(INFO) << "EndCallTimer and StartCallTimer have different functions. Current function: "
44                       << GetFullName(method) << "has been skipped";
45        return;
46    }
47
48    PandaRuntimeTimer *callerTimer = callee->Stop();
49    currentTimer_ = callerTimer;
50}
51
52CString FunctionCallTimer::GetFullName(Method *method)
53{
54    CString funcName(method->GetMethodName());
55    CString recordName = method->GetRecordNameStr();
56    CString fullName = funcName + "@" + recordName;
57    return fullName;
58}
59
60void FunctionCallTimer::InitialStatAndTimer(Method *method, size_t methodId, bool isAot)
61{
62    if (isAot) {
63        auto iter = aotCallStat_.find(methodId);
64        if (iter == aotCallStat_.end()) {
65            CString funcName = GetFullName(method);
66            FunctionCallStat stat(funcName, isAot);
67            aotCallStat_[methodId] = stat;
68        }
69    } else {
70        auto iter = intCallStat_.find(methodId);
71        if (iter == intCallStat_.end()) {
72            CString funcName = GetFullName(method);
73            FunctionCallStat stat(funcName, isAot);
74            intCallStat_[methodId] = stat;
75        }
76    }
77
78    PandaRuntimeTimer timer;
79    callTimer_[methodId] = timer;
80}
81
82void FunctionCallTimer::PrintAllStats()
83{
84    LOG_ECMA(INFO) << "function call stat:";
85    static constexpr int nameRightAdjustment = 45;
86    static constexpr int numberRightAdjustment = 15;
87    LOG_ECMA(INFO) << std::right << std::setw(nameRightAdjustment) << "JS && TS Function Name"
88        << std::setw(numberRightAdjustment) << "Type"
89        << std::setw(numberRightAdjustment) << "Time(ns)" << std::setw(numberRightAdjustment) << "Count"
90        << std::setw(numberRightAdjustment) << "MaxTime(ns)"
91        << std::setw(numberRightAdjustment) << "AvgTime(ns)";
92    LOG_ECMA(INFO) << "============================================================="
93                   << "=============================================================";
94
95    CVector<FunctionCallStat> callStatVec;
96    for (auto &stat : aotCallStat_) {
97        callStatVec.emplace_back(stat.second);
98    }
99    for (auto &stat : intCallStat_) {
100        callStatVec.emplace_back(stat.second);
101    }
102    // Sort by TotalTime
103    std::sort(callStatVec.begin(), callStatVec.end(),
104        [](const FunctionCallStat &a, const FunctionCallStat &b) -> bool {
105            return a.TotalTime() > b.TotalTime();
106    });
107
108    for (auto &stat : callStatVec) {
109        if (stat.TotalCount() != 0) {
110            CString type = stat.IsAot() ? "Aot" : "Interpreter";
111            LOG_ECMA(INFO) << std::right << std::setw(nameRightAdjustment) << stat.Name()
112                << std::setw(numberRightAdjustment) << type
113                << std::setw(numberRightAdjustment) << stat.TotalTime()
114                << std::setw(numberRightAdjustment) << stat.TotalCount()
115                << std::setw(numberRightAdjustment) << stat.MaxTime()
116                << std::setw(numberRightAdjustment) << stat.TotalTime() / stat.TotalCount();
117        }
118    }
119}
120
121void FunctionCallTimer::ResetStat()
122{
123    for (auto &stat : aotCallStat_) {
124        stat.second.Reset();
125    }
126
127    for (auto &stat : intCallStat_) {
128        stat.second.Reset();
129    }
130}
131}