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
26namespace panda::ecmascript {
27using Clock = std::chrono::high_resolution_clock;
28using Duration = std::chrono::duration<uint64_t, std::nano>;
29
30struct 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
44    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
50class Method;
51class JitDfx {
52public:
53    using ThreadId = uint32_t;
54    static JitDfx *GetInstance();
55    void Init(const JSRuntimeOptions &options, std::string &bundleName);
56    void EnableDump();
57    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
67    void SetBundleName(CString bundleName)
68    {
69        bundleName_ = bundleName;
70    }
71
72    CString GetBundleName() const
73    {
74        return bundleName_;
75    }
76
77    void SetPidNumber(ThreadId number)
78    {
79        pidNum_ = number;
80    }
81
82    ThreadId GetPidNumber() const
83    {
84        return pidNum_;
85    }
86
87    void RecordSpentTimeAndPrintStatsLogInJsThread(int time)
88    {
89        SetTotalTimeOnMainThread(time);
90        PrintJitStatsLog();
91    }
92
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
103    void SetTotalTimeOnMainThread(int time)
104    {
105        jitEventParams.totalTimeOnMainThread_.fetch_add(time);
106    }
107
108    int GetTotalTimeOnMainThread() const
109    {
110        return jitEventParams.totalTimeOnMainThread_.load() / CONVERT_TO_MILLISECOND;
111    }
112
113    void SetSingleTimeOnMainThread(int time)
114    {
115        jitEventParams.singleTimeOnMainThread_.store(time);
116    }
117
118    int GetSingleTimeOnMainThread() const
119    {
120        return jitEventParams.singleTimeOnMainThread_.load() / CONVERT_TO_MILLISECOND;
121    }
122
123    void SetTotalTimeOnJitThread(int time)
124    {
125        jitEventParams.totalTimeOnJitThread_.fetch_add(time);
126    }
127
128    int GetTotalTimeOnJitThread() const
129    {
130        return jitEventParams.totalTimeOnJitThread_.load() / CONVERT_TO_MILLISECOND;
131    }
132
133    void SetSingleTimeOnJitThread(int time)
134    {
135        jitEventParams.singleTimeOnJitThread_.store(time);
136    }
137
138    int GetSingleTimeOnJitThread() const
139    {
140        return jitEventParams.singleTimeOnJitThread_.load() / CONVERT_TO_MILLISECOND;
141    }
142
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
152    int GetTotalBaselineJitCount() const
153    {
154        return jitEventParams.totalBaselineJitTimes_.load();
155    }
156
157    int GetTotalFastoptJitCount() const
158    {
159        return jitEventParams.totalFastoptJitTimes_.load();
160    }
161
162    void SetIsBaselineJit(bool isBaselineJit)
163    {
164        isBaselineJit_ = isBaselineJit;
165    }
166
167    bool GetIsBaselineJit() const
168    {
169        return isBaselineJit_;
170    }
171
172    void SetMethodInfo(CString method)
173    {
174        methodInfo_ = method;
175    }
176
177    CString GetMethodInfo() const
178    {
179        return methodInfo_;
180    }
181
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
193    int GetTotalLockHoldingTime() const
194    {
195        return jitEventParams.totalLockHoldingTime_.load() / CONVERT_TO_MILLISECOND;
196    }
197
198    int GetMaxLockHoldingTime() const
199    {
200        return jitEventParams.maxLockHoldingTime_.load() / CONVERT_TO_MILLISECOND;
201    }
202
203    int GetLongtimeLockCount() const
204    {
205        return jitEventParams.longtimeLockTimes_.load();
206    }
207
208    void SetJitDeoptCount()
209    {
210        jitEventParams.jitDeoptTimes_.fetch_add(1);
211    }
212
213    int GetJitDeoptCount() const
214    {
215        return jitEventParams.jitDeoptTimes_.load();
216    }
217
218    void ResetCompilerTime()
219    {
220        jitEventParams.start_ = Clock::now();
221    }
222
223    void ResetBlockUIEventTime()
224    {
225        jitEventParams.blockUIEventstart_ = Clock::now();
226    }
227
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
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
246private:
247    void OpenLogFile(uint32_t threadId);
248    void InitializeRecord();
249    void InitializeBlockUIRecord();
250    void SendJitStatsEvent() const;
251    void SendJitBlockUIEvent() const;
252    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