Commit f5767bf6 authored by dgozman's avatar dgozman Committed by Commit Bot

[inspector] Make pausing on console.assert work with multiple sessions

Instead of going through debugger agent, this patch implements
console.assert pause similar to debugger statement and OOM break.

New test uncovered a bug, where pause on exceptions state mix up
between different context groups. Added a TODO to fix it.

BUG=chromium:590878

Review-Url: https://codereview.chromium.org/2916363002
Cr-Commit-Position: refs/heads/master@{#45711}
parent b5843923
...@@ -275,14 +275,7 @@ void V8Console::Assert(const v8::debug::ConsoleCallArguments& info) { ...@@ -275,14 +275,7 @@ void V8Console::Assert(const v8::debug::ConsoleCallArguments& info) {
arguments.push_back( arguments.push_back(
toV8String(m_inspector->isolate(), String16("console.assert"))); toV8String(m_inspector->isolate(), String16("console.assert")));
helper.reportCall(ConsoleAPIType::kAssert, arguments); helper.reportCall(ConsoleAPIType::kAssert, arguments);
m_inspector->debugger()->breakProgramOnAssert(helper.groupId());
// TODO(dgozman): only break once, not per each session.
helper.forEachSession([](V8InspectorSessionImpl* session) {
if (session->debuggerAgent()->enabled()) {
session->debuggerAgent()->breakProgramOnException(
protocol::Debugger::Paused::ReasonEnum::Assert, nullptr);
}
});
} }
void V8Console::MarkTimeline(const v8::debug::ConsoleCallArguments& info) { void V8Console::MarkTimeline(const v8::debug::ConsoleCallArguments& info) {
......
...@@ -764,6 +764,8 @@ Response V8DebuggerAgentImpl::setPauseOnExceptions( ...@@ -764,6 +764,8 @@ Response V8DebuggerAgentImpl::setPauseOnExceptions(
} }
void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) { void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
// TODO(dgozman): this changes the global state and forces all context groups
// to pause. We should make this flag be per-context-group.
m_debugger->setPauseOnExceptionsState( m_debugger->setPauseOnExceptionsState(
static_cast<v8::debug::ExceptionBreakState>(pauseState)); static_cast<v8::debug::ExceptionBreakState>(pauseState));
m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState); m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
...@@ -1137,7 +1139,7 @@ void V8DebuggerAgentImpl::didPause(int contextId, ...@@ -1137,7 +1139,7 @@ void V8DebuggerAgentImpl::didPause(int contextId,
v8::Local<v8::Value> exception, v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints, const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection, bool isUncaught, bool isPromiseRejection, bool isUncaught,
bool isOOMBreak) { bool isOOMBreak, bool isAssert) {
JavaScriptCallFrames frames = m_debugger->currentCallFrames(); JavaScriptCallFrames frames = m_debugger->currentCallFrames();
m_pausedCallFrames.swap(frames); m_pausedCallFrames.swap(frames);
v8::HandleScope handles(m_isolate); v8::HandleScope handles(m_isolate);
...@@ -1147,6 +1149,9 @@ void V8DebuggerAgentImpl::didPause(int contextId, ...@@ -1147,6 +1149,9 @@ void V8DebuggerAgentImpl::didPause(int contextId,
if (isOOMBreak) { if (isOOMBreak) {
hitReasons.push_back( hitReasons.push_back(
std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr)); std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
} else if (isAssert) {
hitReasons.push_back(std::make_pair(
protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
} else if (!exception.IsEmpty()) { } else if (!exception.IsEmpty()) {
InjectedScript* injectedScript = nullptr; InjectedScript* injectedScript = nullptr;
m_session->findInjectedScript(contextId, injectedScript); m_session->findInjectedScript(contextId, injectedScript);
...@@ -1254,15 +1259,6 @@ void V8DebuggerAgentImpl::breakProgram( ...@@ -1254,15 +1259,6 @@ void V8DebuggerAgentImpl::breakProgram(
} }
} }
void V8DebuggerAgentImpl::breakProgramOnException(
const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data) {
if (!enabled() ||
m_debugger->getPauseOnExceptionsState() == v8::debug::NoBreakOnException)
return;
breakProgram(breakReason, std::move(data));
}
void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId, void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
int lineNumber, int columnNumber, int lineNumber, int columnNumber,
BreakpointSource source, BreakpointSource source,
......
...@@ -122,15 +122,14 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -122,15 +122,14 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
void cancelPauseOnNextStatement(); void cancelPauseOnNextStatement();
void breakProgram(const String16& breakReason, void breakProgram(const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data); std::unique_ptr<protocol::DictionaryValue> data);
void breakProgramOnException(const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data);
void reset(); void reset();
// Interface for V8InspectorImpl // Interface for V8InspectorImpl
void didPause(int contextId, v8::Local<v8::Value> exception, void didPause(int contextId, v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints, const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection, bool isUncaught, bool isOOMBreak); bool isPromiseRejection, bool isUncaught, bool isOOMBreak,
bool isAssert);
void didContinue(); void didContinue();
void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success); void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success);
......
...@@ -363,6 +363,18 @@ void V8Debugger::continueProgram(int targetContextGroupId) { ...@@ -363,6 +363,18 @@ void V8Debugger::continueProgram(int targetContextGroupId) {
m_executionState.Clear(); m_executionState.Clear();
} }
void V8Debugger::breakProgramOnAssert(int targetContextGroupId) {
if (!enabled()) return;
if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException) return;
// Don't allow nested breaks.
if (isPaused()) return;
if (!canBreakProgram()) return;
DCHECK(targetContextGroupId);
m_targetContextGroupId = targetContextGroupId;
m_scheduledAssertBreak = true;
v8::debug::BreakRightNow(m_isolate);
}
void V8Debugger::stepIntoStatement(int targetContextGroupId) { void V8Debugger::stepIntoStatement(int targetContextGroupId) {
DCHECK(isPaused()); DCHECK(isPaused());
DCHECK(!m_executionState.IsEmpty()); DCHECK(!m_executionState.IsEmpty());
...@@ -599,6 +611,7 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, ...@@ -599,6 +611,7 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
m_breakRequested = false; m_breakRequested = false;
bool scheduledOOMBreak = m_scheduledOOMBreak; bool scheduledOOMBreak = m_scheduledOOMBreak;
bool scheduledAssertBreak = m_scheduledAssertBreak;
auto agentCheck = [&scheduledOOMBreak](V8DebuggerAgentImpl* agent) { auto agentCheck = [&scheduledOOMBreak](V8DebuggerAgentImpl* agent) {
return agent->enabled() && (scheduledOOMBreak || !agent->skipAllPauses()); return agent->enabled() && (scheduledOOMBreak || !agent->skipAllPauses());
}; };
...@@ -636,12 +649,13 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, ...@@ -636,12 +649,13 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
m_inspector->forEachSession( m_inspector->forEachSession(
contextGroupId, [&agentCheck, &pausedContext, &exception, &breakpointIds, contextGroupId, [&agentCheck, &pausedContext, &exception, &breakpointIds,
&isPromiseRejection, &isUncaught, &isPromiseRejection, &isUncaught, &scheduledOOMBreak,
&scheduledOOMBreak](V8InspectorSessionImpl* session) { &scheduledAssertBreak](V8InspectorSessionImpl* session) {
if (agentCheck(session->debuggerAgent())) { if (agentCheck(session->debuggerAgent())) {
session->debuggerAgent()->didPause( session->debuggerAgent()->didPause(
InspectedContext::contextId(pausedContext), exception, InspectedContext::contextId(pausedContext), exception,
breakpointIds, isPromiseRejection, isUncaught, scheduledOOMBreak); breakpointIds, isPromiseRejection, isUncaught, scheduledOOMBreak,
scheduledAssertBreak);
} }
}); });
{ {
...@@ -660,6 +674,7 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, ...@@ -660,6 +674,7 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit(); if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
m_scheduledOOMBreak = false; m_scheduledOOMBreak = false;
m_scheduledAssertBreak = false;
m_pausedContext.Clear(); m_pausedContext.Clear();
m_executionState.Clear(); m_executionState.Clear();
} }
......
...@@ -52,6 +52,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -52,6 +52,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
bool canBreakProgram(); bool canBreakProgram();
void breakProgram(int targetContextGroupId); void breakProgram(int targetContextGroupId);
void continueProgram(int targetContextGroupId); void continueProgram(int targetContextGroupId);
void breakProgramOnAssert(int targetContextGroupId);
void setPauseOnNextStatement(bool, int targetContextGroupId); void setPauseOnNextStatement(bool, int targetContextGroupId);
void stepIntoStatement(int targetContextGroupId); void stepIntoStatement(int targetContextGroupId);
...@@ -188,6 +189,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -188,6 +189,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
v8::Local<v8::Context> m_pausedContext; v8::Local<v8::Context> m_pausedContext;
int m_ignoreScriptParsedEventsCounter; int m_ignoreScriptParsedEventsCounter;
bool m_scheduledOOMBreak = false; bool m_scheduledOOMBreak = false;
bool m_scheduledAssertBreak = false;
int m_targetContextGroupId = 0; int m_targetContextGroupId = 0;
int m_pausedContextGroupId = 0; int m_pausedContextGroupId = 0;
String16 m_continueToLocationBreakpointId; String16 m_continueToLocationBreakpointId;
......
Tests that multiple sessions pause once on console.assert.
Pausing on exceptions in 1
Asserting in 1
Paused in 2 with reason assert
Paused in 1 with reason assert
Asserting in 2
Paused in 2 with reason assert
Paused in 1 with reason assert
Pausing on exceptions in both
Asserting in 1
Paused in 2 with reason assert
Paused in 1 with reason assert
Asserting in 2
Paused in 2 with reason assert
Paused in 1 with reason assert
Not pausing on exceptions
Asserting in 1
Asserting in 2
Pausing on exceptions in 3 (different context group)
Asserting in 3
Paused in 3 with reason assert
Asserting in 1
Paused in 2 with reason assert
Paused in 1 with reason assert
// 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.
InspectorTest.log('Tests that multiple sessions pause once on console.assert.');
(async function test() {
var contextGroup1 = new InspectorTest.ContextGroup();
var session1 = await connect(contextGroup1, 1);
var session2 = await connect(contextGroup1, 2);
var contextGroup2 = new InspectorTest.ContextGroup();
var session3 = await connect(contextGroup2, 3);
InspectorTest.log('Pausing on exceptions in 1');
await session1.Protocol.Debugger.setPauseOnExceptions({state: 'all'});
InspectorTest.log('Asserting in 1');
await session1.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Asserting in 2');
await session2.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Pausing on exceptions in both');
await session2.Protocol.Debugger.setPauseOnExceptions({state: 'all'});
InspectorTest.log('Asserting in 1');
await session1.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Asserting in 2');
await session2.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Not pausing on exceptions');
await session1.Protocol.Debugger.setPauseOnExceptions({state: 'none'});
await session2.Protocol.Debugger.setPauseOnExceptions({state: 'none'});
InspectorTest.log('Asserting in 1');
await session1.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Asserting in 2');
await session2.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Pausing on exceptions in 3 (different context group)');
await session3.Protocol.Debugger.setPauseOnExceptions({state: 'all'});
InspectorTest.log('Asserting in 3');
await session3.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.log('Asserting in 1');
await session1.Protocol.Runtime.evaluate({expression: 'console.assert(false)'});
InspectorTest.completeTest();
})();
async function connect(contextGroup, num) {
var session = contextGroup.connect();
await session.Protocol.Debugger.enable();
session.Protocol.Debugger.onPaused(message => {
InspectorTest.log(`Paused in ${num} with reason ${message.params.reason}`);
session.Protocol.Debugger.resume();
});
return session;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment