Commit f2b5a6da authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] added Debugger.setBreakpointOnFunctionCall

This function can be used to set breakpoint on any function call,
including native functions without source code, for them new method is
only one way to set breakpoint.

R=dgozman@chromium.org

Bug: chromium:828076
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Iae8f4805b6e860a7ca008041fdfbe75e43a1959c
Reviewed-on: https://chromium-review.googlesource.com/1023128
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52745}
parent 94139bc6
......@@ -711,6 +711,31 @@
}
]
},
{
"name": "setBreakpointOnFunctionCall",
"description": "Sets JavaScript breakpoint before each call to the given function.\nIf another function was created from the same source as a given one,\ncalling it will also trigger the breakpoint.",
"experimental": true,
"parameters": [
{
"name": "objectId",
"description": "Function object id.",
"$ref": "Runtime.RemoteObjectId"
},
{
"name": "condition",
"description": "Expression to use as a breakpoint condition. When specified, debugger will\nstop on the breakpoint if this expression evaluates to true.",
"optional": true,
"type": "string"
}
],
"returns": [
{
"name": "breakpointId",
"description": "Id of the created breakpoint for further reference.",
"$ref": "BreakpointId"
}
]
},
{
"name": "setBreakpointsActive",
"description": "Activates / deactivates all breakpoints on the page.",
......
......@@ -343,6 +343,20 @@ domain Debugger
# List of the locations this breakpoint resolved into upon addition.
array of Location locations
# Sets JavaScript breakpoint before each call to the given function.
# If another function was created from the same source as a given one,
# calling it will also trigger the breakpoint.
experimental command setBreakpointOnFunctionCall
parameters
# Function object id.
Runtime.RemoteObjectId objectId
# Expression to use as a breakpoint condition. When specified, debugger will
# stop on the breakpoint if this expression evaluates to true.
optional string condition
returns
# Id of the created breakpoint for further reference.
BreakpointId breakpointId
# Activates / deactivates all breakpoints on the page.
command setBreakpointsActive
parameters
......
......@@ -80,7 +80,8 @@ enum class BreakpointType {
kByScriptHash,
kByScriptId,
kDebugCommand,
kMonitorCommand
kMonitorCommand,
kBreakpointAtEntry
};
String16 generateBreakpointId(BreakpointType type,
......@@ -114,12 +115,13 @@ bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
rawType > static_cast<int>(BreakpointType::kMonitorCommand)) {
rawType > static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
return false;
}
if (type) *type = static_cast<BreakpointType>(rawType);
if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
rawType == static_cast<int>(BreakpointType::kMonitorCommand)) {
rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
// The script and source position is not encoded in this case.
return true;
}
......@@ -603,6 +605,30 @@ Response V8DebuggerAgentImpl::setBreakpoint(
return Response::OK();
}
Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
const String16& functionObjectId, Maybe<String16> optionalCondition,
String16* outBreakpointId) {
InjectedScript::ObjectScope scope(m_session, functionObjectId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!scope.object()->IsFunction()) {
return Response::Error("Could not find function with given id");
}
v8::Local<v8::Function> function =
v8::Local<v8::Function>::Cast(scope.object());
String16 breakpointId =
generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
m_breakpointIdToDebuggerBreakpointIds.end()) {
return Response::Error("Breakpoint at specified location already exists.");
}
v8::Local<v8::String> condition =
toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
setBreakpointImpl(breakpointId, function, condition);
*outBreakpointId = breakpointId;
return Response::OK();
}
Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
BreakpointType type;
......
......@@ -55,6 +55,9 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
std::unique_ptr<protocol::Debugger::Location>,
Maybe<String16> optionalCondition, String16*,
std::unique_ptr<protocol::Debugger::Location>* actualLocation) override;
Response setBreakpointOnFunctionCall(const String16& functionObjectId,
Maybe<String16> optionalCondition,
String16* outBreakpointId) override;
Response removeBreakpoint(const String16& breakpointId) override;
Response continueToLocation(std::unique_ptr<protocol::Debugger::Location>,
Maybe<String16> targetCallFrames) override;
......
Tests Debugger.setBreakpointOnFunctionCall.
set breakpoint on function call
call function
paused
hitBreakpoints contains id: true
remove breakpoint
{
id : <messageId>
result : {
}
}
call function again
evaluate finished without pause
set breakpoint on function call
set breakpoint on same function call
{
error : {
code : -32000
message : Breakpoint at specified location already exists.
}
id : <messageId>
}
set breakpoint on function call with condition
call function, condition is false
evaluate finished without pause
call function, condition is true
paused
hitBreakpoints contains id: true
remove breakpoint
{
id : <messageId>
result : {
}
}
call function again
evaluate finished without pause
// Copyright 2018 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.
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests Debugger.setBreakpointOnFunctionCall.');
(async function test() {
Protocol.Debugger.enable();
{
const {result: {result: {objectId}}} = await Protocol.Runtime.evaluate(
{expression: 'function foo(a,b){ return a + b; }; foo'});
InspectorTest.log('set breakpoint on function call');
const {result: {breakpointId}} =
await Protocol.Debugger.setBreakpointOnFunctionCall({objectId});
InspectorTest.log('call function');
Protocol.Runtime.evaluate({expression: 'foo(1,2)'});
const {params: {hitBreakpoints}} = await Protocol.Debugger.oncePaused();
InspectorTest.log('paused');
InspectorTest.log(
`hitBreakpoints contains id: ${hitBreakpoints[0] === breakpointId}`);
await Protocol.Debugger.resume();
InspectorTest.log('remove breakpoint');
const result = await Protocol.Debugger.removeBreakpoint({breakpointId});
InspectorTest.logMessage(result);
InspectorTest.log('call function again');
await Protocol.Runtime.evaluate({expression: 'foo(1,2)'});
InspectorTest.log('evaluate finished without pause');
}
{
const {result: {result: {objectId}}} = await Protocol.Runtime.evaluate(
{expression: 'function foo(a,b){ return a + b; }; foo'});
InspectorTest.log('set breakpoint on function call');
const {result: {breakpointId}} =
await Protocol.Debugger.setBreakpointOnFunctionCall({objectId});
InspectorTest.log('set breakpoint on same function call');
InspectorTest.logMessage(
await Protocol.Debugger.setBreakpointOnFunctionCall({objectId}));
await Protocol.Debugger.removeBreakpoint({breakpointId});
}
{
const {result: {result: {objectId}}} =
await Protocol.Runtime.evaluate({expression: 'Array.prototype.push'});
InspectorTest.log('set breakpoint on function call with condition');
const {result: {breakpointId}} =
await Protocol.Debugger.setBreakpointOnFunctionCall(
{objectId, condition: 'arguments[0] === 2'});
InspectorTest.log('call function, condition is false');
await Protocol.Runtime.evaluate({expression: '[].push(0)'});
InspectorTest.log('evaluate finished without pause');
InspectorTest.log('call function, condition is true');
Protocol.Runtime.evaluate({expression: '[].push(2)'});
const {params: {hitBreakpoints}} = await Protocol.Debugger.oncePaused();
InspectorTest.log('paused');
InspectorTest.log(
`hitBreakpoints contains id: ${hitBreakpoints[0] === breakpointId}`);
await Protocol.Debugger.resume();
InspectorTest.log('remove breakpoint');
const result = await Protocol.Debugger.removeBreakpoint({breakpointId});
InspectorTest.logMessage(result);
InspectorTest.log('call function again');
Protocol.Runtime.evaluate({expression: '[].push(2)'});
InspectorTest.log('evaluate finished without pause');
}
InspectorTest.completeTest();
})();
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