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