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 "tooling/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 enum class DebuggerFeature { LAUNCH_ACCELERATE, UNKNOWN }; 36 class DebuggerImpl final { 37 public: 38 DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime); 39 ~DebuggerImpl(); 40 41 // event 42 bool NotifyScriptParsed(const std::string &fileName, 43 std::string_view entryPoint = "func_main_0"); 44 bool CheckScriptParsed(const std::string &fileName); 45 bool NotifyScriptParsedBySendable(JSHandle<Method> method); 46 bool MatchUrlAndFileName(const std::string &url, const std::string &fileName); 47 bool NotifySingleStep(const JSPtLocation &location); 48 void NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason); 49 void GeneratePausedInfo(PauseReason reason, 50 std::vector<std::string> &hitBreakpoints, 51 const Local<JSValueRef> &exception); 52 bool NotifyNativeOut(); 53 void NotifyHandleProtocolCommand(); 54 void NotifyNativeCalling(const void *nativeAddress); 55 void NotifyNativeReturn(const void *nativeAddress); 56 void NotifyReturnNative(); 57 bool IsUserCode(const void *nativeAddress); 58 void SetDebuggerState(DebuggerState debuggerState); 59 void SetNativeOutPause(bool nativeOutPause); 60 void AddBreakpointDetail(const std::string &url, int32_t lineNumber, 61 std::string *outId, std::vector<std::unique_ptr<Location>> *outLocations); 62 63 DispatchResponse ContinueToLocation(const ContinueToLocationParams ¶ms); 64 DispatchResponse Enable(const EnableParams ¶ms, UniqueDebuggerId *id); 65 DispatchResponse Disable(); 66 DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, 67 std::unique_ptr<RemoteObject> *result); 68 DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, 69 std::vector<std::unique_ptr<BreakLocation>> *outLocations); 70 DispatchResponse GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source); 71 DispatchResponse Pause(); 72 DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams ¶ms); 73 DispatchResponse RemoveBreakpointsByUrl(const RemoveBreakpointsByUrlParams ¶ms); 74 DispatchResponse Resume(const ResumeParams ¶ms); 75 DispatchResponse SetAsyncCallStackDepth(); 76 DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, std::string *outId, 77 std::vector<std::unique_ptr<Location>> *outLocations, 78 bool isSmartBreakpoint = false); 79 DispatchResponse SetBreakpointsActive(const SetBreakpointsActiveParams ¶ms); 80 DispatchResponse GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams ¶ms, 81 std::vector<std::shared_ptr<BreakpointReturnInfo>> &outLocations); 82 DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms); 83 DispatchResponse SetSkipAllPauses(const SetSkipAllPausesParams ¶ms); 84 DispatchResponse SetNativeRange(const SetNativeRangeParams ¶ms); 85 DispatchResponse ResetSingleStepper(const ResetSingleStepperParams ¶ms); 86 DispatchResponse StepInto(const StepIntoParams ¶ms); 87 DispatchResponse SmartStepInto(const SmartStepIntoParams ¶ms); 88 DispatchResponse StepOut(); 89 DispatchResponse StepOver(const StepOverParams ¶ms); 90 DispatchResponse SetBlackboxPatterns(); 91 DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams ¶ms); 92 DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams ¶ms); 93 DispatchResponse DropFrame(const DropFrameParams ¶ms); 94 DispatchResponse ClientDisconnect(); 95 DispatchResponse CallFunctionOn( 96 const CallFunctionOnParams ¶ms, 97 std::unique_ptr<RemoteObject> *outRemoteObject, 98 std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails); 99 DispatchResponse SaveAllPossibleBreakpoints(const SaveAllPossibleBreakpointsParams ¶ms); 100 /** 101 * @brief: match first script and callback 102 * 103 * @return: true means matched and callback execute success 104 */ 105 template<class Callback> MatchScripts(const Callback & cb,const std::string & matchStr,ScriptMatchType type)106 bool MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const 107 { 108 for (const auto &script : scripts_) { 109 std::string value; 110 switch (type) { 111 case ScriptMatchType::URL: { 112 value = script.second->GetUrl(); 113 break; 114 } 115 case ScriptMatchType::FILE_NAME: { 116 value = script.second->GetFileName(); 117 break; 118 } 119 case ScriptMatchType::HASH: { 120 value = script.second->GetHash(); 121 break; 122 } 123 default: { 124 return false; 125 } 126 } 127 if (matchStr == value) { 128 return cb(script.second.get()); 129 } 130 } 131 return false; 132 } 133 MatchAllScripts(const std::string & url)134 std::vector<PtScript *> MatchAllScripts(const std::string &url) const 135 { 136 std::vector<PtScript *> result; 137 for (const auto &script : scripts_) { 138 if (url == script.second->GetUrl()) { 139 result.push_back(script.second.get()); 140 } 141 } 142 return result; 143 } 144 bool GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames, bool getScope); 145 146 class DispatcherImpl final : public DispatcherBase { 147 public: DispatcherImpl(ProtocolChannel * channel,std::unique_ptr<DebuggerImpl> debugger)148 DispatcherImpl(ProtocolChannel *channel, std::unique_ptr<DebuggerImpl> debugger) 149 : DispatcherBase(channel), debugger_(std::move(debugger)) {} 150 ~DispatcherImpl() override = default; 151 152 void ContinueToLocation(const DispatchRequest &request); 153 void Dispatch(const DispatchRequest &request) override; 154 void Enable(const DispatchRequest &request); 155 void Disable(const DispatchRequest &request); 156 void EvaluateOnCallFrame(const DispatchRequest &request); 157 void GetPossibleBreakpoints(const DispatchRequest &request); 158 void GetScriptSource(const DispatchRequest &request); 159 void Pause(const DispatchRequest &request); 160 void RemoveBreakpoint(const DispatchRequest &request); 161 void RemoveBreakpointsByUrl(const DispatchRequest &request); 162 void Resume(const DispatchRequest &request); 163 void SetAsyncCallStackDepth(const DispatchRequest &request); 164 void SetBreakpointByUrl(const DispatchRequest &request); 165 void SetBreakpointsActive(const DispatchRequest &request); 166 void SetPauseOnExceptions(const DispatchRequest &request); 167 void SetSkipAllPauses(const DispatchRequest &request); 168 void SetNativeRange(const DispatchRequest &request); 169 void ResetSingleStepper(const DispatchRequest &request); 170 void StepInto(const DispatchRequest &request); 171 void SmartStepInto(const DispatchRequest &request); 172 void StepOut(const DispatchRequest &request); 173 void StepOver(const DispatchRequest &request); 174 void SetMixedDebugEnabled(const DispatchRequest &request); 175 void SetBlackboxPatterns(const DispatchRequest &request); 176 void ReplyNativeCalling(const DispatchRequest &request); 177 void GetPossibleAndSetBreakpointByUrl(const DispatchRequest &request); 178 void DropFrame(const DispatchRequest &request); 179 void ClientDisconnect(const DispatchRequest &request); 180 void CallFunctionOn(const DispatchRequest &request); 181 void SaveAllPossibleBreakpoints(const DispatchRequest &request); 182 183 enum class Method { 184 CONTINUE_TO_LOCATION, 185 ENABLE, 186 DISABLE, 187 EVALUATE_ON_CALL_FRAME, 188 GET_POSSIBLE_BREAKPOINTS, 189 GET_SCRIPT_SOURCE, 190 PAUSE, 191 REMOVE_BREAKPOINT, 192 REMOVE_BREAKPOINTS_BY_URL, 193 RESUME, 194 SET_ASYNC_CALL_STACK_DEPTH, 195 SET_BREAKPOINT_BY_URL, 196 SET_BREAKPOINTS_ACTIVE, 197 SET_PAUSE_ON_EXCEPTIONS, 198 SET_SKIP_ALL_PAUSES, 199 STEP_INTO, 200 SMART_STEP_INTO, 201 STEP_OUT, 202 STEP_OVER, 203 SET_MIXED_DEBUG_ENABLED, 204 SET_BLACKBOX_PATTERNS, 205 REPLY_NATIVE_CALLING, 206 GET_POSSIBLE_AND_SET_BREAKPOINT_BY_URL, 207 DROP_FRAME, 208 SET_NATIVE_RANGE, 209 RESET_SINGLE_STEPPER, 210 CLIENT_DISCONNECT, 211 CALL_FUNCTION_ON, 212 SAVE_ALL_POSSIBLE_BREAKPOINTS, 213 UNKNOWN 214 }; 215 Method GetMethodEnum(const std::string& method); 216 217 private: 218 NO_COPY_SEMANTIC(DispatcherImpl); 219 NO_MOVE_SEMANTIC(DispatcherImpl); 220 221 std::unique_ptr<DebuggerImpl> debugger_ {}; 222 }; 223 224 private: 225 NO_COPY_SEMANTIC(DebuggerImpl); 226 NO_MOVE_SEMANTIC(DebuggerImpl); 227 228 std::string Trim(const std::string &str); 229 DebugInfoExtractor *GetExtractor(const JSPandaFile *jsPandaFile); 230 std::vector<DebugInfoExtractor *> GetExtractors(const std::string &url); 231 std::optional<std::string> CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, 232 std::unique_ptr<RemoteObject> *result); 233 bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId, bool getScope); 234 void GenerateScopeChains(bool getScope, const FrameHandler *frameHandler, const JSPandaFile *jsPandaFile, 235 std::vector<std::unique_ptr<Scope>> &scopeChain, std::unique_ptr<RemoteObject> &thisObj); 236 void SaveCallFrameHandler(const FrameHandler *frameHandler); 237 std::unique_ptr<Scope> GetLocalScopeChain(const FrameHandler *frameHandler, 238 std::unique_ptr<RemoteObject> *thisObj); 239 std::unique_ptr<Scope> GetModuleScopeChain(const FrameHandler *frameHandler); 240 std::unique_ptr<Scope> GetGlobalScopeChain(const FrameHandler *frameHandler); 241 std::vector<std::unique_ptr<Scope>> GetClosureScopeChains(const FrameHandler *frameHandler, 242 std::unique_ptr<RemoteObject> *thisObj); 243 void GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId, 244 const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj); 245 void CleanUpOnPaused(); 246 void CleanUpRuntimeProperties(); 247 void UpdateScopeObject(const FrameHandler *frameHandler, std::string_view varName, 248 Local<JSValueRef> newVal, const std::string& scope); 249 void ClearSingleStepper(); 250 Local<JSValueRef> ConvertToLocal(const std::string &varValue); 251 bool DecodeAndCheckBase64(const std::string &src, std::vector<uint8_t> &dest); 252 bool IsSkipLine(const JSPtLocation &location); 253 bool CheckPauseOnException(); 254 bool IsWithinVariableScope(const LocalVariableInfo &localVariableInfo, uint32_t bcOffset); 255 bool ProcessSingleBreakpoint(const BreakpointInfo &breakpoint, 256 std::vector<std::shared_ptr<BreakpointReturnInfo>> &outLocations); 257 bool IsVariableSkipped(const std::string &varName); 258 Local<FunctionRef> CheckAndGenerateCondFunc(const std::optional<std::string> &condition); 259 void InitializeExtendedProtocolsList(); 260 bool NeedToSetBreakpointsWhenParsingScript(const std::string &url); 261 std::vector<std::shared_ptr<BreakpointReturnInfo>> SetBreakpointsWhenParsingScript(const std::string &url); 262 void SavePendingBreakpoints(const SaveAllPossibleBreakpointsParams ¶ms); 263 bool InsertIntoPendingBreakpoints(const BreakpointInfo &breakpoint); 264 void SaveParsedScriptsAndUrl(const std::string &fileName, const std::string &url, 265 const std::string &recordName, const std::string &source = ""); 266 void EnableDebuggerFeatures(const EnableParams ¶ms); 267 DebuggerFeature GetDebuggerFeatureEnum(std::string &option); 268 void EnableFeature(DebuggerFeature feature); 269 GetRecordName(const std::string & url)270 const std::unordered_set<std::string> &GetRecordName(const std::string &url) 271 { 272 static const std::unordered_set<std::string> recordName; 273 auto iter = recordNames_.find(url); 274 if (iter != recordNames_.end()) { 275 return iter->second; 276 } 277 return recordName; 278 } EnableLaunchAccelerateMode()279 void EnableLaunchAccelerateMode() 280 { 281 breakOnStartEnable_ = false; 282 } IsLaunchAccelerateMode()283 bool IsLaunchAccelerateMode() const 284 { 285 return !breakOnStartEnable_; 286 } 287 288 class Frontend { 289 public: Frontend(ProtocolChannel * channel)290 explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} 291 ~Frontend() = default; 292 293 void BreakpointResolved(const EcmaVM *vm); 294 void Paused(const EcmaVM *vm, const tooling::Paused &paused); 295 void Resumed(const EcmaVM *vm); 296 void NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling); 297 void MixedStack(const EcmaVM *vm, const tooling::MixedStack &mixedStack); 298 void ScriptFailedToParse(const EcmaVM *vm); 299 void ScriptParsed(const EcmaVM *vm, const PtScript &script); 300 void WaitForDebugger(const EcmaVM *vm); 301 void RunIfWaitingForDebugger(const EcmaVM *vm); 302 303 private: 304 bool AllowNotify(const EcmaVM *vm) const; 305 306 ProtocolChannel *channel_ {nullptr}; 307 }; 308 309 const EcmaVM *vm_ {nullptr}; 310 Frontend frontend_; 311 312 RuntimeImpl *runtime_ {nullptr}; 313 std::unique_ptr<JSPtHooks> hooks_ {nullptr}; 314 JSDebugger *jsDebugger_ {nullptr}; 315 316 std::unordered_map<std::string, std::unordered_set<std::string>> recordNames_ {}; 317 std::unordered_map<std::string, std::unordered_set<std::string>> urlFileNameMap_ {}; 318 std::unordered_map<ScriptId, std::shared_ptr<PtScript>> scripts_ {}; 319 PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE}; 320 DebuggerState debuggerState_ {DebuggerState::ENABLED}; 321 bool pauseOnNextByteCode_ {false}; 322 bool breakpointsState_ {true}; 323 bool skipAllPausess_ {false}; 324 bool mixStackEnabled_ {false}; 325 std::unique_ptr<SingleStepper> singleStepper_ {nullptr}; 326 Location location_ {}; 327 328 std::unique_ptr<SingleStepper> nativeOut_ {nullptr}; 329 std::vector<void *> nativePointer_; 330 331 bool nativeOutPause_ {false}; 332 std::vector<NativeRange> nativeRanges_ {}; 333 std::unordered_map<JSTaggedType *, std::unordered_map<std::string, 334 std::vector<RemoteObjectId>>> scopeObjects_ {}; 335 std::vector<std::shared_ptr<FrameHandler>> callFrameHandlers_; 336 JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr}; 337 JsDebuggerManager::SingleStepperFunc stepperFunc_ {nullptr}; 338 JsDebuggerManager::ReturnNativeFunc returnNative_ {nullptr}; 339 std::vector<std::string> debuggerExtendedProtocols_ {}; 340 // For launch accelerate mode 341 std::unordered_map<std::string, CUnorderedSet<std::shared_ptr<BreakpointInfo>, HashBreakpointInfo>> 342 breakpointPendingMap_ {}; 343 bool breakOnStartEnable_ {true}; 344 345 friend class JSPtHooks; 346 friend class test::TestHooks; 347 friend class DebuggerImplFriendTest; 348 }; 349 } // namespace panda::ecmascript::tooling 350 #endif