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(Method * method)95 void 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
TraceJitCode(Method * method,bool isEntry)125 void 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
PrintJitStatsLog()146 void 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
SendJitStatsEvent() const168 void 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
PrintJitBlockUILog()192 void 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
SendJitBlockUIEvent() const207 void 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
InitializeRecord()226 void 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
InitializeBlockUIRecord()239 void 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