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