1/*
2 * Copyright (c) 2021-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_DFX_CPU_PROFILER_CPU_PROFILER_H
17#define ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H
18
19#include <csignal>
20
21
22#include "ecmascript/dfx/cpu_profiler/samples_record.h"
23#include "ecmascript/dfx/cpu_profiler/sampling_processor.h"
24#include "ecmascript/debugger/js_debugger_manager.h"
25#include "ecmascript/interpreter/frame_handler.h"
26#include "ecmascript/js_thread.h"
27
28#include "ecmascript/platform/mutex.h"
29
30namespace panda::ecmascript {
31const int THRESHOLD_GROWTH_FACTORY = 2; // 2:TimeDelta Threshold Growth Factory
32const int THRESHOLD_FIXED_INCREMENT = 2000; // 2000:TimeDelta Threshold Fixed Increment
33using JSTaggedType = uint64_t;
34using JsDebuggerManager = tooling::JsDebuggerManager;
35class SamplesRecord;
36
37struct TaskInfo {
38    const EcmaVM *vm_ { nullptr };
39    void *taskHandle_ { nullptr };
40};
41
42class GcStateScope {
43public:
44    inline explicit GcStateScope(JSThread *thread)
45    {
46        thread_ = thread;
47        thread_->SetGcState(true);
48    }
49
50    inline ~GcStateScope()
51    {
52        thread_->SetGcState(false);
53    }
54private:
55    JSThread *thread_ = nullptr;
56};
57
58class SignalStateScope {
59public:
60    inline explicit SignalStateScope(JsDebuggerManager *jsDebuggerManager)
61    {
62        jsDebuggerManager_ = jsDebuggerManager;
63        jsDebuggerManager_->SetSignalState(true);
64    }
65
66    inline ~SignalStateScope()
67    {
68        jsDebuggerManager_->SetSignalState(false);
69    }
70private:
71    JsDebuggerManager *jsDebuggerManager_ = nullptr;
72};
73
74class RuntimeStateScope {
75public:
76    inline explicit RuntimeStateScope(JSThread *thread)
77    {
78        thread_ = thread;
79        oldState_ = thread_->GetRuntimeState();
80        thread_->SetRuntimeState(true);
81    }
82
83    inline ~RuntimeStateScope()
84    {
85        thread_->SetRuntimeState(oldState_);
86    }
87private:
88    bool oldState_ = false;
89    JSThread *thread_ = nullptr;
90};
91
92class CpuProfiler {
93public:
94    static const int CPUPROFILER_DEFAULT_INTERVAL = 500; // 500:Default Sampling interval 500 microseconds
95    static const int INTERVAL_OF_ACTIVE_SAMPLING = 300; // 300:interval of active sampling
96    static const int INTERVAL_OF_INNER_START = 100; // 100:interval of inner start(stake in runtime) sampling
97
98    bool InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize,
99                        uint64_t tailSize) const;
100    bool IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const;
101    bool GetStackBeforeCallNapi(JSThread *thread);
102    void GetStackAfterCallNapi(JSThread *thread);
103    bool GetStackCallNapi(JSThread *thread, bool beforeCallNapi);
104#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
105    // siginfo_t may be undefined on some platforms
106    static void GetStackSignalHandler(int signal, siginfo_t *siginfo, void *context);
107#endif
108
109    bool StartCpuProfilerForInfo();
110    bool StopCpuProfilerForInfo(std::unique_ptr<struct ProfileInfo> &profileInfo);
111    bool StartCpuProfilerForFile(const std::string &fileName);
112    bool StopCpuProfilerForFile();
113    void SetCpuSamplingInterval(int interval);
114    void SetBuildNapiStack(bool flag);
115    bool GetBuildNapiStack();
116    bool GetOutToFile();
117    explicit CpuProfiler(const EcmaVM *vm, const int interval = CPUPROFILER_DEFAULT_INTERVAL);
118    virtual ~CpuProfiler();
119
120    static CMap<pthread_t, struct TaskInfo> profilerMap_;
121    static EcmaVM *GetVmbyTid(pthread_t tid);
122private:
123    static Mutex synchronizationMutex_;
124
125    void GetStack(FrameIterator &it);
126    static uint64_t GetPcFromContext(void *context);
127    bool IsAddrAtStubOrAot(uint64_t pc) const;
128    bool CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const;
129    bool RegisterGetStackSignal();
130
131    bool isProfiling_ = false;
132    bool outToFile_ = false;
133    std::string fileName_ = "";
134    SamplesRecord *generator_ = nullptr;
135    pthread_t tid_ = 0;
136    const EcmaVM *vm_ = nullptr;
137    uint32_t interval_ = 0;
138    uint64_t beforeCallNapiTimeStamp_ = 0;
139    std::atomic_bool isBuildNapiStack_ {false};
140    bool enableVMTag_ {false};
141    RunParams *params_ = nullptr;
142
143    friend class CpuProfilerFriendTest;
144};
145
146class CallNapiScope {
147public:
148    inline explicit CallNapiScope(CpuProfiler *profiler)
149    {
150        profiler_ = profiler;
151        profiler_->SetBuildNapiStack(true);
152    }
153
154    inline ~CallNapiScope()
155    {
156        profiler_->SetBuildNapiStack(false);
157    }
158private:
159    CpuProfiler *profiler_ {nullptr};
160};
161} // namespace panda::ecmascript
162#endif // ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H