1 /* 2 * Copyright (c) 2021-2023 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_DEBUGGER_JS_DEBUGGER_H 17 #define ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H 18 19 #include "ecmascript/debugger/debugger_api.h" 20 #include "ecmascript/debugger/js_debugger_manager.h" 21 #include "ecmascript/debugger/js_pt_method.h" 22 #include "ecmascript/ecma_vm.h" 23 #include "ecmascript/jspandafile/method_literal.h" 24 25 namespace panda::ecmascript::tooling { 26 class JSBreakpoint { 27 public: 28 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) JSBreakpoint(const std::string & sourceFile,PtMethod * ptMethod,uint32_t bcOffset,const Global<FunctionRef> & condFuncRef)29 JSBreakpoint(const std::string &sourceFile, PtMethod *ptMethod, uint32_t bcOffset, 30 const Global<FunctionRef> &condFuncRef) : sourceFile_(sourceFile), ptMethod_(ptMethod), 31 bcOffset_(bcOffset), condFuncRef_(condFuncRef) {} 32 ~JSBreakpoint() = default; 33 GetSourceFile()34 const std::string &GetSourceFile() const 35 { 36 return sourceFile_; 37 } 38 GetPtMethod()39 PtMethod *GetPtMethod() const 40 { 41 return ptMethod_; 42 } 43 GetBytecodeOffset()44 uint32_t GetBytecodeOffset() const 45 { 46 return bcOffset_; 47 } 48 49 bool operator==(const JSBreakpoint &bpoint) const 50 { 51 return bcOffset_ == bpoint.GetBytecodeOffset() && 52 ptMethod_->GetMethodId() == bpoint.GetPtMethod()->GetMethodId() && 53 sourceFile_ == bpoint.GetSourceFile() && 54 ptMethod_->GetJSPandaFile() == bpoint.GetPtMethod()->GetJSPandaFile(); 55 } 56 ToString()57 std::string ToString() const 58 { 59 std::stringstream breakpoint; 60 breakpoint << "["; 61 breakpoint << "methodId:" << ptMethod_->GetMethodId() << ", "; 62 breakpoint << "bytecodeOffset:" << bcOffset_ << ", "; 63 breakpoint << "sourceFile:" << "\""<< sourceFile_ << "\""<< ", "; 64 breakpoint << "jsPandaFile:" << "\"" << ptMethod_->GetJSPandaFile()->GetJSPandaFileDesc() << "\""; 65 breakpoint << "]"; 66 return breakpoint.str(); 67 } 68 GetConditionFunction()69 const Global<FunctionRef> &GetConditionFunction() 70 { 71 return condFuncRef_; 72 } 73 74 DEFAULT_COPY_SEMANTIC(JSBreakpoint); 75 DEFAULT_MOVE_SEMANTIC(JSBreakpoint); 76 77 private: 78 std::string sourceFile_; 79 PtMethod *ptMethod_ {nullptr}; 80 uint32_t bcOffset_; 81 Global<FunctionRef> condFuncRef_; 82 }; 83 84 class HashJSBreakpoint { 85 public: operator()86 size_t operator()(const JSBreakpoint &bpoint) const 87 { 88 return (std::hash<std::string>()(bpoint.GetSourceFile())) ^ 89 (std::hash<uint32_t>()(bpoint.GetPtMethod()->GetMethodId().GetOffset())) ^ 90 (std::hash<uint32_t>()(bpoint.GetBytecodeOffset())); 91 } 92 }; 93 94 class PUBLIC_API JSDebugger : public JSDebugInterface, RuntimeListener { 95 public: JSDebugger(const EcmaVM * vm)96 explicit JSDebugger(const EcmaVM *vm) : ecmaVm_(vm) 97 { 98 notificationMgr_ = ecmaVm_->GetJsDebuggerManager()->GetNotificationManager(); 99 notificationMgr_->AddListener(this); 100 } ~JSDebugger()101 ~JSDebugger() override 102 { 103 notificationMgr_->RemoveListener(this); 104 } 105 RegisterHooks(PtHooks * hooks)106 void RegisterHooks(PtHooks *hooks) override 107 { 108 hooks_ = hooks; 109 // send vm start event after add hooks 110 notificationMgr_->VmStartEvent(); 111 } UnregisterHooks()112 void UnregisterHooks() override 113 { 114 // send vm death event before delete hooks 115 notificationMgr_->VmDeathEvent(); 116 hooks_ = nullptr; 117 } 118 bool HandleDebuggerStmt(JSHandle<Method> method, uint32_t bcOffset) override; 119 bool SetBreakpoint(const JSPtLocation &location, Local<FunctionRef> condFuncRef) override; 120 bool SetSmartBreakpoint(const JSPtLocation &location); 121 bool RemoveBreakpoint(const JSPtLocation &location) override; 122 void RemoveAllBreakpoints() override; 123 bool RemoveBreakpointsByUrl(const std::string &url) override; 124 void BytecodePcChanged(JSThread *thread, JSHandle<Method> method, uint32_t bcOffset) override; GenerateAsyncFrames(std::shared_ptr<AsyncStack> asyncStack,bool skipTopFrame)125 void GenerateAsyncFrames(std::shared_ptr<AsyncStack> asyncStack, bool skipTopFrame) override 126 { 127 if (hooks_ == nullptr) { 128 return; 129 } 130 hooks_->GenerateAsyncFrames(asyncStack, skipTopFrame); 131 } LoadModule(std::string_view filename,std::string_view entryPoint)132 void LoadModule(std::string_view filename, std::string_view entryPoint) override 133 { 134 if (hooks_ == nullptr) { 135 return; 136 } 137 hooks_->LoadModule(filename, entryPoint); 138 } VmStart()139 void VmStart() override 140 { 141 if (hooks_ == nullptr) { 142 return; 143 } 144 hooks_->VmStart(); 145 } VmDeath()146 void VmDeath() override 147 { 148 if (hooks_ == nullptr) { 149 return; 150 } 151 hooks_->VmDeath(); 152 } NativeCalling(const void * nativeAddress)153 void NativeCalling(const void *nativeAddress) override 154 { 155 if (hooks_ == nullptr) { 156 return; 157 } 158 hooks_->NativeCalling(nativeAddress); 159 } NativeReturn(const void * nativeAddress)160 void NativeReturn(const void *nativeAddress) override 161 { 162 if (hooks_ == nullptr) { 163 return; 164 } 165 hooks_->NativeReturn(nativeAddress); 166 } 167 void MethodEntry(JSHandle<Method> method, JSHandle<JSTaggedValue> envHandle) override; 168 void MethodExit(JSHandle<Method> method) override; 169 // used by debugger statement GetSingleStepStatus()170 bool GetSingleStepStatus() const 171 { 172 return singleStepOnDebuggerStmt_; 173 } SetSingleStepStatus(bool status)174 void SetSingleStepStatus(bool status) 175 { 176 singleStepOnDebuggerStmt_ = status; 177 } 178 // Used by Launch Accelerate mode DisableFirstTimeFlag()179 void DisableFirstTimeFlag() 180 { 181 if (hooks_ == nullptr) { 182 return; 183 } 184 hooks_->DisableFirstTimeFlag(); 185 } 186 SetSymbolicBreakpoint(const std::unordered_set<std::string> & functionNamesSet)187 void SetSymbolicBreakpoint(const std::unordered_set<std::string> &functionNamesSet) 188 { 189 symbolicBreakpoints_.insert(functionNamesSet.begin(), functionNamesSet.end()); 190 } 191 RemoveSymbolicBreakpoint(const std::string & functionName)192 void RemoveSymbolicBreakpoint(const std::string &functionName) 193 { 194 auto symbolicBreakpoint = symbolicBreakpoints_.find(functionName); 195 if (symbolicBreakpoint != symbolicBreakpoints_.end()) { 196 symbolicBreakpoints_.erase(symbolicBreakpoint); 197 } else { 198 LOG_DEBUGGER(ERROR) << "RemoveSymbolicBreakpoint: invalid symbol breakpoint " << functionName; 199 } 200 } 201 202 private: 203 std::unique_ptr<PtMethod> FindMethod(const JSPtLocation &location) const; 204 std::optional<JSBreakpoint> FindBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const; 205 std::optional<JSBreakpoint> FindSmartBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const; 206 bool RemoveBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset); 207 bool RemoveSmartBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset); 208 void HandleExceptionThrowEvent(const JSThread *thread, JSHandle<Method> method, uint32_t bcOffset); 209 bool HandleStep(JSHandle<Method> method, uint32_t bcOffset); 210 bool HandleNativeOut(); 211 bool HandleBreakpoint(JSHandle<Method> method, uint32_t bcOffset); 212 void DumpBreakpoints(); 213 bool IsBreakpointCondSatisfied(std::optional<JSBreakpoint> breakpoint) const; 214 void HandleSymbolicBreakpoint(const JSHandle<Method> &method); 215 216 const EcmaVM *ecmaVm_; 217 PtHooks *hooks_ {nullptr}; 218 NotificationManager *notificationMgr_ {nullptr}; 219 bool singleStepOnDebuggerStmt_ {false}; 220 221 CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpoints_ {}; 222 CUnorderedSet<JSBreakpoint, HashJSBreakpoint> smartBreakpoints_ {}; 223 std::unordered_set<std::string> symbolicBreakpoints_ {}; 224 225 friend class JsDebuggerFriendTest; 226 }; 227 } // namespace panda::ecmascript::tooling 228 229 #endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H 230