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