OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/inspector/v8-debugger.h" | 5 #include "src/inspector/v8-debugger.h" |
6 | 6 |
7 #include "src/inspector/debugger-script.h" | 7 #include "src/inspector/debugger-script.h" |
8 #include "src/inspector/inspected-context.h" | 8 #include "src/inspector/inspected-context.h" |
9 #include "src/inspector/protocol/Protocol.h" | 9 #include "src/inspector/protocol/Protocol.h" |
10 #include "src/inspector/script-breakpoint.h" | 10 #include "src/inspector/script-breakpoint.h" |
(...skipping 12 matching lines...) Expand all Loading... |
23 | 23 |
24 // Based on DevTools frontend measurement, with asyncCallStackDepth = 4, | 24 // Based on DevTools frontend measurement, with asyncCallStackDepth = 4, |
25 // average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb | 25 // average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb |
26 // for async stacks. | 26 // for async stacks. |
27 static const int kMaxAsyncTaskStacks = 128 * 1024; | 27 static const int kMaxAsyncTaskStacks = 128 * 1024; |
28 | 28 |
29 inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { | 29 inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { |
30 return value ? v8::True(isolate) : v8::False(isolate); | 30 return value ? v8::True(isolate) : v8::False(isolate); |
31 } | 31 } |
32 | 32 |
| 33 V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector, |
| 34 v8::Local<v8::debug::Script> script) { |
| 35 v8::Local<v8::Value> contextData; |
| 36 if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) { |
| 37 return nullptr; |
| 38 } |
| 39 int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); |
| 40 int contextGroupId = inspector->contextGroupId(contextId); |
| 41 if (!contextGroupId) return nullptr; |
| 42 return inspector->enabledDebuggerAgentForGroup(contextGroupId); |
| 43 } |
| 44 |
33 } // namespace | 45 } // namespace |
34 | 46 |
35 static bool inLiveEditScope = false; | 47 static bool inLiveEditScope = false; |
36 | 48 |
37 v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod( | 49 v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod( |
38 const char* functionName, int argc, v8::Local<v8::Value> argv[]) { | 50 const char* functionName, int argc, v8::Local<v8::Value> argv[]) { |
39 v8::MicrotasksScope microtasks(m_isolate, | 51 v8::MicrotasksScope microtasks(m_isolate, |
40 v8::MicrotasksScope::kDoNotRunMicrotasks); | 52 v8::MicrotasksScope::kDoNotRunMicrotasks); |
41 DCHECK(m_isolate->InContext()); | 53 DCHECK(m_isolate->InContext()); |
42 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); | 54 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); |
(...skipping 18 matching lines...) Expand all Loading... |
61 m_maxAsyncCallStackDepth(0), | 73 m_maxAsyncCallStackDepth(0), |
62 m_pauseOnExceptionsState(v8::debug::NoBreakOnException), | 74 m_pauseOnExceptionsState(v8::debug::NoBreakOnException), |
63 m_wasmTranslation(isolate) {} | 75 m_wasmTranslation(isolate) {} |
64 | 76 |
65 V8Debugger::~V8Debugger() {} | 77 V8Debugger::~V8Debugger() {} |
66 | 78 |
67 void V8Debugger::enable() { | 79 void V8Debugger::enable() { |
68 if (m_enableCount++) return; | 80 if (m_enableCount++) return; |
69 DCHECK(!enabled()); | 81 DCHECK(!enabled()); |
70 v8::HandleScope scope(m_isolate); | 82 v8::HandleScope scope(m_isolate); |
71 v8::debug::SetDebugEventListener(m_isolate, this); | 83 v8::debug::SetDebugDelegate(m_isolate, this); |
72 v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback, | 84 v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback, |
73 this); | 85 this); |
74 m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate)); | 86 m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate)); |
75 v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException); | 87 v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException); |
76 m_pauseOnExceptionsState = v8::debug::NoBreakOnException; | 88 m_pauseOnExceptionsState = v8::debug::NoBreakOnException; |
77 compileDebuggerScript(); | 89 compileDebuggerScript(); |
78 } | 90 } |
79 | 91 |
80 void V8Debugger::disable() { | 92 void V8Debugger::disable() { |
81 if (--m_enableCount) return; | 93 if (--m_enableCount) return; |
82 DCHECK(enabled()); | 94 DCHECK(enabled()); |
83 clearBreakpoints(); | 95 clearBreakpoints(); |
84 m_debuggerScript.Reset(); | 96 m_debuggerScript.Reset(); |
85 m_debuggerContext.Reset(); | 97 m_debuggerContext.Reset(); |
86 allAsyncTasksCanceled(); | 98 allAsyncTasksCanceled(); |
87 m_wasmTranslation.Clear(); | 99 m_wasmTranslation.Clear(); |
88 v8::debug::SetDebugEventListener(m_isolate, nullptr); | 100 v8::debug::SetDebugDelegate(m_isolate, nullptr); |
89 v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr); | 101 v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr); |
90 m_isolate->RestoreOriginalHeapLimit(); | 102 m_isolate->RestoreOriginalHeapLimit(); |
91 } | 103 } |
92 | 104 |
93 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); } | 105 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); } |
94 | 106 |
95 void V8Debugger::getCompiledScripts( | 107 void V8Debugger::getCompiledScripts( |
96 int contextGroupId, | 108 int contextGroupId, |
97 std::vector<std::unique_ptr<V8DebuggerScript>>& result) { | 109 std::vector<std::unique_ptr<V8DebuggerScript>>& result) { |
98 v8::HandleScope scope(m_isolate); | 110 v8::HandleScope scope(m_isolate); |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 void V8Debugger::setPauseOnNextStatement(bool pause) { | 248 void V8Debugger::setPauseOnNextStatement(bool pause) { |
237 if (m_runningNestedMessageLoop) return; | 249 if (m_runningNestedMessageLoop) return; |
238 if (pause) | 250 if (pause) |
239 v8::debug::DebugBreak(m_isolate); | 251 v8::debug::DebugBreak(m_isolate); |
240 else | 252 else |
241 v8::debug::CancelDebugBreak(m_isolate); | 253 v8::debug::CancelDebugBreak(m_isolate); |
242 } | 254 } |
243 | 255 |
244 bool V8Debugger::canBreakProgram() { | 256 bool V8Debugger::canBreakProgram() { |
245 if (!m_breakpointsActivated) return false; | 257 if (!m_breakpointsActivated) return false; |
246 return m_isolate->InContext(); | 258 return v8::debug::HasNonBlackboxedFrameOnStack(m_isolate); |
247 } | 259 } |
248 | 260 |
249 void V8Debugger::breakProgram() { | 261 void V8Debugger::breakProgram() { |
250 if (isPaused()) { | 262 if (isPaused()) { |
251 DCHECK(!m_runningNestedMessageLoop); | 263 DCHECK(!m_runningNestedMessageLoop); |
252 v8::Local<v8::Value> exception; | 264 v8::Local<v8::Value> exception; |
253 v8::Local<v8::Array> hitBreakpoints; | 265 v8::Local<v8::Array> hitBreakpoints; |
254 handleProgramBreak(m_pausedContext, m_executionState, exception, | 266 handleProgramBreak(m_pausedContext, m_executionState, exception, |
255 hitBreakpoints); | 267 hitBreakpoints); |
256 return; | 268 return; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 continueProgram(); | 301 continueProgram(); |
290 } | 302 } |
291 | 303 |
292 void V8Debugger::stepOutOfFunction() { | 304 void V8Debugger::stepOutOfFunction() { |
293 DCHECK(isPaused()); | 305 DCHECK(isPaused()); |
294 DCHECK(!m_executionState.IsEmpty()); | 306 DCHECK(!m_executionState.IsEmpty()); |
295 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); | 307 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); |
296 continueProgram(); | 308 continueProgram(); |
297 } | 309 } |
298 | 310 |
299 void V8Debugger::clearStepping() { | |
300 DCHECK(enabled()); | |
301 v8::debug::ClearStepping(m_isolate); | |
302 } | |
303 | |
304 Response V8Debugger::setScriptSource( | 311 Response V8Debugger::setScriptSource( |
305 const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, | 312 const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, |
306 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails, | 313 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails, |
307 JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged, | 314 JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged, |
308 bool* compileError) { | 315 bool* compileError) { |
309 class EnableLiveEditScope { | 316 class EnableLiveEditScope { |
310 public: | 317 public: |
311 explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) { | 318 explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) { |
312 v8::debug::SetLiveEditEnabled(m_isolate, true); | 319 v8::debug::SetLiveEditEnabled(m_isolate, true); |
313 inLiveEditScope = true; | 320 inLiveEditScope = true; |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 v8::Local<v8::Value> hitBreakpointNumber = | 480 v8::Local<v8::Value> hitBreakpointNumber = |
474 hitBreakpointNumbers->Get(debuggerContext(), i).ToLocalChecked(); | 481 hitBreakpointNumbers->Get(debuggerContext(), i).ToLocalChecked(); |
475 DCHECK(hitBreakpointNumber->IsInt32()); | 482 DCHECK(hitBreakpointNumber->IsInt32()); |
476 breakpointIds.push_back(String16::fromInteger( | 483 breakpointIds.push_back(String16::fromInteger( |
477 hitBreakpointNumber->Int32Value(debuggerContext()).FromJust())); | 484 hitBreakpointNumber->Int32Value(debuggerContext()).FromJust())); |
478 } | 485 } |
479 } | 486 } |
480 | 487 |
481 m_pausedContext = pausedContext; | 488 m_pausedContext = pausedContext; |
482 m_executionState = executionState; | 489 m_executionState = executionState; |
483 V8DebuggerAgentImpl::SkipPauseRequest result = | 490 bool shouldPause = |
484 agent->didPause(pausedContext, exception, breakpointIds, | 491 agent->didPause(pausedContext, exception, breakpointIds, |
485 isPromiseRejection, isUncaught, m_scheduledOOMBreak); | 492 isPromiseRejection, isUncaught, m_scheduledOOMBreak); |
486 if (result == V8DebuggerAgentImpl::RequestNoSkip) { | 493 if (shouldPause) { |
487 m_runningNestedMessageLoop = true; | 494 m_runningNestedMessageLoop = true; |
488 int groupId = m_inspector->contextGroupId(pausedContext); | 495 int groupId = m_inspector->contextGroupId(pausedContext); |
489 DCHECK(groupId); | 496 DCHECK(groupId); |
490 v8::Context::Scope scope(pausedContext); | 497 v8::Context::Scope scope(pausedContext); |
491 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); | 498 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); |
492 CHECK(!context.IsEmpty() && | 499 CHECK(!context.IsEmpty() && |
493 context != v8::debug::GetDebugContext(m_isolate)); | 500 context != v8::debug::GetDebugContext(m_isolate)); |
494 m_inspector->client()->runMessageLoopOnPause(groupId); | 501 m_inspector->client()->runMessageLoopOnPause(groupId); |
495 // The agent may have been removed in the nested loop. | 502 // The agent may have been removed in the nested loop. |
496 agent = m_inspector->enabledDebuggerAgentForGroup( | 503 agent = m_inspector->enabledDebuggerAgentForGroup( |
497 m_inspector->contextGroupId(pausedContext)); | 504 m_inspector->contextGroupId(pausedContext)); |
498 if (agent) agent->didContinue(); | 505 if (agent) agent->didContinue(); |
499 m_runningNestedMessageLoop = false; | 506 m_runningNestedMessageLoop = false; |
500 } | 507 } |
501 if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit(); | 508 if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit(); |
502 m_scheduledOOMBreak = false; | 509 m_scheduledOOMBreak = false; |
503 m_pausedContext.Clear(); | 510 m_pausedContext.Clear(); |
504 m_executionState.Clear(); | 511 m_executionState.Clear(); |
505 | |
506 if (result == V8DebuggerAgentImpl::RequestStepFrame) { | |
507 v8::debug::PrepareStep(m_isolate, v8::debug::StepFrame); | |
508 } else if (result == V8DebuggerAgentImpl::RequestStepInto) { | |
509 v8::debug::PrepareStep(m_isolate, v8::debug::StepIn); | |
510 } else if (result == V8DebuggerAgentImpl::RequestStepOut) { | |
511 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); | |
512 } | |
513 } | 512 } |
514 | 513 |
515 void V8Debugger::v8OOMCallback(void* data) { | 514 void V8Debugger::v8OOMCallback(void* data) { |
516 V8Debugger* thisPtr = static_cast<V8Debugger*>(data); | 515 V8Debugger* thisPtr = static_cast<V8Debugger*>(data); |
517 thisPtr->m_isolate->IncreaseHeapLimitForDebugging(); | 516 thisPtr->m_isolate->IncreaseHeapLimitForDebugging(); |
518 thisPtr->m_scheduledOOMBreak = true; | 517 thisPtr->m_scheduledOOMBreak = true; |
519 thisPtr->setPauseOnNextStatement(true); | 518 thisPtr->setPauseOnNextStatement(true); |
520 } | 519 } |
521 | 520 |
522 void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script, | 521 void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script, |
523 bool has_compile_error) { | 522 bool has_compile_error) { |
524 v8::Local<v8::Value> contextData; | 523 V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script); |
525 if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) { | |
526 return; | |
527 } | |
528 int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); | |
529 int contextGroupId = m_inspector->contextGroupId(contextId); | |
530 if (!contextGroupId) return; | |
531 V8DebuggerAgentImpl* agent = | |
532 m_inspector->enabledDebuggerAgentForGroup(contextGroupId); | |
533 if (!agent) return; | 524 if (!agent) return; |
534 if (script->IsWasm()) { | 525 if (script->IsWasm()) { |
535 m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent); | 526 m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent); |
536 } else if (m_ignoreScriptParsedEventsCounter == 0) { | 527 } else if (m_ignoreScriptParsedEventsCounter == 0) { |
537 agent->didParseSource( | 528 agent->didParseSource( |
538 V8DebuggerScript::Create(m_isolate, script, inLiveEditScope), | 529 V8DebuggerScript::Create(m_isolate, script, inLiveEditScope), |
539 !has_compile_error); | 530 !has_compile_error); |
540 } | 531 } |
541 } | 532 } |
542 | 533 |
(...skipping 12 matching lines...) Expand all Loading... |
555 } | 546 } |
556 | 547 |
557 void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext, | 548 void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext, |
558 v8::Local<v8::Object> execState, | 549 v8::Local<v8::Object> execState, |
559 v8::Local<v8::Value> exception, | 550 v8::Local<v8::Value> exception, |
560 bool isPromiseRejection, bool isUncaught) { | 551 bool isPromiseRejection, bool isUncaught) { |
561 handleProgramBreak(pausedContext, execState, exception, | 552 handleProgramBreak(pausedContext, execState, exception, |
562 v8::Local<v8::Array>(), isPromiseRejection, isUncaught); | 553 v8::Local<v8::Array>(), isPromiseRejection, isUncaught); |
563 } | 554 } |
564 | 555 |
| 556 bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, |
| 557 const v8::debug::Location& start, |
| 558 const v8::debug::Location& end) { |
| 559 V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script); |
| 560 if (!agent) return false; |
| 561 return agent->isFunctionBlackboxed(String16::fromInteger(script->Id()), start, |
| 562 end); |
| 563 } |
| 564 |
565 void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, | 565 void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, |
566 int id) { | 566 int id) { |
567 if (!m_maxAsyncCallStackDepth) return; | 567 if (!m_maxAsyncCallStackDepth) return; |
568 // Async task events from Promises are given misaligned pointers to prevent | 568 // Async task events from Promises are given misaligned pointers to prevent |
569 // from overlapping with other Blink task identifiers. There is a single | 569 // from overlapping with other Blink task identifiers. There is a single |
570 // namespace of such ids, managed by src/js/promise.js. | 570 // namespace of such ids, managed by src/js/promise.js. |
571 void* ptr = reinterpret_cast<void*>(id * 2 + 1); | 571 void* ptr = reinterpret_cast<void*>(id * 2 + 1); |
572 switch (type) { | 572 switch (type) { |
573 case v8::debug::kDebugEnqueueAsyncFunction: | 573 case v8::debug::kDebugEnqueueAsyncFunction: |
574 asyncTaskScheduled("async function", ptr, true); | 574 asyncTaskScheduled("async function", ptr, true); |
(...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 | 962 |
963 size_t stackSize = | 963 size_t stackSize = |
964 fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1; | 964 fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1; |
965 if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) | 965 if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) |
966 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; | 966 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; |
967 | 967 |
968 return V8StackTraceImpl::capture(this, contextGroupId, stackSize); | 968 return V8StackTraceImpl::capture(this, contextGroupId, stackSize); |
969 } | 969 } |
970 | 970 |
971 } // namespace v8_inspector | 971 } // namespace v8_inspector |
OLD | NEW |