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 #ifndef ECMASCRIPT_JIT_JIT_DFX_H
17 #define ECMASCRIPT_JIT_JIT_DFX_H
18 
19 #include <fstream>
20 #include <map>
21 #include <atomic>
22 
23 #include "ecmascript/log.h"
24 #include "ecmascript/mem/c_string.h"
25 
26 namespace panda::ecmascript {
27 using Clock = std::chrono::high_resolution_clock;
28 using Duration = std::chrono::duration<uint64_t, std::nano>;
29 
30 struct JitEventParams {
31     std::atomic<int> totalBaselineJitTimes_;
32     std::atomic<int> totalFastoptJitTimes_;
33     std::atomic<int> jitDeoptTimes_;
34     std::atomic<int> longtimeLockTimes_;
35     std::atomic<int> singleTimeOnMainThread_;
36     std::atomic<int> totalTimeOnMainThread_;
37     std::atomic<int> singleTimeOnJitThread_;
38     std::atomic<int> totalTimeOnJitThread_;
39     std::atomic<int> totalLockHoldingTime_;
40     std::atomic<int> maxLockHoldingTime_;
41     Clock::time_point start_;
42     Clock::time_point blockUIEventstart_;
43 
JitEventParamspanda::ecmascript::JitEventParams44     JitEventParams() : totalBaselineJitTimes_(0), totalFastoptJitTimes_(0), jitDeoptTimes_(0),
45         longtimeLockTimes_(0), singleTimeOnMainThread_(0), totalTimeOnMainThread_(0),
46         singleTimeOnJitThread_(0), totalTimeOnJitThread_(0), totalLockHoldingTime_(0),
47         maxLockHoldingTime_(0), start_(Clock::now()), blockUIEventstart_(Clock::now()) {}
48 };
49 
50 class Method;
51 class JitDfx {
52 public:
53     using ThreadId = uint32_t;
54     static JitDfx *GetInstance();
55     void Init(const JSRuntimeOptions &options, std::string &bundleName);
56     void EnableDump();
IsEnableDump() const57     bool IsEnableDump() const
58     {
59         return isEnableDump_;
60     }
61 
62     std::ostream &GetLogFileStream();
63 
64     void DumpBytecodeInst(Method *method);
65     void TraceJitCode(Method *method, bool isEntry);
66 
SetBundleName(CString bundleName)67     void SetBundleName(CString bundleName)
68     {
69         bundleName_ = bundleName;
70     }
71 
GetBundleName() const72     CString GetBundleName() const
73     {
74         return bundleName_;
75     }
76 
SetPidNumber(ThreadId number)77     void SetPidNumber(ThreadId number)
78     {
79         pidNum_ = number;
80     }
81 
GetPidNumber() const82     ThreadId GetPidNumber() const
83     {
84         return pidNum_;
85     }
86 
RecordSpentTimeAndPrintStatsLogInJsThread(int time)87     void RecordSpentTimeAndPrintStatsLogInJsThread(int time)
88     {
89         SetTotalTimeOnMainThread(time);
90         PrintJitStatsLog();
91     }
92 
RecordSpentTimeAndPrintStatsLogInJitThread(int compilerTime, CString methodName, bool isBaselineJit, int mainThreadCompileTime)93     void RecordSpentTimeAndPrintStatsLogInJitThread(int compilerTime, CString methodName, bool isBaselineJit,
94         int mainThreadCompileTime)
95     {
96         SetTotalTimeOnJitThread(compilerTime);
97         if (ReportBlockUIEvent(mainThreadCompileTime)) {
98             SetBlockUIEventInfo(methodName, isBaselineJit, mainThreadCompileTime, compilerTime);
99         }
100         PrintJitStatsLog();
101     }
102 
SetTotalTimeOnMainThread(int time)103     void SetTotalTimeOnMainThread(int time)
104     {
105         jitEventParams.totalTimeOnMainThread_.fetch_add(time);
106     }
107 
GetTotalTimeOnMainThread() const108     int GetTotalTimeOnMainThread() const
109     {
110         return jitEventParams.totalTimeOnMainThread_.load() / CONVERT_TO_MILLISECOND;
111     }
112 
SetSingleTimeOnMainThread(int time)113     void SetSingleTimeOnMainThread(int time)
114     {
115         jitEventParams.singleTimeOnMainThread_.store(time);
116     }
117 
GetSingleTimeOnMainThread() const118     int GetSingleTimeOnMainThread() const
119     {
120         return jitEventParams.singleTimeOnMainThread_.load() / CONVERT_TO_MILLISECOND;
121     }
122 
SetTotalTimeOnJitThread(int time)123     void SetTotalTimeOnJitThread(int time)
124     {
125         jitEventParams.totalTimeOnJitThread_.fetch_add(time);
126     }
127 
GetTotalTimeOnJitThread() const128     int GetTotalTimeOnJitThread() const
129     {
130         return jitEventParams.totalTimeOnJitThread_.load() / CONVERT_TO_MILLISECOND;
131     }
132 
SetSingleTimeOnJitThread(int time)133     void SetSingleTimeOnJitThread(int time)
134     {
135         jitEventParams.singleTimeOnJitThread_.store(time);
136     }
137 
GetSingleTimeOnJitThread() const138     int GetSingleTimeOnJitThread() const
139     {
140         return jitEventParams.singleTimeOnJitThread_.load() / CONVERT_TO_MILLISECOND;
141     }
142 
SetTriggerCount(bool isBaselineJit)143     void SetTriggerCount(bool isBaselineJit)
144     {
145         if (isBaselineJit) {
146             jitEventParams.totalBaselineJitTimes_.fetch_add(1);
147         } else {
148             jitEventParams.totalFastoptJitTimes_.fetch_add(1);
149         }
150     }
151 
GetTotalBaselineJitCount() const152     int GetTotalBaselineJitCount() const
153     {
154         return jitEventParams.totalBaselineJitTimes_.load();
155     }
156 
GetTotalFastoptJitCount() const157     int GetTotalFastoptJitCount() const
158     {
159         return jitEventParams.totalFastoptJitTimes_.load();
160     }
161 
SetIsBaselineJit(bool isBaselineJit)162     void SetIsBaselineJit(bool isBaselineJit)
163     {
164         isBaselineJit_ = isBaselineJit;
165     }
166 
GetIsBaselineJit() const167     bool GetIsBaselineJit() const
168     {
169         return isBaselineJit_;
170     }
171 
SetMethodInfo(CString method)172     void SetMethodInfo(CString method)
173     {
174         methodInfo_ = method;
175     }
176 
GetMethodInfo() const177     CString GetMethodInfo() const
178     {
179         return methodInfo_;
180     }
181 
SetLockHoldingTime(int time)182     void SetLockHoldingTime(int time)
183     {
184         jitEventParams.totalLockHoldingTime_.fetch_add(time);
185         if (time > jitEventParams.maxLockHoldingTime_.load()) {
186             jitEventParams.maxLockHoldingTime_.store(time);
187         }
188         if (time > HOLD_LOCK_LIMIT) {
189             jitEventParams.longtimeLockTimes_.fetch_add(1);
190         }
191     }
192 
GetTotalLockHoldingTime() const193     int GetTotalLockHoldingTime() const
194     {
195         return jitEventParams.totalLockHoldingTime_.load() / CONVERT_TO_MILLISECOND;
196     }
197 
GetMaxLockHoldingTime() const198     int GetMaxLockHoldingTime() const
199     {
200         return jitEventParams.maxLockHoldingTime_.load() / CONVERT_TO_MILLISECOND;
201     }
202 
GetLongtimeLockCount() const203     int GetLongtimeLockCount() const
204     {
205         return jitEventParams.longtimeLockTimes_.load();
206     }
207 
SetJitDeoptCount()208     void SetJitDeoptCount()
209     {
210         jitEventParams.jitDeoptTimes_.fetch_add(1);
211     }
212 
GetJitDeoptCount() const213     int GetJitDeoptCount() const
214     {
215         return jitEventParams.jitDeoptTimes_.load();
216     }
217 
ResetCompilerTime()218     void ResetCompilerTime()
219     {
220         jitEventParams.start_ = Clock::now();
221     }
222 
ResetBlockUIEventTime()223     void ResetBlockUIEventTime()
224     {
225         jitEventParams.blockUIEventstart_ = Clock::now();
226     }
227 
ReportBlockUIEvent(int time)228     bool ReportBlockUIEvent(int time)
229     {
230         return std::chrono::duration_cast<std::chrono::seconds>(Clock::now() -
231             jitEventParams.blockUIEventstart_).count() >= MIN_SEND_INTERVAL && (time >= MAX_OCCUPY_MAIN_THREAD_TIME);
232     }
233 
SetBlockUIEventInfo(CString info, bool isBaselineJit, int mainThreadTime, int jitThreadTime)234     void SetBlockUIEventInfo(CString info, bool isBaselineJit, int mainThreadTime, int jitThreadTime)
235     {
236         SetMethodInfo(info);
237         SetIsBaselineJit(isBaselineJit);
238         SetSingleTimeOnMainThread(mainThreadTime);
239         SetSingleTimeOnJitThread(jitThreadTime);
240         PrintJitBlockUILog();
241     }
242 
243     void PrintJitStatsLog();
244     void PrintJitBlockUILog();
245 
246 private:
247     void OpenLogFile(uint32_t threadId);
248     void InitializeRecord();
249     void InitializeBlockUIRecord();
250     void SendJitStatsEvent() const;
251     void SendJitBlockUIEvent() const;
checkUploadConditions() const252     bool checkUploadConditions() const
253     {
254         return std::chrono::duration_cast<std::chrono::seconds>(Clock::now() - jitEventParams.start_).count() >=
255             MIN_SEND_INTERVAL && (jitEventParams.totalBaselineJitTimes_.load() +
256             jitEventParams.totalFastoptJitTimes_.load()) >= MAX_TRIGGER_TIMES;
257     }
258 
259     static JitDfx instance;
260     bool isEnableDump_ {false};
261     bool isBaselineJit_ {true};
262     std::map<uint32_t, std::ofstream> logFiles_;
263     static thread_local uint32_t prefixOffset_;
264 
265     CString bundleName_ = "";
266     CString methodInfo_ = "";
267     ThreadId pidNum_ {0};
268     JitEventParams jitEventParams;
269     static constexpr int MAX_TRIGGER_TIMES = 100;
270     static constexpr int MIN_SEND_INTERVAL = 60; // seconds
271     static constexpr int HOLD_LOCK_LIMIT = 1000; // microseconds
272     static constexpr int MAX_OCCUPY_MAIN_THREAD_TIME = 3000; // microseconds
273     static constexpr int CONVERT_TO_MILLISECOND = 1000;
274 };
275 }  // namespace panda::ecmascript
276 #endif  // ECMASCRIPT_JIT_JIT_DFX_H
277