1 /* 2 * Copyright (c) 2021 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_TOOLING_AGENT_DEBUGGER_IMPL_H 17 #define ECMASCRIPT_TOOLING_AGENT_DEBUGGER_IMPL_H 18 19 #include "agent/runtime_impl.h" 20 #include "backend/js_pt_hooks.h" 21 #include "base/pt_params.h" 22 #include "backend/js_single_stepper.h" 23 #include "dispatcher.h" 24 25 #include "ecmascript/debugger/js_debugger_manager.h" 26 #include "ecmascript/debugger/js_pt_method.h" 27 #include "libpandabase/macros.h" 28 29 namespace panda::ecmascript::tooling { 30 namespace test { 31 class TestHooks; 32 } // namespace test 33 34 enum class DebuggerState { DISABLED, ENABLED, PAUSED }; 35 class DebuggerImpl final { 36 public: 37 DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime); 38 ~DebuggerImpl(); 39 40 // event 41 bool NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, 42 std::string_view entryPoint = "func_main_0"); 43 bool NotifySingleStep(const JSPtLocation &location); 44 void NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason); 45 void NotifyHandleProtocolCommand(); 46 void NotifyNativeCalling(const void *nativeAddress); 47 void SetDebuggerState(DebuggerState debuggerState); 48 49 DispatchResponse Enable(const EnableParams ¶ms, UniqueDebuggerId *id); 50 DispatchResponse Disable(); 51 DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, 52 std::unique_ptr<RemoteObject> *result); 53 DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, 54 std::vector<std::unique_ptr<BreakLocation>> *outLocations); 55 DispatchResponse GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source); 56 DispatchResponse Pause(); 57 DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams ¶ms); 58 DispatchResponse Resume(const ResumeParams ¶ms); 59 DispatchResponse SetAsyncCallStackDepth(); 60 DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, std::string *outId, 61 std::vector<std::unique_ptr<Location>> *outLocations); 62 DispatchResponse GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams ¶ms, 63 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations); 64 DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms); 65 DispatchResponse StepInto(const StepIntoParams ¶ms); 66 DispatchResponse StepOut(); 67 DispatchResponse StepOver(const StepOverParams ¶ms); 68 DispatchResponse SetBlackboxPatterns(); 69 DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams ¶ms); 70 DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams ¶ms); 71 DispatchResponse DropFrame(const DropFrameParams ¶ms); 72 73 /** 74 * @brief: match first script and callback 75 * 76 * @return: true means matched and callback execute success 77 */ 78 template<class Callback> MatchScripts(const Callback & cb,const std::string & matchStr,ScriptMatchType type)79 bool MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const 80 { 81 for (const auto &script : scripts_) { 82 std::string value; 83 switch (type) { 84 case ScriptMatchType::URL: { 85 value = script.second->GetUrl(); 86 break; 87 } 88 case ScriptMatchType::FILE_NAME: { 89 value = script.second->GetFileName(); 90 break; 91 } 92 case ScriptMatchType::HASH: { 93 value = script.second->GetHash(); 94 break; 95 } 96 default: { 97 return false; 98 } 99 } 100 if (matchStr == value) { 101 return cb(script.second.get()); 102 } 103 } 104 return false; 105 } 106 bool GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames); 107 108 class DispatcherImpl final : public DispatcherBase { 109 public: DispatcherImpl(ProtocolChannel * channel,std::unique_ptr<DebuggerImpl> debugger)110 DispatcherImpl(ProtocolChannel *channel, std::unique_ptr<DebuggerImpl> debugger) 111 : DispatcherBase(channel), debugger_(std::move(debugger)) {} 112 ~DispatcherImpl() override = default; 113 114 void Dispatch(const DispatchRequest &request) override; 115 void Enable(const DispatchRequest &request); 116 void Disable(const DispatchRequest &request); 117 void EvaluateOnCallFrame(const DispatchRequest &request); 118 void GetPossibleBreakpoints(const DispatchRequest &request); 119 void GetScriptSource(const DispatchRequest &request); 120 void Pause(const DispatchRequest &request); 121 void RemoveBreakpoint(const DispatchRequest &request); 122 void Resume(const DispatchRequest &request); 123 void SetAsyncCallStackDepth(const DispatchRequest &request); 124 void SetBreakpointByUrl(const DispatchRequest &request); 125 void SetPauseOnExceptions(const DispatchRequest &request); 126 void StepInto(const DispatchRequest &request); 127 void StepOut(const DispatchRequest &request); 128 void StepOver(const DispatchRequest &request); 129 void SetMixedDebugEnabled(const DispatchRequest &request); 130 void SetBlackboxPatterns(const DispatchRequest &request); 131 void ReplyNativeCalling(const DispatchRequest &request); 132 void GetPossibleAndSetBreakpointByUrl(const DispatchRequest &request); 133 void DropFrame(const DispatchRequest &request); 134 135 private: 136 NO_COPY_SEMANTIC(DispatcherImpl); 137 NO_MOVE_SEMANTIC(DispatcherImpl); 138 139 using AgentHandler = void (DebuggerImpl::DispatcherImpl::*)(const DispatchRequest &request); 140 std::unique_ptr<DebuggerImpl> debugger_ {}; 141 }; 142 143 private: 144 NO_COPY_SEMANTIC(DebuggerImpl); 145 NO_MOVE_SEMANTIC(DebuggerImpl); 146 147 std::string Trim(const std::string &str); 148 DebugInfoExtractor *GetExtractor(const JSPandaFile *jsPandaFile); 149 DebugInfoExtractor *GetExtractor(const std::string &url); 150 std::optional<std::string> CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, 151 std::unique_ptr<RemoteObject> *result); 152 bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId); 153 void SaveCallFrameHandler(const FrameHandler *frameHandler); 154 std::unique_ptr<Scope> GetLocalScopeChain(const FrameHandler *frameHandler, 155 std::unique_ptr<RemoteObject> *thisObj); 156 std::unique_ptr<Scope> GetModuleScopeChain(); 157 std::unique_ptr<Scope> GetGlobalScopeChain(); 158 void GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId, 159 const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj); 160 void GetClosureVariables(const FrameHandler *frameHandler, Local<JSValueRef> &thisVal, 161 Local<ObjectRef> &localObj); 162 void CleanUpOnPaused(); 163 void UpdateScopeObject(const FrameHandler *frameHandler, std::string_view varName, Local<JSValueRef> newVal); 164 void ClearSingleStepper(); 165 Local<JSValueRef> ConvertToLocal(const std::string &varValue); 166 bool DecodeAndCheckBase64(const std::string &src, std::vector<uint8_t> &dest); 167 bool IsSkipLine(const JSPtLocation &location); 168 bool CheckPauseOnException(); 169 bool IsWithinVariableScope(const LocalVariableInfo &localVariableInfo, uint32_t bcOffset); 170 bool ProcessSingleBreakpoint(const BreakpointInfo &breakpoint, 171 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations); 172 GetRecordName(const std::string & url)173 const std::string &GetRecordName(const std::string &url) 174 { 175 static const std::string recordName = ""; 176 auto iter = recordNames_.find(url); 177 if (iter != recordNames_.end()) { 178 return iter->second; 179 } 180 return recordName; 181 } 182 183 class Frontend { 184 public: Frontend(ProtocolChannel * channel)185 explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} 186 ~Frontend() = default; 187 188 void BreakpointResolved(const EcmaVM *vm); 189 void Paused(const EcmaVM *vm, const tooling::Paused &paused); 190 void Resumed(const EcmaVM *vm); 191 void NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling); 192 void ScriptFailedToParse(const EcmaVM *vm); 193 void ScriptParsed(const EcmaVM *vm, const PtScript &script); 194 void WaitForDebugger(const EcmaVM *vm); 195 void RunIfWaitingForDebugger(const EcmaVM *vm); 196 197 private: 198 bool AllowNotify(const EcmaVM *vm) const; 199 200 ProtocolChannel *channel_ {nullptr}; 201 }; 202 203 const EcmaVM *vm_ {nullptr}; 204 Frontend frontend_; 205 206 RuntimeImpl *runtime_ {nullptr}; 207 std::unique_ptr<JSPtHooks> hooks_ {nullptr}; 208 JSDebugger *jsDebugger_ {nullptr}; 209 210 std::unordered_map<std::string, std::string> recordNames_ {}; 211 std::unordered_map<ScriptId, std::unique_ptr<PtScript>> scripts_ {}; 212 PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE}; 213 DebuggerState debuggerState_ {DebuggerState::ENABLED}; 214 bool pauseOnNextByteCode_ {false}; 215 std::unique_ptr<SingleStepper> singleStepper_ {nullptr}; 216 std::vector<void *> nativePointer_; 217 218 std::unordered_map<JSTaggedType *, RemoteObjectId> scopeObjects_ {}; 219 std::vector<std::shared_ptr<FrameHandler>> callFrameHandlers_; 220 JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr}; 221 JsDebuggerManager::SingleStepperFunc stepperFunc_ {nullptr}; 222 223 friend class JSPtHooks; 224 friend class test::TestHooks; 225 }; 226 } // namespace panda::ecmascript::tooling 227 #endif