• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_INSPECTOR_V8_DEBUGGER_H_
6 #define V8_INSPECTOR_V8_DEBUGGER_H_
7 
8 #include <list>
9 #include <memory>
10 #include <unordered_map>
11 #include <unordered_set>
12 #include <vector>
13 
14 #include "include/v8-inspector.h"
15 #include "src/base/macros.h"
16 #include "src/inspector/inspected-context.h"
17 #include "src/inspector/protocol/Debugger.h"
18 #include "src/inspector/protocol/Forward.h"
19 #include "src/inspector/protocol/Runtime.h"
20 #include "src/inspector/v8-debugger-id.h"
21 #include "src/inspector/v8-debugger-script.h"
22 
23 namespace v8_inspector {
24 
25 class AsyncStackTrace;
26 class StackFrame;
27 class V8Debugger;
28 class V8DebuggerAgentImpl;
29 class V8InspectorImpl;
30 class V8RuntimeAgentImpl;
31 class V8StackTraceImpl;
32 struct V8StackTraceId;
33 
34 enum class WrapMode {
35   kForceValue,
36   kNoPreview,
37   kWithPreview,
38   kGenerateWebDriverValue
39 };
40 
41 using protocol::Response;
42 using TerminateExecutionCallback =
43     protocol::Runtime::Backend::TerminateExecutionCallback;
44 
45 class V8Debugger : public v8::debug::DebugDelegate,
46                    public v8::debug::AsyncEventDelegate {
47  public:
48   V8Debugger(v8::Isolate*, V8InspectorImpl*);
49   ~V8Debugger() override;
50   V8Debugger(const V8Debugger&) = delete;
51   V8Debugger& operator=(const V8Debugger&) = delete;
52 
53   bool enabled() const;
isolate()54   v8::Isolate* isolate() const { return m_isolate; }
55 
56   void setBreakpointsActive(bool);
57 
58   v8::debug::ExceptionBreakState getPauseOnExceptionsState();
59   void setPauseOnExceptionsState(v8::debug::ExceptionBreakState);
60   bool canBreakProgram();
61   void breakProgram(int targetContextGroupId);
62   void interruptAndBreak(int targetContextGroupId);
63   void continueProgram(int targetContextGroupId,
64                        bool terminateOnResume = false);
65   void breakProgramOnAssert(int targetContextGroupId);
66 
67   void setPauseOnNextCall(bool, int targetContextGroupId);
68   void stepIntoStatement(int targetContextGroupId, bool breakOnAsyncCall);
69   void stepOverStatement(int targetContextGroupId);
70   void stepOutOfFunction(int targetContextGroupId);
71 
72   void terminateExecution(std::unique_ptr<TerminateExecutionCallback> callback);
73 
74   Response continueToLocation(int targetContextGroupId,
75                               V8DebuggerScript* script,
76                               std::unique_ptr<protocol::Debugger::Location>,
77                               const String16& targetCallFramess);
78 
79   // Each script inherits debug data from v8::Context where it has been
80   // compiled.
81   // Only scripts whose debug data matches |contextGroupId| will be reported.
82   // Passing 0 will result in reporting all scripts.
83   std::vector<std::unique_ptr<V8DebuggerScript>> getCompiledScripts(
84       int contextGroupId, V8DebuggerAgentImpl* agent);
85   void enable();
86   void disable();
87 
isPaused()88   bool isPaused() const { return m_pausedContextGroupId; }
89   bool isPausedInContextGroup(int contextGroupId) const;
90 
maxAsyncCallChainDepth()91   int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
92   void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
93 
maxCallStackSizeToCapture()94   int maxCallStackSizeToCapture() const { return m_maxCallStackSizeToCapture; }
95   void setMaxCallStackSizeToCapture(V8RuntimeAgentImpl*, int);
96 
97   std::shared_ptr<AsyncStackTrace> currentAsyncParent();
98   V8StackTraceId currentExternalParent();
99 
100   std::shared_ptr<StackFrame> symbolize(v8::Local<v8::StackFrame> v8Frame);
101 
102   std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>);
103   std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack);
104 
105   v8::MaybeLocal<v8::Array> internalProperties(v8::Local<v8::Context>,
106                                                v8::Local<v8::Value>);
107 
108   v8::Local<v8::Array> queryObjects(v8::Local<v8::Context> context,
109                                     v8::Local<v8::Object> prototype);
110 
111   void asyncTaskScheduled(const StringView& taskName, void* task,
112                           bool recurring);
113   void asyncTaskCanceled(void* task);
114   void asyncTaskStarted(void* task);
115   void asyncTaskFinished(void* task);
116   void allAsyncTasksCanceled();
117 
118   V8StackTraceId storeCurrentStackTrace(const StringView& description);
119   void externalAsyncTaskStarted(const V8StackTraceId& parent);
120   void externalAsyncTaskFinished(const V8StackTraceId& parent);
121 
122   uintptr_t storeStackTrace(std::shared_ptr<AsyncStackTrace> stack);
123 
124   void muteScriptParsedEvents();
125   void unmuteScriptParsedEvents();
126 
inspector()127   V8InspectorImpl* inspector() { return m_inspector; }
128 
129   void setMaxAsyncTaskStacksForTest(int limit);
130   void dumpAsyncTaskStacksStateForTest();
131 
132   internal::V8DebuggerId debuggerIdFor(int contextGroupId);
133   std::shared_ptr<AsyncStackTrace> stackTraceFor(int contextGroupId,
134                                                  const V8StackTraceId& id);
135 
136   void reportTermination();
137 
138  private:
139   bool addInternalObject(v8::Local<v8::Context> context,
140                          v8::Local<v8::Object> object,
141                          V8InternalValueType type);
142 
143   void clearContinueToLocation();
144   bool shouldContinueToCurrentLocation();
145 
146   static size_t nearHeapLimitCallback(void* data, size_t current_heap_limit,
147                                       size_t initial_heap_limit);
148   static void terminateExecutionCompletedCallback(v8::Isolate* isolate);
149   static void terminateExecutionCompletedCallbackIgnoringData(
150       v8::Isolate* isolate, void*);
151   void handleProgramBreak(
152       v8::Local<v8::Context> pausedContext, v8::Local<v8::Value> exception,
153       const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
154       v8::debug::BreakReasons break_reasons,
155       v8::debug::ExceptionType exception_type = v8::debug::kException,
156       bool isUncaught = false);
157 
158   enum ScopeTargetKind {
159     FUNCTION,
160     GENERATOR,
161   };
162   v8::MaybeLocal<v8::Value> getTargetScopes(v8::Local<v8::Context>,
163                                             v8::Local<v8::Value>,
164                                             ScopeTargetKind);
165 
166   v8::MaybeLocal<v8::Value> functionScopes(v8::Local<v8::Context>,
167                                            v8::Local<v8::Function>);
168   v8::MaybeLocal<v8::Value> generatorScopes(v8::Local<v8::Context>,
169                                             v8::Local<v8::Value>);
170   v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
171                                                v8::Local<v8::Value> value);
172 
173   void asyncTaskScheduledForStack(const StringView& taskName, void* task,
174                                   bool recurring, bool skipTopFrame = false);
175   void asyncTaskCanceledForStack(void* task);
176   void asyncTaskStartedForStack(void* task);
177   void asyncTaskFinishedForStack(void* task);
178 
179   void asyncTaskCandidateForStepping(void* task);
180   void asyncTaskStartedForStepping(void* task);
181   void asyncTaskFinishedForStepping(void* task);
182   void asyncTaskCanceledForStepping(void* task);
183 
184   // v8::debug::DebugEventListener implementation.
185   void AsyncEventOccurred(v8::debug::DebugAsyncActionType type, int id,
186                           bool isBlackboxed) override;
187   void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited,
188                       bool has_compile_error) override;
189   void BreakProgramRequested(
190       v8::Local<v8::Context> paused_context,
191       const std::vector<v8::debug::BreakpointId>& break_points_hit,
192       v8::debug::BreakReasons break_reasons) override;
193   void BreakOnInstrumentation(v8::Local<v8::Context> paused_context,
194                               v8::debug::BreakpointId) override;
195   void ExceptionThrown(v8::Local<v8::Context> paused_context,
196                        v8::Local<v8::Value> exception,
197                        v8::Local<v8::Value> promise, bool is_uncaught,
198                        v8::debug::ExceptionType exception_type) override;
199   bool IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
200                             const v8::debug::Location& start,
201                             const v8::debug::Location& end) override;
202 
203   bool ShouldBeSkipped(v8::Local<v8::debug::Script> script, int line,
204                        int column) override;
205 
206   int currentContextGroupId();
207 
208   bool hasScheduledBreakOnNextFunctionCall() const;
209 
210   v8::Isolate* m_isolate;
211   V8InspectorImpl* m_inspector;
212   int m_enableCount;
213 
214   int m_breakpointsActiveCount = 0;
215   int m_ignoreScriptParsedEventsCounter;
216   size_t m_originalHeapLimit = 0;
217   bool m_scheduledOOMBreak = false;
218   int m_targetContextGroupId = 0;
219   int m_pausedContextGroupId = 0;
220   int m_continueToLocationBreakpointId;
221   String16 m_continueToLocationTargetCallFrames;
222   std::unique_ptr<V8StackTraceImpl> m_continueToLocationStack;
223 
224   // We cache symbolized stack frames by (scriptId,lineNumber,columnNumber)
225   // to reduce memory pressure for huge web apps with lots of deep async
226   // stacks.
227   struct CachedStackFrameKey {
228     int scriptId;
229     int lineNumber;
230     int columnNumber;
231 
232     struct Equal {
operatorCachedStackFrameKey::Equal233       bool operator()(CachedStackFrameKey const& a,
234                       CachedStackFrameKey const& b) const {
235         return a.scriptId == b.scriptId && a.lineNumber == b.lineNumber &&
236                a.columnNumber == b.columnNumber;
237       }
238     };
239 
240     struct Hash {
operatorCachedStackFrameKey::Hash241       size_t operator()(CachedStackFrameKey const& key) const {
242         size_t code = 0;
243         code = code * 31 + key.scriptId;
244         code = code * 31 + key.lineNumber;
245         code = code * 31 + key.columnNumber;
246         return code;
247       }
248     };
249   };
250   std::unordered_map<CachedStackFrameKey, std::weak_ptr<StackFrame>,
251                      CachedStackFrameKey::Hash, CachedStackFrameKey::Equal>
252       m_cachedStackFrames;
253 
254   using AsyncTaskToStackTrace =
255       std::unordered_map<void*, std::weak_ptr<AsyncStackTrace>>;
256   AsyncTaskToStackTrace m_asyncTaskStacks;
257   std::unordered_set<void*> m_recurringTasks;
258 
259   size_t m_maxAsyncCallStacks;
260   int m_maxAsyncCallStackDepth;
261   int m_maxCallStackSizeToCapture;
262 
263   std::vector<void*> m_currentTasks;
264   std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent;
265   std::vector<V8StackTraceId> m_currentExternalParent;
266 
267   void collectOldAsyncStacksIfNeeded();
268   // V8Debugger owns all the async stacks, while most of the other references
269   // are weak, which allows to collect some stacks when there are too many.
270   std::list<std::shared_ptr<AsyncStackTrace>> m_allAsyncStacks;
271 
272   std::unordered_map<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
273   std::unordered_map<V8RuntimeAgentImpl*, int> m_maxCallStackSizeToCaptureMap;
274   void* m_taskWithScheduledBreak = nullptr;
275 
276   // If any of the following three is true, we schedule pause on next JS
277   // execution using SetBreakOnNextFunctionCall.
278   bool m_externalAsyncTaskPauseRequested = false;       // External async task.
279   bool m_taskWithScheduledBreakPauseRequested = false;  // Local async task.
280   bool m_pauseOnNextCallRequested = false;  // setPauseOnNextCall API call.
281 
282   v8::debug::ExceptionBreakState m_pauseOnExceptionsState;
283   // Whether we should pause on async call execution (if any) while stepping in.
284   // See Debugger.stepInto for details.
285   bool m_pauseOnAsyncCall = false;
286 
287   using StackTraceIdToStackTrace =
288       std::unordered_map<uintptr_t, std::weak_ptr<AsyncStackTrace>>;
289   StackTraceIdToStackTrace m_storedStackTraces;
290   uintptr_t m_lastStackTraceId = 0;
291 
292   std::unordered_map<int, internal::V8DebuggerId> m_contextGroupIdToDebuggerId;
293 
294   std::unique_ptr<TerminateExecutionCallback> m_terminateExecutionCallback;
295 };
296 
297 }  // namespace v8_inspector
298 
299 #endif  // V8_INSPECTOR_V8_DEBUGGER_H_
300