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