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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 m_maxAsyncCallStackDepth(0), | 61 m_maxAsyncCallStackDepth(0), |
62 m_pauseOnExceptionsState(v8::debug::NoBreakOnException), | 62 m_pauseOnExceptionsState(v8::debug::NoBreakOnException), |
63 m_wasmTranslation(isolate) {} | 63 m_wasmTranslation(isolate) {} |
64 | 64 |
65 V8Debugger::~V8Debugger() {} | 65 V8Debugger::~V8Debugger() {} |
66 | 66 |
67 void V8Debugger::enable() { | 67 void V8Debugger::enable() { |
68 if (m_enableCount++) return; | 68 if (m_enableCount++) return; |
69 DCHECK(!enabled()); | 69 DCHECK(!enabled()); |
70 v8::HandleScope scope(m_isolate); | 70 v8::HandleScope scope(m_isolate); |
71 v8::debug::SetDebugEventListener(m_isolate, &V8Debugger::v8DebugEventCallback, | 71 v8::debug::SetDebugEventListener(m_isolate, this); |
72 v8::External::New(m_isolate, this)); | |
73 v8::debug::SetAsyncTaskListener(m_isolate, &V8Debugger::v8AsyncTaskListener, | |
74 this); | |
75 v8::debug::SetCompileEventListener(m_isolate, | |
76 &V8Debugger::v8CompileEventListener, this); | |
77 m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate)); | 72 m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate)); |
78 v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException); | 73 v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException); |
79 m_pauseOnExceptionsState = v8::debug::NoBreakOnException; | 74 m_pauseOnExceptionsState = v8::debug::NoBreakOnException; |
80 compileDebuggerScript(); | 75 compileDebuggerScript(); |
81 } | 76 } |
82 | 77 |
83 void V8Debugger::disable() { | 78 void V8Debugger::disable() { |
84 if (--m_enableCount) return; | 79 if (--m_enableCount) return; |
85 DCHECK(enabled()); | 80 DCHECK(enabled()); |
86 clearBreakpoints(); | 81 clearBreakpoints(); |
87 m_debuggerScript.Reset(); | 82 m_debuggerScript.Reset(); |
88 m_debuggerContext.Reset(); | 83 m_debuggerContext.Reset(); |
89 allAsyncTasksCanceled(); | 84 allAsyncTasksCanceled(); |
90 m_wasmTranslation.Clear(); | 85 m_wasmTranslation.Clear(); |
91 v8::debug::SetDebugEventListener(m_isolate, nullptr); | 86 v8::debug::SetDebugEventListener(m_isolate, nullptr); |
92 v8::debug::SetAsyncTaskListener(m_isolate, nullptr, nullptr); | |
93 v8::debug::SetCompileEventListener(m_isolate, nullptr, nullptr); | |
94 } | 87 } |
95 | 88 |
96 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); } | 89 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); } |
97 | 90 |
98 void V8Debugger::getCompiledScripts( | 91 void V8Debugger::getCompiledScripts( |
99 int contextGroupId, | 92 int contextGroupId, |
100 std::vector<std::unique_ptr<V8DebuggerScript>>& result) { | 93 std::vector<std::unique_ptr<V8DebuggerScript>>& result) { |
101 v8::HandleScope scope(m_isolate); | 94 v8::HandleScope scope(m_isolate); |
102 v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate); | 95 v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate); |
103 v8::debug::GetLoadedScripts(m_isolate, scripts); | 96 v8::debug::GetLoadedScripts(m_isolate, scripts); |
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 | 498 |
506 if (result == V8DebuggerAgentImpl::RequestStepFrame) { | 499 if (result == V8DebuggerAgentImpl::RequestStepFrame) { |
507 v8::debug::PrepareStep(m_isolate, v8::debug::StepFrame); | 500 v8::debug::PrepareStep(m_isolate, v8::debug::StepFrame); |
508 } else if (result == V8DebuggerAgentImpl::RequestStepInto) { | 501 } else if (result == V8DebuggerAgentImpl::RequestStepInto) { |
509 v8::debug::PrepareStep(m_isolate, v8::debug::StepIn); | 502 v8::debug::PrepareStep(m_isolate, v8::debug::StepIn); |
510 } else if (result == V8DebuggerAgentImpl::RequestStepOut) { | 503 } else if (result == V8DebuggerAgentImpl::RequestStepOut) { |
511 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); | 504 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); |
512 } | 505 } |
513 } | 506 } |
514 | 507 |
515 void V8Debugger::v8DebugEventCallback( | 508 void V8Debugger::OnCompileEvent(v8::Local<v8::debug::Script> script, |
516 const v8::debug::EventDetails& eventDetails) { | 509 bool has_compile_error) { |
517 V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData()); | |
518 thisPtr->handleV8DebugEvent(eventDetails); | |
519 } | |
520 | |
521 v8::Local<v8::Value> V8Debugger::callInternalGetterFunction( | |
522 v8::Local<v8::Object> object, const char* functionName) { | |
523 v8::MicrotasksScope microtasks(m_isolate, | |
524 v8::MicrotasksScope::kDoNotRunMicrotasks); | |
525 v8::Local<v8::Value> getterValue = | |
526 object | |
527 ->Get(m_isolate->GetCurrentContext(), | |
528 toV8StringInternalized(m_isolate, functionName)) | |
529 .ToLocalChecked(); | |
530 DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction()); | |
531 return v8::Local<v8::Function>::Cast(getterValue) | |
532 ->Call(m_isolate->GetCurrentContext(), object, 0, nullptr) | |
533 .ToLocalChecked(); | |
534 } | |
535 | |
536 void V8Debugger::handleV8DebugEvent( | |
537 const v8::debug::EventDetails& eventDetails) { | |
538 if (!enabled()) return; | |
539 v8::HandleScope scope(m_isolate); | |
540 | |
541 v8::DebugEvent event = eventDetails.GetEvent(); | |
542 if (event != v8::Break && event != v8::Exception) return; | |
543 | |
544 v8::Local<v8::Context> eventContext = eventDetails.GetEventContext(); | |
545 DCHECK(!eventContext.IsEmpty()); | |
546 V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup( | |
547 m_inspector->contextGroupId(eventContext)); | |
548 if (!agent) return; | |
549 | |
550 if (event == v8::Exception) { | |
551 v8::Local<v8::Context> context = debuggerContext(); | |
552 v8::Local<v8::Object> eventData = eventDetails.GetEventData(); | |
553 v8::Local<v8::Value> exception = | |
554 callInternalGetterFunction(eventData, "exception"); | |
555 v8::Local<v8::Value> promise = | |
556 callInternalGetterFunction(eventData, "promise"); | |
557 bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject(); | |
558 v8::Local<v8::Value> uncaught = | |
559 callInternalGetterFunction(eventData, "uncaught"); | |
560 bool isUncaught = uncaught->BooleanValue(context).FromJust(); | |
561 handleProgramBreak(eventContext, eventDetails.GetExecutionState(), | |
562 exception, v8::Local<v8::Array>(), isPromiseRejection, | |
563 isUncaught); | |
564 } else if (event == v8::Break) { | |
565 v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()}; | |
566 v8::Local<v8::Value> hitBreakpoints; | |
567 if (!callDebuggerMethod("getBreakpointNumbers", 1, argv) | |
568 .ToLocal(&hitBreakpoints)) | |
569 return; | |
570 DCHECK(hitBreakpoints->IsArray()); | |
571 handleProgramBreak(eventContext, eventDetails.GetExecutionState(), | |
572 v8::Local<v8::Value>(), hitBreakpoints.As<v8::Array>()); | |
573 } | |
574 } | |
575 | |
576 void V8Debugger::v8CompileEventListener(v8::Local<v8::debug::Script> script, | |
577 bool has_compile_error, void* data) { | |
578 V8Debugger* debugger = static_cast<V8Debugger*>(data); | |
579 v8::Local<v8::Value> contextData; | 510 v8::Local<v8::Value> contextData; |
580 if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) { | 511 if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) { |
581 return; | 512 return; |
582 } | 513 } |
583 int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); | 514 int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); |
584 V8DebuggerAgentImpl* agent = | 515 V8DebuggerAgentImpl* agent = |
585 debugger->m_inspector->enabledDebuggerAgentForGroup(contextId); | 516 m_inspector->enabledDebuggerAgentForGroup(contextId); |
586 if (!agent) return; | 517 if (!agent) return; |
587 if (script->IsWasm()) { | 518 if (script->IsWasm()) { |
588 debugger->m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), | 519 m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent); |
589 agent); | 520 } else if (m_ignoreScriptParsedEventsCounter == 0) { |
590 } else if (debugger->m_ignoreScriptParsedEventsCounter == 0) { | |
591 agent->didParseSource( | 521 agent->didParseSource( |
592 V8DebuggerScript::Create(debugger->m_isolate, script, inLiveEditScope), | 522 V8DebuggerScript::Create(m_isolate, script, inLiveEditScope), |
593 !has_compile_error); | 523 !has_compile_error); |
594 } | 524 } |
595 } | 525 } |
596 | 526 |
597 void V8Debugger::v8AsyncTaskListener(v8::debug::PromiseDebugActionType type, | 527 void V8Debugger::OnBreakEvent(v8::Local<v8::Context> pausedContext, |
598 int id, void* data) { | 528 v8::Local<v8::Object> execState, |
599 V8Debugger* debugger = static_cast<V8Debugger*>(data); | 529 v8::Local<v8::Value> breakPointsHit) { |
600 if (!debugger->m_maxAsyncCallStackDepth) return; | 530 v8::Local<v8::Value> argv[] = {breakPointsHit}; |
| 531 v8::Local<v8::Value> hitBreakpoints; |
| 532 if (!callDebuggerMethod("getBreakpointNumbers", 1, argv) |
| 533 .ToLocal(&hitBreakpoints)) { |
| 534 return; |
| 535 } |
| 536 DCHECK(hitBreakpoints->IsArray()); |
| 537 handleProgramBreak(pausedContext, execState, v8::Local<v8::Value>(), |
| 538 hitBreakpoints.As<v8::Array>()); |
| 539 } |
| 540 |
| 541 void V8Debugger::OnExceptionEvent(v8::Local<v8::Context> pausedContext, |
| 542 v8::Local<v8::Object> execState, |
| 543 v8::Local<v8::Value> exception, |
| 544 bool isPromiseRejection, bool isUncaught) { |
| 545 handleProgramBreak(pausedContext, execState, exception, |
| 546 v8::Local<v8::Array>(), isPromiseRejection, isUncaught); |
| 547 } |
| 548 |
| 549 void V8Debugger::OnAsyncTaskEvent(v8::debug::PromiseDebugActionType type, |
| 550 int id) { |
| 551 if (!m_maxAsyncCallStackDepth) return; |
601 // Async task events from Promises are given misaligned pointers to prevent | 552 // Async task events from Promises are given misaligned pointers to prevent |
602 // from overlapping with other Blink task identifiers. There is a single | 553 // from overlapping with other Blink task identifiers. There is a single |
603 // namespace of such ids, managed by src/js/promise.js. | 554 // namespace of such ids, managed by src/js/promise.js. |
604 void* ptr = reinterpret_cast<void*>(id * 2 + 1); | 555 void* ptr = reinterpret_cast<void*>(id * 2 + 1); |
605 switch (type) { | 556 switch (type) { |
606 case v8::debug::kDebugEnqueueAsyncFunction: | 557 case v8::debug::kDebugEnqueueAsyncFunction: |
607 debugger->asyncTaskScheduled("async function", ptr, true); | 558 asyncTaskScheduled("async function", ptr, true); |
608 break; | 559 break; |
609 case v8::debug::kDebugEnqueuePromiseResolve: | 560 case v8::debug::kDebugEnqueuePromiseResolve: |
610 debugger->asyncTaskScheduled("Promise.resolve", ptr, true); | 561 asyncTaskScheduled("Promise.resolve", ptr, true); |
611 break; | 562 break; |
612 case v8::debug::kDebugEnqueuePromiseReject: | 563 case v8::debug::kDebugEnqueuePromiseReject: |
613 debugger->asyncTaskScheduled("Promise.reject", ptr, true); | 564 asyncTaskScheduled("Promise.reject", ptr, true); |
614 break; | 565 break; |
615 case v8::debug::kDebugEnqueuePromiseResolveThenableJob: | 566 case v8::debug::kDebugEnqueuePromiseResolveThenableJob: |
616 debugger->asyncTaskScheduled("PromiseResolveThenableJob", ptr, true); | 567 asyncTaskScheduled("PromiseResolveThenableJob", ptr, true); |
617 break; | 568 break; |
618 case v8::debug::kDebugPromiseCollected: | 569 case v8::debug::kDebugPromiseCollected: |
619 debugger->asyncTaskCanceled(ptr); | 570 asyncTaskCanceled(ptr); |
620 break; | 571 break; |
621 case v8::debug::kDebugWillHandle: | 572 case v8::debug::kDebugWillHandle: |
622 debugger->asyncTaskStarted(ptr); | 573 asyncTaskStarted(ptr); |
623 break; | 574 break; |
624 case v8::debug::kDebugDidHandle: | 575 case v8::debug::kDebugDidHandle: |
625 debugger->asyncTaskFinished(ptr); | 576 asyncTaskFinished(ptr); |
626 break; | 577 break; |
627 } | 578 } |
628 } | 579 } |
629 | 580 |
630 V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { | 581 V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { |
631 if (!m_currentStacks.size()) return nullptr; | 582 if (!m_currentStacks.size()) return nullptr; |
632 return m_currentStacks.back().get(); | 583 return m_currentStacks.back().get(); |
633 } | 584 } |
634 | 585 |
635 void V8Debugger::compileDebuggerScript() { | 586 void V8Debugger::compileDebuggerScript() { |
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
995 | 946 |
996 size_t stackSize = | 947 size_t stackSize = |
997 fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1; | 948 fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1; |
998 if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) | 949 if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) |
999 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; | 950 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; |
1000 | 951 |
1001 return V8StackTraceImpl::capture(this, contextGroupId, stackSize); | 952 return V8StackTraceImpl::capture(this, contextGroupId, stackSize); |
1002 } | 953 } |
1003 | 954 |
1004 } // namespace v8_inspector | 955 } // namespace v8_inspector |
OLD | NEW |