• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params);
64     DispatchResponse Enable(const EnableParams &params, UniqueDebuggerId *id);
65     DispatchResponse Disable();
66     DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams &params,
67                                          std::unique_ptr<RemoteObject> *result);
68     DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams &params,
69                                             std::vector<std::unique_ptr<BreakLocation>> *outLocations);
70     DispatchResponse GetScriptSource(const GetScriptSourceParams &params, std::string *source);
71     DispatchResponse Pause();
72     DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams &params);
73     DispatchResponse RemoveBreakpointsByUrl(const RemoveBreakpointsByUrlParams &params);
74     DispatchResponse Resume(const ResumeParams &params);
75     DispatchResponse SetAsyncCallStackDepth();
76     DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams &params, std::string *outId,
77                                         std::vector<std::unique_ptr<Location>> *outLocations,
78                                         bool isSmartBreakpoint = false);
79     DispatchResponse SetBreakpointsActive(const SetBreakpointsActiveParams &params);
80     DispatchResponse GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams &params,
81         std::vector<std::shared_ptr<BreakpointReturnInfo>> &outLocations);
82     DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams &params);
83     DispatchResponse SetSkipAllPauses(const SetSkipAllPausesParams &params);
84     DispatchResponse SetNativeRange(const SetNativeRangeParams &params);
85     DispatchResponse ResetSingleStepper(const ResetSingleStepperParams &params);
86     DispatchResponse StepInto(const StepIntoParams &params);
87     DispatchResponse SmartStepInto(const SmartStepIntoParams &params);
88     DispatchResponse StepOut();
89     DispatchResponse StepOver(const StepOverParams &params);
90     DispatchResponse SetBlackboxPatterns();
91     DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams &params);
92     DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams &params);
93     DispatchResponse DropFrame(const DropFrameParams &params);
94     DispatchResponse ClientDisconnect();
95     DispatchResponse CallFunctionOn(
96             const CallFunctionOnParams &params,
97             std::unique_ptr<RemoteObject> *outRemoteObject,
98             std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails);
99     DispatchResponse SaveAllPossibleBreakpoints(const SaveAllPossibleBreakpointsParams &params);
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 &params);
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 &params);
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