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 IfNeedSkipBarrierStubHeaderOrTail(JSThread *thread, uint64_t pc) const; 101 bool IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const; 102 bool GetStackBeforeCallNapi(JSThread *thread); 103 void GetStackAfterCallNapi(JSThread *thread); 104 bool GetStackCallNapi(JSThread *thread, bool beforeCallNapi); 105 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) 106 // siginfo_t may be undefined on some platforms 107 static void GetStackSignalHandler(int signal, siginfo_t *siginfo, void *context); 108 #endif 109 110 bool StartCpuProfilerForInfo(); 111 bool StopCpuProfilerForInfo(std::unique_ptr<struct ProfileInfo> &profileInfo); 112 bool StartCpuProfilerForFile(const std::string &fileName); 113 bool StopCpuProfilerForFile(); 114 void SetCpuSamplingInterval(int interval); 115 void SetBuildNapiStack(bool flag); 116 bool GetBuildNapiStack(); 117 bool GetOutToFile(); 118 explicit CpuProfiler(const EcmaVM *vm, const int interval = CPUPROFILER_DEFAULT_INTERVAL); 119 virtual ~CpuProfiler(); 120 121 static CMap<pthread_t, struct TaskInfo> profilerMap_; 122 static EcmaVM *GetVmbyTid(pthread_t tid); 123 private: 124 static Mutex synchronizationMutex_; 125 126 void GetStack(FrameIterator &it); 127 static uint64_t GetPcFromContext(void *context); 128 bool IsAddrAtStubOrAot(uint64_t pc) const; 129 bool CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const; 130 bool RegisterGetStackSignal(); 131 132 bool isProfiling_ = false; 133 bool outToFile_ = false; 134 std::string fileName_ = ""; 135 SamplesRecord *generator_ = nullptr; 136 pthread_t tid_ = 0; 137 const EcmaVM *vm_ = nullptr; 138 uint32_t interval_ = 0; 139 uint64_t beforeCallNapiTimeStamp_ = 0; 140 std::atomic_bool isBuildNapiStack_ {false}; 141 bool enableVMTag_ {false}; 142 RunParams *params_ = nullptr; 143 144 friend class CpuProfilerFriendTest; 145 }; 146 147 class CallNapiScope { 148 public: CallNapiScope(CpuProfiler * profiler)149 inline explicit CallNapiScope(CpuProfiler *profiler) 150 { 151 profiler_ = profiler; 152 profiler_->SetBuildNapiStack(true); 153 } 154 ~CallNapiScope()155 inline ~CallNapiScope() 156 { 157 profiler_->SetBuildNapiStack(false); 158 } 159 private: 160 CpuProfiler *profiler_ {nullptr}; 161 }; 162 } // namespace panda::ecmascript 163 #endif // ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H