Commit d7e09f8f authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] added targetCallFrames for continueToLocation

By default we just break when we first time reach passed location, with current - we'll break at passed location only when it happens within the same stack frame.

BUG=v8:6397
R=dgozman@chromium.org

Review-Url: https://codereview.chromium.org/2879923003
Cr-Commit-Position: refs/heads/master@{#45354}
parent b02bb408
......@@ -557,7 +557,8 @@
{
"name": "continueToLocation",
"parameters": [
{ "name": "location", "$ref": "Location", "description": "Location to continue to." }
{ "name": "location", "$ref": "Location", "description": "Location to continue to." },
{ "name": "targetCallFrames", "type": "string", "enum": ["any", "current"], "optional": true, "experimental": true }
],
"description": "Continues execution until specific location is reached."
},
......
......@@ -483,11 +483,14 @@ Response V8DebuggerAgentImpl::getPossibleBreakpoints(
}
Response V8DebuggerAgentImpl::continueToLocation(
std::unique_ptr<protocol::Debugger::Location> location) {
std::unique_ptr<protocol::Debugger::Location> location,
Maybe<String16> targetCallFrames) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
return m_debugger->continueToLocation(m_session->contextGroupId(),
std::move(location));
return m_debugger->continueToLocation(
m_session->contextGroupId(), std::move(location),
targetCallFrames.fromMaybe(
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
}
bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
......
......@@ -56,8 +56,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
Maybe<String16> optionalCondition, String16*,
std::unique_ptr<protocol::Debugger::Location>* actualLocation) override;
Response removeBreakpoint(const String16& breakpointId) override;
Response continueToLocation(
std::unique_ptr<protocol::Debugger::Location>) override;
Response continueToLocation(std::unique_ptr<protocol::Debugger::Location>,
Maybe<String16> targetCallFrames) override;
Response searchInContent(
const String16& scriptId, const String16& query,
Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
......
......@@ -414,7 +414,8 @@ void V8Debugger::scheduleStepIntoAsync(
Response V8Debugger::continueToLocation(
int targetContextGroupId,
std::unique_ptr<protocol::Debugger::Location> location) {
std::unique_ptr<protocol::Debugger::Location> location,
const String16& targetCallFrames) {
DCHECK(isPaused());
DCHECK(!m_executionState.IsEmpty());
DCHECK(targetContextGroupId);
......@@ -427,6 +428,12 @@ Response V8Debugger::continueToLocation(
m_continueToLocationBreakpointId =
setBreakpoint(breakpoint, &lineNumber, &columnNumber);
if (!m_continueToLocationBreakpointId.isEmpty()) {
m_continueToLocationTargetCallFrames = targetCallFrames;
if (m_continueToLocationTargetCallFrames !=
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
m_continueToLocationStack = captureStackTrace(true);
DCHECK(m_continueToLocationStack);
}
continueProgram(targetContextGroupId);
// TODO(kozyatinskiy): Return actual line and column number.
return Response::OK();
......@@ -435,11 +442,26 @@ Response V8Debugger::continueToLocation(
}
}
void V8Debugger::clearContinueToLocation() {
if (m_continueToLocationBreakpointId.length()) {
removeBreakpoint(m_continueToLocationBreakpointId);
m_continueToLocationBreakpointId = String16();
bool V8Debugger::shouldContinueToCurrentLocation() {
if (m_continueToLocationTargetCallFrames ==
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
return true;
}
std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true);
if (m_continueToLocationTargetCallFrames ==
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
return m_continueToLocationStack->isEqualIgnoringTopFrame(
currentStack.get());
}
return true;
}
void V8Debugger::clearContinueToLocation() {
if (m_continueToLocationBreakpointId.isEmpty()) return;
removeBreakpoint(m_continueToLocationBreakpointId);
m_continueToLocationBreakpointId = String16();
m_continueToLocationTargetCallFrames = String16();
m_continueToLocationStack.reset();
}
Response V8Debugger::setScriptSource(
......@@ -597,6 +619,11 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
breakpointIds.push_back(String16::fromInteger(
hitBreakpointNumber->Int32Value(debuggerContext()).FromJust()));
}
if (breakpointIds.size() == 1 &&
breakpointIds[0] == m_continueToLocationBreakpointId) {
v8::Context::Scope contextScope(pausedContext);
if (!shouldContinueToCurrentLocation()) return;
}
}
clearContinueToLocation();
......
......@@ -62,7 +62,8 @@ class V8Debugger : public v8::debug::DebugDelegate {
int targetContextGroupId);
Response continueToLocation(int targetContextGroupId,
std::unique_ptr<protocol::Debugger::Location>);
std::unique_ptr<protocol::Debugger::Location>,
const String16& targetCallFramess);
Response setScriptSource(
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
......@@ -123,6 +124,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
v8::Local<v8::Context> debuggerContext() const;
void clearBreakpoints();
void clearContinueToLocation();
bool shouldContinueToCurrentLocation();
static void v8OOMCallback(void* data);
......@@ -189,6 +191,8 @@ class V8Debugger : public v8::debug::DebugDelegate {
int m_targetContextGroupId = 0;
int m_pausedContextGroupId = 0;
String16 m_continueToLocationBreakpointId;
String16 m_continueToLocationTargetCallFrames;
std::unique_ptr<V8StackTraceImpl> m_continueToLocationStack;
using AsyncTaskToStackTrace =
protocol::HashMap<void*, std::weak_ptr<AsyncStackTrace>>;
......
......@@ -126,6 +126,12 @@ std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject()
.build();
}
bool StackFrame::isEqual(StackFrame* frame) const {
return m_scriptId == frame->m_scriptId &&
m_lineNumber == frame->m_lineNumber &&
m_columnNumber == frame->m_columnNumber;
}
// static
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
v8::Isolate* isolate, bool capture) {
......@@ -241,6 +247,49 @@ std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
return StringBufferImpl::adopt(string);
}
bool V8StackTraceImpl::isEqualIgnoringTopFrame(
V8StackTraceImpl* stackTrace) const {
StackFrameIterator current(this);
StackFrameIterator target(stackTrace);
current.next();
target.next();
while (!current.done() && !target.done()) {
if (!current.frame()->isEqual(target.frame())) {
return false;
}
current.next();
target.next();
}
return current.done() == target.done();
}
V8StackTraceImpl::StackFrameIterator::StackFrameIterator(
const V8StackTraceImpl* stackTrace)
: m_currentIt(stackTrace->m_frames.begin()),
m_currentEnd(stackTrace->m_frames.end()),
m_parent(stackTrace->m_asyncParent.lock().get()) {}
void V8StackTraceImpl::StackFrameIterator::next() {
if (m_currentIt == m_currentEnd) return;
++m_currentIt;
while (m_currentIt == m_currentEnd && m_parent) {
const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
m_currentIt = frames.begin();
if (m_parent->description() == "async function") ++m_currentIt;
m_currentEnd = frames.end();
m_parent = m_parent->parent().lock().get();
}
}
bool V8StackTraceImpl::StackFrameIterator::done() {
return m_currentIt == m_currentEnd;
}
StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
return m_currentIt->get();
}
// static
std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
V8Debugger* debugger, int contextGroupId, const String16& description,
......
......@@ -33,6 +33,7 @@ class StackFrame {
int lineNumber() const; // 0-based.
int columnNumber() const; // 0-based.
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
bool isEqual(StackFrame* frame) const;
private:
String16 m_functionName;
......@@ -72,12 +73,28 @@ class V8StackTraceImpl : public V8StackTrace {
const override;
std::unique_ptr<StringBuffer> toString() const override;
bool isEqualIgnoringTopFrame(V8StackTraceImpl* stackTrace) const;
private:
V8StackTraceImpl(std::vector<std::shared_ptr<StackFrame>> frames,
int maxAsyncDepth,
std::shared_ptr<AsyncStackTrace> asyncParent,
std::shared_ptr<AsyncStackTrace> asyncCreation);
class StackFrameIterator {
public:
explicit StackFrameIterator(const V8StackTraceImpl* stackTrace);
void next();
StackFrame* frame();
bool done();
private:
std::vector<std::shared_ptr<StackFrame>>::const_iterator m_currentIt;
std::vector<std::shared_ptr<StackFrame>>::const_iterator m_currentEnd;
AsyncStackTrace* m_parent;
};
std::vector<std::shared_ptr<StackFrame>> m_frames;
int m_maxAsyncDepth;
std::weak_ptr<AsyncStackTrace> m_asyncParent;
......@@ -106,6 +123,9 @@ class AsyncStackTrace {
// TODO(kozyatinskiy): implement it without hack.
m_description = description;
}
const std::vector<std::shared_ptr<StackFrame>>& frames() const {
return m_frames;
}
private:
AsyncStackTrace(int contextGroupId, const String16& description,
......
Check that continue-to-location works with different strategies.
Running test: testAwaitAny
(anonymous) (expr.js:0:0)
asyncFact (test.js:9:2)
(anonymous) (expr.js:0:0)
asyncFact (test.js:11:2)
-- async function --
asyncFact (test.js:8:24)
asyncFact (test.js:10:20)
asyncFact (test.js:10:20)
asyncFact (test.js:10:20)
(anonymous) (expr.js:0:0)
Running test: testAwaitCurrent
(anonymous) (expr.js:0:0)
asyncFact (test.js:9:2)
(anonymous) (expr.js:0:0)
asyncFact (test.js:11:2)
-- async function --
asyncFact (test.js:8:24)
(anonymous) (expr.js:0:0)
Running test: testAny
(anonymous) (expr.js:0:0)
fact (test.js:16:2)
(anonymous) (expr.js:0:0)
fact (test.js:18:2)
fact (test.js:17:14)
fact (test.js:17:14)
fact (test.js:17:14)
(anonymous) (expr.js:0:0)
Running test: testCurrent
(anonymous) (expr.js:0:0)
fact (test.js:16:2)
(anonymous) (expr.js:0:0)
fact (test.js:18:2)
(anonymous) (expr.js:0:0)
Running test: testTopLevelAny
(anonymous) (expr.js:0:0)
topLevel (test.js:23:2)
(anonymous) (expr.js:0:0)
(anonymous) (:1:10)
topLevel (test.js:23:2)
(anonymous) (expr.js:0:0)
(anonymous) (:4:10)
topLevel (test.js:23:2)
(anonymous) (expr.js:0:0)
Running test: testTopLevelCurrent
(anonymous) (expr.js:0:0)
topLevel (test.js:23:2)
(anonymous) (expr.js:0:0)
(anonymous) (:1:10)
topLevel (test.js:23:2)
(anonymous) (expr.js:0:0)
(anonymous) (:4:10)
topLevel (test.js:23:2)
(anonymous) (expr.js:0:0)
// 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('Check that continue-to-location works with different strategies.');
InspectorTest.addScript(`
async function asyncFact(n) {
if (n == 0) return 1;
let r = n * await asyncFact(n - 1);
console.log(r);
return r;
}
function fact(n) {
if (n == 0) return 1;
let r = n * fact(n - 1);
console.log(r);
return r;
}
function topLevel() {
eval(` + '`' + `
var a = 1;
var b = 2;
fact(3);
console.log(a + b);
` + '`' + `);
}
//# sourceURL=test.js`, 7, 26);
InspectorTest.setupScriptMap();
InspectorTest.runAsyncTestSuite([
async function testAwaitAny() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'asyncFact(4)//# sourceURL=expr.js'});
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
let message = await pausedAndDumpStack();
let location = message.params.callFrames[0].location;
location.lineNumber = 11;
Protocol.Debugger.continueToLocation({location, targetCallFrames: 'any'});
await pausedAndDumpStack();
Protocol.Debugger.disable();
},
async function testAwaitCurrent() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'asyncFact(4)//# sourceURL=expr.js'});
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
let message = await pausedAndDumpStack();
let location = message.params.callFrames[0].location;
location.lineNumber = 11;
Protocol.Debugger.continueToLocation({location, targetCallFrames: 'current'});
await pausedAndDumpStack();
await Protocol.Debugger.resume();
Protocol.Debugger.disable();
},
async function testAny() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'fact(4)//# sourceURL=expr.js'});
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
let message = await pausedAndDumpStack();
let location = message.params.callFrames[0].location;
location.lineNumber = 18;
Protocol.Debugger.continueToLocation({location, targetCallFrames: 'any'});
await pausedAndDumpStack();
Protocol.Debugger.disable();
},
async function testCurrent() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'fact(4)//# sourceURL=expr.js'});
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
let message = await pausedAndDumpStack();
let location = message.params.callFrames[0].location;
location.lineNumber = 18;
Protocol.Debugger.continueToLocation({location, targetCallFrames: 'current'});
await pausedAndDumpStack();
await Protocol.Debugger.resume();
Protocol.Debugger.disable();
},
async function testTopLevelAny() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'topLevel()//# sourceURL=expr.js'});
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
let message = await pausedAndDumpStack();
let location = message.params.callFrames[0].location;
location.lineNumber = 4;
Protocol.Debugger.continueToLocation({location, targetCallFrames: 'any'});
await pausedAndDumpStack();
Protocol.Debugger.disable();
},
async function testTopLevelCurrent() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'topLevel()//# sourceURL=expr.js'});
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
await pausedAndDumpStack();
Protocol.Debugger.stepInto();
let message = await pausedAndDumpStack();
let location = message.params.callFrames[0].location;
location.lineNumber = 4;
Protocol.Debugger.continueToLocation({location, targetCallFrames: 'current'});
await pausedAndDumpStack();
await Protocol.Debugger.resume();
Protocol.Debugger.disable();
}
]);
async function pausedAndDumpStack() {
let message = await Protocol.Debugger.oncePaused();
InspectorTest.logCallFrames(message.params.callFrames);
InspectorTest.logAsyncStackTrace(message.params.asyncStackTrace);
InspectorTest.log('');
return message;
}
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