1/*
2 * Copyright (c) 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/jit/jit_dfx.h"
17#include "ecmascript/runtime.h"
18#include "libpandafile/bytecode_instruction-inl.h"
19#include "libpandafile/code_data_accessor.h"
20#include "libpandafile/class_data_accessor-inl.h"
21#include "ecmascript/platform/file.h"
22
23#ifdef ENABLE_HISYSEVENT
24#include "hisysevent.h"
25#endif
26
27namespace panda::ecmascript {
28JitDfx JitDfx::instance;
29thread_local uint32_t JitDfx::prefixOffset_ = 0;
30class NullStream : public std::ostream {
31public:
32    NullStream() : std::ostream(&buffer_) {}
33
34private:
35    class NullBuffer : public std::streambuf {
36    public:
37        int overflow(int c) override
38        {
39            return c;
40        }
41    };
42    NullBuffer buffer_;
43};
44
45JitDfx *JitDfx::GetInstance()
46{
47    return &instance;
48}
49
50void JitDfx::Init(const JSRuntimeOptions &options, std::string &bundleName)
51{
52    if (options.IsEnableJitDfxDump()) {
53        EnableDump();
54    }
55    ResetCompilerTime();
56    ResetBlockUIEventTime();
57    SetBundleName(ConvertToString(bundleName));
58    // main thread
59    SetPidNumber(JSThread::GetCurrentThreadId());
60}
61
62void JitDfx::EnableDump()
63{
64    isEnableDump_ = true;
65}
66
67void JitDfx::OpenLogFile(uint32_t threadId)
68{
69#ifdef PANDA_TARGET_OHOS
70    CString path = CString("/data/storage/ark-profile/jit_dfx_") + ToCString(threadId) + CString(".log");
71#else
72    CString path = CString("jit_dfx_") + ToCString(threadId) + CString(".log");
73#endif
74    std::string realOutPath;
75    if (!ecmascript::RealPath(path.c_str(), realOutPath, false)) {
76        return;
77    }
78    logFiles_[threadId].open(realOutPath, std::ios::out);
79}
80
81std::ostream &JitDfx::GetLogFileStream()
82{
83    if (!isEnableDump_) {
84        static NullStream nullStream_;
85        return nullStream_;
86    }
87    uint32_t threadId = os::thread::GetCurrentThreadId();
88    auto it = logFiles_.find(threadId);
89    if (it == logFiles_.end()) {
90        OpenLogFile(threadId);
91    }
92    return logFiles_[threadId];
93}
94
95void JitDfx::DumpBytecodeInst(Method *method)
96{
97    if (!isEnableDump_) {
98        return;
99    }
100    CString methodInfo = method->GetRecordNameStr() + "." + CString(method->GetMethodName());
101    MethodLiteral *methodLiteral = method->GetMethodLiteral();
102    auto jsPandaFile = method->GetJSPandaFile();
103    const panda_file::File *pf = jsPandaFile->GetPandaFile();
104    ASSERT(methodLiteral != nullptr);
105    panda_file::File::EntityId methodIdx = methodLiteral->GetMethodId();
106    panda_file::MethodDataAccessor mda(*pf, methodIdx);
107    auto codeId = mda.GetCodeId();
108    panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
109    uint32_t codeSize = codeDataAccessor.GetCodeSize();
110    const uint8_t *insns = codeDataAccessor.GetInstructions();
111
112    std::ostringstream ss;
113    ss << "BytecodeInst func:" << methodInfo << "\n";
114    auto bcIns = BytecodeInst(insns);
115    auto bcInsLast = bcIns.JumpTo(codeSize);
116    while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
117        ss << bcIns << std::endl;
118        auto nextInst = bcIns.GetNext();
119        bcIns = nextInst;
120    }
121
122    GetLogFileStream() << ss.str() << std::endl;
123}
124
125void JitDfx::TraceJitCode(Method *method, bool isEntry)
126{
127    if (!isEnableDump_) {
128        return;
129    }
130    if (!isEntry) {
131        prefixOffset_ -= 1;
132    }
133    CString prefixStr = isEntry ? CString("JitCodeEntry:") : CString("JitCodeExit :");
134    CString methodInfo = method->GetRecordNameStr() + "." + CString(method->GetMethodName());
135    static CString blackSpace("  ");
136    CString prefix;
137    for (uint32_t i = 0; i < prefixOffset_; i++) {
138        prefix += blackSpace;
139    }
140    if (isEntry) {
141        prefixOffset_ += 1;
142    }
143    LOG_JIT(INFO) << prefixStr << prefix << methodInfo;
144}
145
146void JitDfx::PrintJitStatsLog()
147{
148    if (checkUploadConditions()) {
149        LOG_JIT(DEBUG) << "Jit Compiler stats Log: "
150        << " bundleName: " << GetBundleName()
151        << " pid: " << GetPidNumber()
152        << " total main thread time: " << GetTotalTimeOnMainThread()
153        << " total Jit thread time: " << GetTotalTimeOnJitThread()
154        << " total Baseline Jit times: " << GetTotalBaselineJitCount()
155        << " total Fastopt Jit times: " << GetTotalFastoptJitCount()
156        << " report time interval:"
157        << std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - jitEventParams.start_).count()
158        << " total time on hold lock: " << GetTotalLockHoldingTime()
159        << " max time on hold lock: " << GetMaxLockHoldingTime()
160        << " longtime of hold lock: " << GetLongtimeLockCount()
161        << " JitDeopt times: " << GetJitDeoptCount()
162        << "\n";
163        SendJitStatsEvent();
164        InitializeRecord();
165    }
166}
167
168void JitDfx::SendJitStatsEvent() const
169{
170#ifdef ENABLE_HISYSEVENT
171    int32_t ret = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::ARKTS_RUNTIME,
172        "ARK_STATS_JIT",
173        OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
174        "BUNDLE_NAME", ConvertToStdString(GetBundleName()),
175        "PID", GetPidNumber(),
176        "TIME_INTERVAL",
177        std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - jitEventParams.start_).count(),
178        "TOTAL_BASELINE_JIT_TIMES", GetTotalBaselineJitCount(),
179        "TOTAL_FASTOPT_JIT_TIMES", GetTotalFastoptJitCount(),
180        "TOTAL_TIME_ON_MAIN_THREAD", GetTotalTimeOnMainThread(),
181        "TOTAL_TIME_ON_JIT_THREAD", GetTotalTimeOnJitThread(),
182        "TOTAL_TIME_ON_HOLD_LOCK", GetTotalLockHoldingTime(),
183        "MAX_TIME_ON_HOLD_LOCK", GetMaxLockHoldingTime(),
184        "LONG_TIME_OF_HOLD_LOCK", GetLongtimeLockCount(),
185        "UNINSTALL_TIME", GetJitDeoptCount());
186    if (ret != 0) {
187        LOG_JIT(ERROR) << "Jit Compiler Stats send stats event failed! ret = " << ret;
188    }
189#endif
190}
191
192void JitDfx::PrintJitBlockUILog()
193{
194    std::string jitType = isBaselineJit_ ? "Baseline JIT" : "Fast optimization JIT";
195    LOG_JIT(DEBUG) << "Jit BlockUI Event Log: "
196        << " bundleName: " << GetBundleName()
197        << " pid: " << GetPidNumber()
198        << " Single main thread time: " << GetSingleTimeOnMainThread()
199        << " Single Jit thread time: " << GetSingleTimeOnJitThread()
200        << " Jit type: " << jitType
201        << " method info: " << GetMethodInfo()
202        << "\n";
203    SendJitBlockUIEvent();
204    InitializeBlockUIRecord();
205}
206
207void JitDfx::SendJitBlockUIEvent() const
208{
209#ifdef ENABLE_HISYSEVENT
210    std::string jitType = isBaselineJit_ ? "Baseline JIT" : "Fast optimization JIT";
211    int32_t ret = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::ARKTS_RUNTIME,
212        "ARK_BLOCKUI_JIT",
213        OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
214        "BUNDLE_NAME", ConvertToStdString(GetBundleName()),
215        "PID", GetPidNumber(),
216        "JIT_TYPE", jitType,
217        "JIT_FUNCTION_NAME", ConvertToStdString(GetMethodInfo()),
218        "TIME_ON_MAIN_THREAD", GetSingleTimeOnMainThread(),
219        "TIME_ON_JIT_THREAD", GetSingleTimeOnJitThread());
220    if (ret != 0) {
221        LOG_JIT(ERROR) << "Jit Compiler Stats send jit blockUI event failed! ret = " << ret;
222    }
223#endif
224}
225
226void JitDfx::InitializeRecord()
227{
228    jitEventParams.totalBaselineJitTimes_.store(0);
229    jitEventParams.totalFastoptJitTimes_.store(0);
230    jitEventParams.jitDeoptTimes_.store(0);
231    jitEventParams.longtimeLockTimes_.store(0);
232    jitEventParams.totalTimeOnMainThread_.store(0);
233    jitEventParams.totalTimeOnJitThread_.store(0);
234    jitEventParams.totalLockHoldingTime_.store(0);
235    jitEventParams.maxLockHoldingTime_ .store(0);
236    ResetCompilerTime();
237}
238
239void JitDfx::InitializeBlockUIRecord()
240{
241    jitEventParams.singleTimeOnMainThread_.store(0);
242    jitEventParams.singleTimeOnJitThread_.store(0);
243    isBaselineJit_ = true;
244    methodInfo_ = "";
245    ResetBlockUIEventTime();
246}
247}  // namespace panda::ecmascript
248