* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/jit/jit_dfx.h"
#include "ecmascript/runtime.h"
#include "bytecode_instruction-inl.h"
#include "code_data_accessor.h"
#include "class_data_accessor-inl.h"
#include "ecmascript/platform/file.h"
#ifdef ENABLE_HISYSEVENT
#include "hisysevent.h"
#endif
namespace panda::ecmascript {
JitDfx JitDfx::instance;
thread_local uint32_t JitDfx::prefixOffset_ = 0;
class NullStream : public std::ostream {
public:
NullStream() : std::ostream(&buffer_) {}
private:
class NullBuffer : public std::streambuf {
public:
int overflow(int c) override
{
return c;
}
};
NullBuffer buffer_;
};
JitDfx *JitDfx::GetInstance()
{
return &instance;
}
void JitDfx::Init(const JSRuntimeOptions &options, std::string &bundleName)
{
if (options.IsEnableJitDfxDump()) {
EnableDump();
}
ResetCompilerTime();
ResetBlockUIEventTime();
SetBundleName(ConvertToString(bundleName));
SetPidNumber(JSThread::GetCurrentThreadId());
}
void JitDfx::EnableDump()
{
isEnableDump_ = true;
}
void JitDfx::OpenLogFile(uint32_t threadId)
{
#ifdef PANDA_TARGET_OHOS
CString path = CString("/data/storage/ark-profile/jit_dfx_") + ToCString(threadId) + CString(".log");
#else
CString path = CString("jit_dfx_") + ToCString(threadId) + CString(".log");
#endif
std::string realOutPath;
if (!ecmascript::RealPath(path.c_str(), realOutPath, false)) {
return;
}
logFiles_[threadId].open(realOutPath, std::ios::out);
}
std::ostream &JitDfx::GetLogFileStream()
{
if (!isEnableDump_) {
static NullStream nullStream_;
return nullStream_;
}
uint32_t threadId = os::thread::GetCurrentThreadId();
auto it = logFiles_.find(threadId);
if (it == logFiles_.end()) {
OpenLogFile(threadId);
}
return logFiles_[threadId];
}
void JitDfx::DumpBytecodeInst(JSThread *thread, Method *method)
{
if (!isEnableDump_) {
return;
}
CString methodInfo = method->GetRecordNameStr(thread) + "." + CString(method->GetMethodName(thread));
MethodLiteral *methodLiteral = method->GetMethodLiteral(thread);
auto jsPandaFile = method->GetJSPandaFile(thread);
ASSERT(jsPandaFile != nullptr);
const panda_file::File *pf = jsPandaFile->GetPandaFile();
ASSERT(methodLiteral != nullptr);
panda_file::File::EntityId methodIdx = methodLiteral->GetMethodId();
panda_file::MethodDataAccessor mda(*pf, methodIdx);
auto codeId = mda.GetCodeId();
panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
uint32_t codeSize = codeDataAccessor.GetCodeSize();
const uint8_t *insns = codeDataAccessor.GetInstructions();
std::ostringstream ss;
ss << "BytecodeInst func:" << methodInfo << "\n";
auto bcIns = BytecodeInst(insns);
auto bcInsLast = bcIns.JumpTo(codeSize);
while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
ss << bcIns << std::endl;
auto nextInst = bcIns.GetNext();
bcIns = nextInst;
}
GetLogFileStream() << ss.str() << std::endl;
}
void JitDfx::TraceJitCode(JSThread *thread, Method *method, bool isEntry)
{
if (!isEnableDump_) {
return;
}
if (!isEntry) {
prefixOffset_ -= 1;
}
CString prefixStr = isEntry ? CString("JitCodeEntry:") : CString("JitCodeExit :");
CString methodInfo = method->GetRecordNameStr(thread) + "." + CString(method->GetMethodName(thread));
static CString blackSpace(" ");
CString prefix;
for (uint32_t i = 0; i < prefixOffset_; i++) {
prefix += blackSpace;
}
if (isEntry) {
prefixOffset_ += 1;
}
LOG_JIT(INFO) << prefixStr << prefix << methodInfo;
}
void JitDfx::PrintJitStatsLog()
{
if (checkUploadConditions()) {
LOG_JIT(DEBUG) << "Jit Compiler stats Log: "
<< " bundleName: " << GetBundleName()
<< " pid: " << GetPidNumber()
<< " total main thread time: " << GetTotalTimeOnMainThread()
<< " total Jit thread time: " << GetTotalTimeOnJitThread()
<< " total Baseline Jit times: " << GetTotalBaselineJitCount()
<< " total Fastopt Jit times: " << GetTotalFastoptJitCount()
<< " report time interval:"
<< std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - jitEventParams.start_).count()
<< " total time on hold lock: " << GetTotalLockHoldingTime()
<< " max time on hold lock: " << GetMaxLockHoldingTime()
<< " longtime of hold lock: " << GetLongtimeLockCount()
<< " JitDeopt times: " << GetJitDeoptCount()
<< "\n";
SendJitStatsEvent();
InitializeRecord();
}
}
void JitDfx::SendJitStatsEvent() const
{
#ifdef ENABLE_HISYSEVENT
int32_t ret = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::ARKTS_RUNTIME,
"ARK_STATS_JIT",
OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
"BUNDLE_NAME", ConvertToStdString(GetBundleName()),
"PID", GetPidNumber(),
"TIME_INTERVAL",
std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - jitEventParams.start_).count(),
"TOTAL_BASELINE_JIT_TIMES", GetTotalBaselineJitCount(),
"TOTAL_FASTOPT_JIT_TIMES", GetTotalFastoptJitCount(),
"TOTAL_TIME_ON_MAIN_THREAD", GetTotalTimeOnMainThread(),
"TOTAL_TIME_ON_JIT_THREAD", GetTotalTimeOnJitThread(),
"TOTAL_TIME_ON_HOLD_LOCK", GetTotalLockHoldingTime(),
"MAX_TIME_ON_HOLD_LOCK", GetMaxLockHoldingTime(),
"LONG_TIME_OF_HOLD_LOCK", GetLongtimeLockCount(),
"UNINSTALL_TIME", GetJitDeoptCount());
if (ret != 0) {
LOG_JIT(ERROR) << "Jit Compiler Stats send stats event failed! ret = " << ret;
}
#endif
}
void JitDfx::PrintJitBlockUILog()
{
std::string jitType = isBaselineJit_ ? "Baseline JIT" : "Fast optimization JIT";
LOG_JIT(DEBUG) << "Jit BlockUI Event Log: "
<< " bundleName: " << GetBundleName()
<< " pid: " << GetPidNumber()
<< " Single main thread time: " << GetSingleTimeOnMainThread()
<< " Single Jit thread time: " << GetSingleTimeOnJitThread()
<< " Jit type: " << jitType
<< " method info: " << GetMethodInfo()
<< "\n";
SendJitBlockUIEvent();
InitializeBlockUIRecord();
}
void JitDfx::SendJitBlockUIEvent() const
{
#ifdef ENABLE_HISYSEVENT
std::string jitType = isBaselineJit_ ? "Baseline JIT" : "Fast optimization JIT";
int32_t ret = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::ARKTS_RUNTIME,
"ARK_BLOCKUI_JIT",
OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
"BUNDLE_NAME", ConvertToStdString(GetBundleName()),
"PID", GetPidNumber(),
"JIT_TYPE", jitType,
"JIT_FUNCTION_NAME", ConvertToStdString(GetMethodInfo()),
"TIME_ON_MAIN_THREAD", GetSingleTimeOnMainThread(),
"TIME_ON_JIT_THREAD", GetSingleTimeOnJitThread());
if (ret != 0) {
LOG_JIT(ERROR) << "Jit Compiler Stats send jit blockUI event failed! ret = " << ret;
}
#endif
}
void JitDfx::InitializeRecord()
{
jitEventParams.totalBaselineJitTimes_.store(0);
jitEventParams.totalFastoptJitTimes_.store(0);
jitEventParams.jitDeoptTimes_.store(0);
jitEventParams.longtimeLockTimes_.store(0);
jitEventParams.totalTimeOnMainThread_.store(0);
jitEventParams.totalTimeOnJitThread_.store(0);
jitEventParams.totalLockHoldingTime_.store(0);
jitEventParams.maxLockHoldingTime_ .store(0);
ResetCompilerTime();
}
void JitDfx::InitializeBlockUIRecord()
{
jitEventParams.singleTimeOnMainThread_.store(0);
jitEventParams.singleTimeOnJitThread_.store(0);
isBaselineJit_ = true;
methodInfo_ = "";
ResetBlockUIEventTime();
}
}