* 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.
*/
#ifndef ECMASCRIPT_JIT_JIT_DFX_H
#define ECMASCRIPT_JIT_JIT_DFX_H
#include <fstream>
#include <map>
#include <atomic>
#include "ecmascript/log_wrapper.h"
#include "ecmascript/mem/c_string.h"
#include "ecmascript/jit/compile_decision.h"
namespace panda::ecmascript {
using Clock = std::chrono::high_resolution_clock;
using Duration = std::chrono::duration<uint64_t, std::nano>;
struct JitEventParams {
std::atomic<int> totalBaselineJitTimes_;
std::atomic<int> totalFastoptJitTimes_;
std::atomic<int> jitDeoptTimes_;
std::atomic<int> longtimeLockTimes_;
std::atomic<int> singleTimeOnMainThread_;
std::atomic<int> totalTimeOnMainThread_;
std::atomic<int> singleTimeOnJitThread_;
std::atomic<int> totalTimeOnJitThread_;
std::atomic<int> totalLockHoldingTime_;
std::atomic<int> maxLockHoldingTime_;
Clock::time_point start_;
Clock::time_point blockUIEventstart_;
JitEventParams() : totalBaselineJitTimes_(0), totalFastoptJitTimes_(0), jitDeoptTimes_(0),
longtimeLockTimes_(0), singleTimeOnMainThread_(0), totalTimeOnMainThread_(0),
singleTimeOnJitThread_(0), totalTimeOnJitThread_(0), totalLockHoldingTime_(0),
maxLockHoldingTime_(0), start_(Clock::now()), blockUIEventstart_(Clock::now()) {}
};
class Method;
class JitDfx {
public:
using ThreadId = uint32_t;
static JitDfx *GetInstance();
void Init(const JSRuntimeOptions &options, std::string &bundleName);
void EnableDump();
bool IsEnableDump() const
{
return isEnableDump_;
}
std::ostream &GetLogFileStream();
void DumpBytecodeInst(JSThread *thread, Method *method);
void TraceJitCode(JSThread *thread, Method *method, bool isEntry);
void SetBundleName(CString bundleName)
{
bundleName_ = bundleName;
}
CString GetBundleName() const
{
return bundleName_;
}
void SetPidNumber(ThreadId number)
{
pidNum_ = number;
}
ThreadId GetPidNumber() const
{
return pidNum_;
}
void RecordSpentTimeAndPrintStatsLogInJsThread(int time)
{
SetTotalTimeOnMainThread(time);
PrintJitStatsLog();
}
void RecordSpentTimeAndPrintStatsLogInJitThread(int compilerTime, CString methodName, bool isBaselineJit,
int mainThreadCompileTime)
{
SetTotalTimeOnJitThread(compilerTime);
if (ReportBlockUIEvent(mainThreadCompileTime)) {
SetBlockUIEventInfo(methodName, isBaselineJit, mainThreadCompileTime, compilerTime);
}
PrintJitStatsLog();
}
void SetTotalTimeOnMainThread(int time)
{
jitEventParams.totalTimeOnMainThread_.fetch_add(time);
}
int GetTotalTimeOnMainThread() const
{
return jitEventParams.totalTimeOnMainThread_.load() / CONVERT_TO_MILLISECOND;
}
void SetSingleTimeOnMainThread(int time)
{
jitEventParams.singleTimeOnMainThread_.store(time);
}
int GetSingleTimeOnMainThread() const
{
return jitEventParams.singleTimeOnMainThread_.load() / CONVERT_TO_MILLISECOND;
}
void SetTotalTimeOnJitThread(int time)
{
jitEventParams.totalTimeOnJitThread_.fetch_add(time);
}
int GetTotalTimeOnJitThread() const
{
return jitEventParams.totalTimeOnJitThread_.load() / CONVERT_TO_MILLISECOND;
}
void SetSingleTimeOnJitThread(int time)
{
jitEventParams.singleTimeOnJitThread_.store(time);
}
int GetSingleTimeOnJitThread() const
{
return jitEventParams.singleTimeOnJitThread_.load() / CONVERT_TO_MILLISECOND;
}
void SetTriggerCount(CompilerTier tier)
{
if (tier.IsBaseLine()) {
jitEventParams.totalBaselineJitTimes_.fetch_add(1);
} else {
jitEventParams.totalFastoptJitTimes_.fetch_add(1);
}
}
int GetTotalBaselineJitCount() const
{
return jitEventParams.totalBaselineJitTimes_.load();
}
int GetTotalFastoptJitCount() const
{
return jitEventParams.totalFastoptJitTimes_.load();
}
void SetIsBaselineJit(bool isBaselineJit)
{
isBaselineJit_ = isBaselineJit;
}
bool GetIsBaselineJit() const
{
return isBaselineJit_;
}
void SetMethodInfo(CString method)
{
methodInfo_ = method;
}
CString GetMethodInfo() const
{
return methodInfo_;
}
void SetLockHoldingTime(int time)
{
jitEventParams.totalLockHoldingTime_.fetch_add(time);
if (time > jitEventParams.maxLockHoldingTime_.load()) {
jitEventParams.maxLockHoldingTime_.store(time);
}
if (time > HOLD_LOCK_LIMIT) {
jitEventParams.longtimeLockTimes_.fetch_add(1);
}
}
int GetTotalLockHoldingTime() const
{
return jitEventParams.totalLockHoldingTime_.load() / CONVERT_TO_MILLISECOND;
}
int GetMaxLockHoldingTime() const
{
return jitEventParams.maxLockHoldingTime_.load() / CONVERT_TO_MILLISECOND;
}
int GetLongtimeLockCount() const
{
return jitEventParams.longtimeLockTimes_.load();
}
void SetJitDeoptCount()
{
jitEventParams.jitDeoptTimes_.fetch_add(1);
}
int GetJitDeoptCount() const
{
return jitEventParams.jitDeoptTimes_.load();
}
void ResetCompilerTime()
{
jitEventParams.start_ = Clock::now();
}
void ResetBlockUIEventTime()
{
jitEventParams.blockUIEventstart_ = Clock::now();
}
bool ReportBlockUIEvent(int time)
{
return std::chrono::duration_cast<std::chrono::seconds>(Clock::now() -
jitEventParams.blockUIEventstart_).count() >= MIN_SEND_INTERVAL && (time >= MAX_OCCUPY_MAIN_THREAD_TIME);
}
void SetBlockUIEventInfo(CString info, bool isBaselineJit, int mainThreadTime, int jitThreadTime)
{
SetMethodInfo(info);
SetIsBaselineJit(isBaselineJit);
SetSingleTimeOnMainThread(mainThreadTime);
SetSingleTimeOnJitThread(jitThreadTime);
PrintJitBlockUILog();
}
void PrintJitStatsLog();
void PrintJitBlockUILog();
private:
void OpenLogFile(uint32_t threadId);
void InitializeRecord();
void InitializeBlockUIRecord();
void SendJitStatsEvent() const;
void SendJitBlockUIEvent() const;
bool checkUploadConditions() const
{
return std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - jitEventParams.start_).count() >=
MIN_SEND_INTERVAL && (jitEventParams.totalBaselineJitTimes_.load() +
jitEventParams.totalFastoptJitTimes_.load()) >= MAX_TRIGGER_TIMES;
}
static JitDfx instance;
bool isEnableDump_ {false};
bool isBaselineJit_ {true};
std::map<uint32_t, std::ofstream> logFiles_;
static thread_local uint32_t prefixOffset_;
CString bundleName_ = "";
CString methodInfo_ = "";
ThreadId pidNum_ {0};
JitEventParams jitEventParams;
static constexpr int MAX_TRIGGER_TIMES = 100;
static constexpr int MIN_SEND_INTERVAL = 60;
static constexpr int HOLD_LOCK_LIMIT = 1000;
static constexpr int MAX_OCCUPY_MAIN_THREAD_TIME = 3000;
static constexpr int CONVERT_TO_MILLISECOND = 1000;
};
}
#endif