[inspector] V8DebuggerAgent cleanup

V8DebuggerAgentImpl::m_skipAllPaused is moved to V8Debugger.
V8DebuggerAgentImpl::didPaused doesn't return shouldBreak flag and called only when break is required and stack trace presented.
V8DebuggerAgentImpl doesn't store paused context.
Logic of conversion step-next at return into step-in is moved to debug.cc.

BUG=none
R=dgozman@chromium.org,yangguo@chromium.org

Review-Url: https://codereview.chromium.org/2668763003
Cr-Commit-Position: refs/heads/master@{#42907}
This commit is contained in:
kozyatinskiy 2017-02-02 23:09:11 -08:00 committed by Commit bot
parent 7c32e07d31
commit 3a4f5fafe0
9 changed files with 186 additions and 65 deletions

View File

@ -1764,6 +1764,7 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
if (!break_on_exception_) return;
}
bool empty_js_stack = false;
{
JavaScriptFrameIterator it(isolate_);
// Check whether the top frame is blackboxed or the break location is muted.
@ -1771,12 +1772,13 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
IsExceptionBlackboxed(uncaught))) {
return;
}
empty_js_stack = it.done();
}
DebugScope debug_scope(this);
if (debug_scope.failed()) return;
if (debug_delegate_) {
if (debug_delegate_ && !empty_js_stack) {
HandleScope scope(isolate_);
// Create the execution state.
@ -1788,8 +1790,8 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
GetDebugEventContext(isolate_),
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(exception), promise->IsJSObject(), uncaught);
if (!non_inspector_listener_exists()) return;
}
if (debug_delegate_ && !non_inspector_listener_exists()) return;
// Create the event data object.
Handle<Object> event_data;

View File

@ -134,16 +134,13 @@ V8DebuggerAgentImpl::V8DebuggerAgentImpl(
m_breakReason(protocol::Debugger::Paused::ReasonEnum::Other),
m_scheduledDebuggerStep(NoStep),
m_javaScriptPauseScheduled(false),
m_recursionLevelForStepOut(0),
m_skipAllPauses(false) {
m_recursionLevelForStepOut(0) {
clearBreakDetails();
}
V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
void V8DebuggerAgentImpl::enableImpl() {
// m_inspector->addListener may result in reporting all parsed scripts to
// the agent so it should already be in enabled state by then.
m_enabled = true;
m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
m_debugger->enable();
@ -179,9 +176,8 @@ Response V8DebuggerAgentImpl::disable() {
v8::debug::NoBreakOnException);
m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
if (!m_pausedContext.IsEmpty()) m_debugger->continueProgram();
if (isPaused()) m_debugger->continueProgram();
m_debugger->disable();
m_pausedContext.Reset();
JavaScriptCallFrames emptyCallFrames;
m_pausedCallFrames.swap(emptyCallFrames);
m_blackboxedPositions.clear();
@ -195,6 +191,7 @@ Response V8DebuggerAgentImpl::disable() {
m_scheduledDebuggerStep = NoStep;
m_javaScriptPauseScheduled = false;
m_skipAllPauses = false;
m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
m_state->remove(DebuggerAgentState::blackboxPattern);
m_enabled = false;
m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
@ -236,8 +233,8 @@ Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
}
Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
m_skipAllPauses = skip;
m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
return Response::OK();
}
@ -548,7 +545,7 @@ Response V8DebuggerAgentImpl::restartFrame(
const String16& callFrameId,
std::unique_ptr<Array<CallFrame>>* newCallFrames,
Maybe<StackTrace>* asyncStackTrace) {
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
callFrameId);
Response response = scope.initialize();
@ -589,7 +586,7 @@ void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data) {
if (!enabled() || m_scheduledDebuggerStep == StepInto ||
m_javaScriptPauseScheduled || m_debugger->isPaused() ||
m_javaScriptPauseScheduled || isPaused() ||
!m_debugger->breakpointsActivated())
return;
m_breakReason = breakReason;
@ -600,22 +597,21 @@ void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
DCHECK(enabled());
if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled ||
m_debugger->isPaused())
isPaused())
return;
clearBreakDetails();
m_debugger->setPauseOnNextStatement(true);
}
void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
if (m_javaScriptPauseScheduled || m_debugger->isPaused()) return;
if (m_javaScriptPauseScheduled || isPaused()) return;
clearBreakDetails();
m_debugger->setPauseOnNextStatement(false);
}
Response V8DebuggerAgentImpl::pause() {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (m_javaScriptPauseScheduled || m_debugger->isPaused())
return Response::OK();
if (m_javaScriptPauseScheduled || isPaused()) return Response::OK();
clearBreakDetails();
m_javaScriptPauseScheduled = true;
m_scheduledDebuggerStep = NoStep;
@ -624,7 +620,7 @@ Response V8DebuggerAgentImpl::pause() {
}
Response V8DebuggerAgentImpl::resume() {
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
m_scheduledDebuggerStep = NoStep;
m_session->releaseObjectGroup(kBacktraceObjectGroup);
m_debugger->continueProgram();
@ -632,7 +628,7 @@ Response V8DebuggerAgentImpl::resume() {
}
Response V8DebuggerAgentImpl::stepOver() {
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
// StepOver at function return point should fallback to StepInto.
JavaScriptCallFrame* frame =
!m_pausedCallFrames.empty() ? m_pausedCallFrames[0].get() : nullptr;
@ -644,7 +640,7 @@ Response V8DebuggerAgentImpl::stepOver() {
}
Response V8DebuggerAgentImpl::stepInto() {
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
m_scheduledDebuggerStep = StepInto;
m_session->releaseObjectGroup(kBacktraceObjectGroup);
m_debugger->stepIntoStatement();
@ -652,7 +648,7 @@ Response V8DebuggerAgentImpl::stepInto() {
}
Response V8DebuggerAgentImpl::stepOut() {
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
m_scheduledDebuggerStep = StepOut;
m_recursionLevelForStepOut = 1;
m_session->releaseObjectGroup(kBacktraceObjectGroup);
@ -690,7 +686,7 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
std::unique_ptr<RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
callFrameId);
Response response = scope.initialize();
@ -720,7 +716,7 @@ Response V8DebuggerAgentImpl::setVariableValue(
std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
const String16& callFrameId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
callFrameId);
Response response = scope.initialize();
@ -829,7 +825,6 @@ Response V8DebuggerAgentImpl::setBlackboxedRanges(
void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
changeJavaScriptRecursionLevel(+1);
// Fast return.
if (m_scheduledDebuggerStep != StepInto) return;
schedulePauseOnNextStatementIfSteppingInto();
}
@ -839,8 +834,7 @@ void V8DebuggerAgentImpl::didExecuteScript() {
}
void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
if (m_javaScriptPauseScheduled && !m_skipAllPauses &&
!m_debugger->isPaused()) {
if (m_javaScriptPauseScheduled && !m_skipAllPauses && !isPaused()) {
// Do not ever loose user's pause request until we have actually paused.
m_debugger->setPauseOnNextStatement(true);
}
@ -858,7 +852,7 @@ void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
Response V8DebuggerAgentImpl::currentCallFrames(
std::unique_ptr<Array<CallFrame>>* result) {
if (m_pausedContext.IsEmpty() || !m_pausedCallFrames.size()) {
if (!isPaused()) {
*result = Array<CallFrame>::create();
return Response::OK();
}
@ -967,12 +961,14 @@ Response V8DebuggerAgentImpl::currentCallFrames(
}
std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
if (m_pausedContext.IsEmpty()) return nullptr;
if (!isPaused()) return nullptr;
V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
: nullptr;
}
bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
void V8DebuggerAgentImpl::didParseSource(
std::unique_ptr<V8DebuggerScript> script, bool success) {
v8::HandleScope handles(m_isolate);
@ -1056,23 +1052,13 @@ void V8DebuggerAgentImpl::didParseSource(
}
}
bool V8DebuggerAgentImpl::didPause(v8::Local<v8::Context> context,
void V8DebuggerAgentImpl::didPause(int contextId,
v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection, bool isUncaught,
bool isOOMBreak) {
if (!isOOMBreak) {
if (m_skipAllPauses) return false;
JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
JavaScriptCallFrame* topCallFrame =
!callFrames.empty() ? callFrames.begin()->get() : nullptr;
// Skip pauses inside V8 internal scripts and on syntax errors.
if (!topCallFrame) return false;
}
DCHECK(m_pausedContext.IsEmpty());
JavaScriptCallFrames frames = m_debugger->currentCallFrames();
m_pausedCallFrames.swap(frames);
m_pausedContext.Reset(m_isolate, context);
v8::HandleScope handles(m_isolate);
if (isOOMBreak) {
@ -1080,8 +1066,7 @@ bool V8DebuggerAgentImpl::didPause(v8::Local<v8::Context> context,
m_breakAuxData = nullptr;
} else if (!exception.IsEmpty()) {
InjectedScript* injectedScript = nullptr;
m_session->findInjectedScript(InspectedContext::contextId(context),
injectedScript);
m_session->findInjectedScript(contextId, injectedScript);
if (injectedScript) {
m_breakReason =
isPromiseRejection
@ -1129,11 +1114,9 @@ bool V8DebuggerAgentImpl::didPause(v8::Local<v8::Context> context,
m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
m_continueToLocationBreakpointId = "";
}
return true;
}
void V8DebuggerAgentImpl::didContinue() {
m_pausedContext.Reset();
JavaScriptCallFrames emptyCallFrames;
m_pausedCallFrames.swap(emptyCallFrames);
clearBreakDetails();
@ -1143,9 +1126,7 @@ void V8DebuggerAgentImpl::didContinue() {
void V8DebuggerAgentImpl::breakProgram(
const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data) {
if (!enabled() || m_skipAllPauses || !m_pausedContext.IsEmpty() ||
!m_debugger->canBreakProgram())
return;
if (!enabled() || !m_debugger->canBreakProgram() || m_skipAllPauses) return;
m_breakReason = breakReason;
m_breakAuxData = std::move(data);
m_scheduledDebuggerStep = NoStep;

View File

@ -127,7 +127,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
void reset();
// Interface for V8InspectorImpl
bool didPause(v8::Local<v8::Context>, v8::Local<v8::Value> exception,
void didPause(int contextId, v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection, bool isUncaught, bool isOOMBreak);
void didContinue();
@ -139,6 +139,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
const v8::debug::Location& start,
const v8::debug::Location& end);
bool skipAllPauses() const { return m_skipAllPauses; }
v8::Isolate* isolate() { return m_isolate; }
private:
@ -165,6 +167,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
Response setBlackboxPattern(const String16& pattern);
void resetBlackboxedStateCache();
bool isPaused() const;
using ScriptsMap =
protocol::HashMap<String16, std::unique_ptr<V8DebuggerScript>>;
using BreakpointIdToDebuggerBreakpointIdsMap =
@ -182,7 +186,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
protocol::DictionaryValue* m_state;
protocol::Debugger::Frontend m_frontend;
v8::Isolate* m_isolate;
v8::Global<v8::Context> m_pausedContext;
JavaScriptCallFrames m_pausedCallFrames;
ScriptsMap m_scripts;
BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds;
@ -194,7 +197,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
bool m_javaScriptPauseScheduled;
int m_recursionLevelForStepOut;
bool m_skipAllPauses;
bool m_skipAllPauses = false;
std::unique_ptr<V8Regex> m_blackboxPattern;
protocol::HashMap<String16, std::vector<std::pair<int, int>>>

View File

@ -253,7 +253,7 @@ void V8Debugger::setPauseOnExceptionsState(
}
void V8Debugger::setPauseOnNextStatement(bool pause) {
if (m_runningNestedMessageLoop) return;
if (isPaused()) return;
if (pause)
v8::debug::DebugBreak(m_isolate);
else
@ -474,11 +474,11 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Array> hitBreakpointNumbers,
bool isPromiseRejection, bool isUncaught) {
// Don't allow nested breaks.
if (m_runningNestedMessageLoop) return;
if (isPaused()) return;
V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup(
m_inspector->contextGroupId(pausedContext));
if (!agent) return;
if (!agent || (agent->skipAllPauses() && !m_scheduledOOMBreak)) return;
std::vector<String16> breakpointIds;
if (!hitBreakpointNumbers.IsEmpty()) {
@ -494,24 +494,23 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
m_pausedContext = pausedContext;
m_executionState = executionState;
bool shouldPause =
agent->didPause(pausedContext, exception, breakpointIds,
isPromiseRejection, isUncaught, m_scheduledOOMBreak);
if (shouldPause) {
m_runningNestedMessageLoop = true;
int groupId = m_inspector->contextGroupId(pausedContext);
DCHECK(groupId);
m_runningNestedMessageLoop = true;
agent->didPause(InspectedContext::contextId(pausedContext), exception,
breakpointIds, isPromiseRejection, isUncaught,
m_scheduledOOMBreak);
int groupId = m_inspector->contextGroupId(pausedContext);
DCHECK(groupId);
{
v8::Context::Scope scope(pausedContext);
v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
CHECK(!context.IsEmpty() &&
context != v8::debug::GetDebugContext(m_isolate));
m_inspector->client()->runMessageLoopOnPause(groupId);
// The agent may have been removed in the nested loop.
agent = m_inspector->enabledDebuggerAgentForGroup(
m_inspector->contextGroupId(pausedContext));
if (agent) agent->didContinue();
m_runningNestedMessageLoop = false;
}
// The agent may have been removed in the nested loop.
agent = m_inspector->enabledDebuggerAgentForGroup(groupId);
if (agent) agent->didContinue();
if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
m_scheduledOOMBreak = false;
m_pausedContext.Clear();
@ -836,8 +835,6 @@ v8::Local<v8::Value> V8Debugger::functionLocation(
return location;
}
bool V8Debugger::isPaused() { return !m_pausedContext.IsEmpty(); }
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
v8::Local<v8::StackTrace> stackTrace) {
int contextGroupId =

View File

@ -65,7 +65,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
void enable();
void disable();
bool isPaused();
bool isPaused() const { return m_runningNestedMessageLoop; }
v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }

View File

@ -0,0 +1,83 @@
Debugger breaks in next script after stepOut from previous one.
Running test: testStepOut
test (foo.js:12:2)
(anonymous) (:0:0)
(anonymous) (:0:5)
(anonymous) (timeout1.js:0:0)
foo (timeout2.js:1:12)
(anonymous) (timeout3.js:0:0)
Running test: testStepOver
(anonymous) (:0:0)
test (foo.js:12:2)
(anonymous) (:0:0)
test (foo.js:13:0)
(anonymous) (:0:0)
(anonymous) (:0:5)
(anonymous) (timeout1.js:0:0)
(anonymous) (timeout1.js:0:8)
(anonymous) (timeout1.js:0:34)
foo (timeout2.js:1:12)
foo (timeout2.js:2:2)
foo (timeout2.js:3:0)
(anonymous) (timeout3.js:0:0)
(anonymous) (timeout3.js:0:8)
(anonymous) (timeout3.js:0:34)
Running test: testStepInto
(anonymous) (:0:0)
test (foo.js:9:2)
(anonymous) (:0:0)
test (foo.js:10:2)
(anonymous) (:0:0)
test (foo.js:11:2)
(anonymous) (:0:0)
test (foo.js:12:2)
(anonymous) (:0:0)
test (foo.js:13:0)
(anonymous) (:0:0)
(anonymous) (:0:5)
(anonymous) (timeout1.js:0:0)
(anonymous) (timeout1.js:0:8)
(anonymous) (timeout1.js:0:34)
foo (timeout2.js:1:12)
foo (timeout2.js:2:2)
foo (timeout2.js:3:0)
(anonymous) (timeout3.js:0:0)
(anonymous) (timeout3.js:0:8)
(anonymous) (timeout3.js:0:34)

View File

@ -0,0 +1,51 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
print('Debugger breaks in next script after stepOut from previous one.');
InspectorTest.addScript(`
function test() {
setTimeout('var a = 1;//# sourceURL=timeout1.js', 0);
setTimeout(foo, 0);
setTimeout('var a = 3;//# sourceURL=timeout3.js', 0);
debugger;
}
//# sourceURL=foo.js`, 7, 26);
InspectorTest.addScript(`
function foo() {
return 42;
}
//# sourceURL=timeout2.js`)
InspectorTest.setupScriptMap();
var stepAction;
Protocol.Debugger.onPaused(message => {
InspectorTest.logCallFrames(message.params.callFrames);
InspectorTest.log('');
Protocol.Debugger[stepAction]();
});
Protocol.Debugger.enable()
InspectorTest.runTestSuite([
function testStepOut(next) {
stepAction = 'stepOut';
Protocol.Runtime.evaluate({ expression: 'test()' })
.then(() => InspectorTest.waitPendingTasks())
.then(next);
},
function testStepOver(next) {
stepAction = 'stepOver';
Protocol.Runtime.evaluate({ expression: 'test()' })
.then(() => InspectorTest.waitPendingTasks())
.then(next);
},
function testStepInto(next) {
stepAction = 'stepInto';
Protocol.Runtime.evaluate({ expression: 'test()' })
.then(() => InspectorTest.waitPendingTasks())
.then(next);
}
]);

View File

@ -223,7 +223,11 @@ void ExecuteStringTask::AsyncRun(v8::Isolate* isolate,
.ToLocal(&script))
return;
v8::MaybeLocal<v8::Value> result;
if (inspector_)
inspector_->willExecuteScript(local_context,
script->GetUnboundScript()->GetId());
result = script->Run(local_context);
if (inspector_) inspector_->didExecuteScript(local_context);
} else {
v8::Local<v8::Module> module;
if (!v8::ScriptCompiler::CompileModule(isolate, &scriptSource)

View File

@ -99,7 +99,7 @@ class AsyncTask : public TaskRunner::Task {
virtual void AsyncRun(v8::Isolate* isolate,
const v8::Global<v8::Context>& context) = 0;
private:
protected:
v8_inspector::V8Inspector* inspector_;
};