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 
30 namespace panda::ecmascript {
31 const int THRESHOLD_GROWTH_FACTORY = 2; // 2:TimeDelta Threshold Growth Factory
32 const int THRESHOLD_FIXED_INCREMENT = 2000; // 2000:TimeDelta Threshold Fixed Increment
33 using JSTaggedType = uint64_t;
34 using JsDebuggerManager = tooling::JsDebuggerManager;
35 class SamplesRecord;
36 
37 struct TaskInfo {
38     const EcmaVM *vm_ { nullptr };
39     void *taskHandle_ { nullptr };
40 };
41 
42 class GcStateScope {
43 public:
GcStateScope(JSThread *thread)44     inline explicit GcStateScope(JSThread *thread)
45     {
46         thread_ = thread;
47         thread_->SetGcState(true);
48     }
49 
~GcStateScope()50     inline ~GcStateScope()
51     {
52         thread_->SetGcState(false);
53     }
54 private:
55     JSThread *thread_ = nullptr;
56 };
57 
58 class SignalStateScope {
59 public:
SignalStateScope(JsDebuggerManager *jsDebuggerManager)60     inline explicit SignalStateScope(JsDebuggerManager *jsDebuggerManager)
61     {
62         jsDebuggerManager_ = jsDebuggerManager;
63         jsDebuggerManager_->SetSignalState(true);
64     }
65 
~SignalStateScope()66     inline ~SignalStateScope()
67     {
68         jsDebuggerManager_->SetSignalState(false);
69     }
70 private:
71     JsDebuggerManager *jsDebuggerManager_ = nullptr;
72 };
73 
74 class RuntimeStateScope {
75 public:
RuntimeStateScope(JSThread *thread)76     inline explicit RuntimeStateScope(JSThread *thread)
77     {
78         thread_ = thread;
79         oldState_ = thread_->GetRuntimeState();
80         thread_->SetRuntimeState(true);
81     }
82 
~RuntimeStateScope()83     inline ~RuntimeStateScope()
84     {
85         thread_->SetRuntimeState(oldState_);
86     }
87 private:
88     bool oldState_ = false;
89     JSThread *thread_ = nullptr;
90 };
91 
92 class CpuProfiler {
93 public:
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);
122 private:
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 
146 class CallNapiScope {
147 public:
CallNapiScope(CpuProfiler *profiler)148     inline explicit CallNapiScope(CpuProfiler *profiler)
149     {
150         profiler_ = profiler;
151         profiler_->SetBuildNapiStack(true);
152     }
153 
~CallNapiScope()154     inline ~CallNapiScope()
155     {
156         profiler_->SetBuildNapiStack(false);
157     }
158 private:
159     CpuProfiler *profiler_ {nullptr};
160 };
161 } // namespace panda::ecmascript
162 #endif // ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H