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