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