Skip to content

Commit

Permalink
Add support for function breakpoints
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Moran <rob.moran@arm.com>
  • Loading branch information
thegecko committed Nov 14, 2019
1 parent 8084f52 commit 5da76e8
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 19 deletions.
17 changes: 17 additions & 0 deletions src/GDBDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export class GDBDebugSession extends LoggingDebugSession {
response.body.supportsConditionalBreakpoints = true;
response.body.supportsHitConditionalBreakpoints = true;
response.body.supportsLogPoints = true;
response.body.supportsFunctionBreakpoints = true;
// response.body.supportsSetExpression = true;
response.body.supportsDisassembleRequest = true;
this.sendResponse(response);
Expand Down Expand Up @@ -352,6 +353,22 @@ export class GDBDebugSession extends LoggingDebugSession {
}
}

protected async setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse,
args: DebugProtocol.SetFunctionBreakpointsArguments) {
try {
for (const fn of args.breakpoints) {
try {
await mi.sendBreakFunctionInsert(this.gdb, fn.name);
} catch (error) {
this.sendEvent(new OutputEvent(`Unable to set function breakpoint: ${error.message}`));
}
}
this.sendResponse(response);
} catch (err) {
this.sendErrorResponse(response, 1, err.message);
}
}

protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse,
args: DebugProtocol.ConfigurationDoneArguments): Promise<void> {
try {
Expand Down
4 changes: 2 additions & 2 deletions src/integration-tests/evaluate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('evaluate request', function() {
const res = await dc.evaluateRequest({
context: 'repl',
expression: '2 + 2',
frameId: scope.frameId,
frameId: scope.frame.id,
});

expect(res.body.result).eq('4');
Expand All @@ -76,7 +76,7 @@ describe('evaluate request', function() {
const err = await expectRejection(dc.evaluateRequest({
context: 'repl',
expression: '2 +',
frameId: scope.frameId,
frameId: scope.frame.id,
}));

expect(err.message).eq('-var-create: unable to create variable object');
Expand Down
68 changes: 68 additions & 0 deletions src/integration-tests/functionBreakpoints.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*********************************************************************
* Copyright (c) 2019 Arm and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*********************************************************************/

import { join } from 'path';
import { expect } from 'chai';
import { CdtDebugClient } from './debugClient';
import { LaunchRequestArguments } from '../GDBDebugSession';
import {
standardBeforeEach,
gdbPath,
testProgramsDir,
openGdbConsole,
getScopes,
} from './utils';

describe('function breakpoints', async () => {
let dc: CdtDebugClient;

beforeEach(async () => {
dc = await standardBeforeEach();

await dc.launchRequest({
verbose: true,
gdb: gdbPath,
program: join(testProgramsDir, 'functions'),
openGdbConsole,
} as LaunchRequestArguments);
});

afterEach(async () => {
await dc.stop();
});

it('hits the main function breakpoint', async () => {
await dc.setFunctionBreakpointsRequest({
breakpoints: [
{
name: 'main',
},
],
});
await dc.configurationDoneRequest();
dc.waitForEvent('stopped');
const scope = await getScopes(dc);
expect(scope.frame.line).to.eq(6);
});

it('hits the sub function breakpoint', async () => {
await dc.setFunctionBreakpointsRequest({
breakpoints: [
{
name: 'sub',
},
],
});
await dc.configurationDoneRequest();
dc.waitForEvent('stopped');
const scope = await getScopes(dc);
expect(scope.frame.line).to.eq(2);
});
});
2 changes: 1 addition & 1 deletion src/integration-tests/logpoints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
standardBeforeEach,
gdbPath,
testProgramsDir,
openGdbConsole
openGdbConsole,
} from './utils';

describe('logpoints', async () => {
Expand Down
1 change: 1 addition & 0 deletions src/integration-tests/test-programs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
count
functions
disassemble
empty
empty space
Expand Down
5 changes: 4 additions & 1 deletion src/integration-tests/test-programs/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
BINS = empty empty\ space evaluate vars vars_cpp mem segv count disassemble
BINS = empty empty\ space evaluate vars vars_cpp mem segv count disassemble functions

.PHONY: all
all: $(BINS)
Expand All @@ -8,6 +8,9 @@ CXX = g++
LINK = $(CC) -o $@ $^
LINK_CXX = $(CXX) -o $@ $^

functions: functions.o
$(LINK)

count: count.o
$(LINK)

Expand Down
8 changes: 8 additions & 0 deletions src/integration-tests/test-programs/functions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
int sub() {
return 0;
}

int main() {
sub();
return 0;
}
12 changes: 7 additions & 5 deletions src/integration-tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { DebugProtocol } from 'vscode-debugprotocol';
import { CdtDebugClient } from './debugClient';

export interface Scope {
threadId: number;
frameId: number;
thread: DebugProtocol.Thread;
frame: DebugProtocol.StackFrame;
scopes: DebugProtocol.ScopesResponse;
}

Expand All @@ -29,13 +29,15 @@ export async function getScopes(
// threads
const threads = await dc.threadsRequest();
expect(threads.body.threads.length, 'There are fewer threads than expected.').to.be.at.least(threadIndex + 1);
const threadId = threads.body.threads[threadIndex].id;
const thread = threads.body.threads[threadIndex];
const threadId = thread.id;
// stack trace
const stack = await dc.stackTraceRequest({ threadId });
expect(stack.body.stackFrames.length, 'There are fewer stack frames than expected.').to.be.at.least(stackIndex + 1);
const frameId = stack.body.stackFrames[stackIndex].id;
const frame = stack.body.stackFrames[stackIndex];
const frameId = frame.id;
const scopes = await dc.scopesRequest({ frameId });
return Promise.resolve({ threadId, frameId, scopes });
return Promise.resolve({ thread, frame, scopes });
}

/**
Expand Down
20 changes: 10 additions & 10 deletions src/integration-tests/var.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('Variables Test Suite', function() {
verifyVariable(vars.body.variables[0], 'a', 'int', '25');
verifyVariable(vars.body.variables[1], 'b', 'int', '10');
// step the program and see that the values were passed to the program and evaluated.
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 1 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 1 });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
vr = scope.scopes.body.scopes[0].variablesReference;
Expand All @@ -89,8 +89,8 @@ describe('Variables Test Suite', function() {

it('can read and set struct variables in a program', async function() {
// step past the initialization for the structure
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 1 });
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 2 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 1 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 2 });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
// assert we can see the struct and its elements
Expand Down Expand Up @@ -119,7 +119,7 @@ describe('Variables Test Suite', function() {
verifyVariable(children.body.variables[0], 'x', 'int', '25');
verifyVariable(children.body.variables[1], 'y', 'int', '10');
// step the program and see that the values were passed to the program and evaluated.
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 3 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 3 });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
vr = scope.scopes.body.scopes[0].variablesReference;
Expand All @@ -130,8 +130,8 @@ describe('Variables Test Suite', function() {

it('can read and set nested struct variables in a program', async function() {
// step past the initialization for the structure
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 1 });
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 2 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 1 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 2 });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
// assert we can see the 'foo' struct and its child 'bar' struct
Expand Down Expand Up @@ -167,8 +167,8 @@ describe('Variables Test Suite', function() {
verifyVariable(subChildren.body.variables[0], 'a', 'int', '25');
verifyVariable(subChildren.body.variables[1], 'b', 'int', '10');
// step the program and see that the values were passed to the program and evaluated.
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 3 });
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: lineTags['STOP HERE'] + 4 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 3 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: lineTags['STOP HERE'] + 4 });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
vr = scope.scopes.body.scopes[0].variablesReference;
Expand All @@ -181,7 +181,7 @@ describe('Variables Test Suite', function() {
// skip ahead to array initialization
const br = await dc.setBreakpointsRequest({ source: { path: varsSrc }, breakpoints: [{ line: 24 }] });
expect(br.success).to.equal(true);
await dc.continueRequest({ threadId: scope.threadId });
await dc.continueRequest({ threadId: scope.thread.id });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
// assert we can see the array and its elements
Expand Down Expand Up @@ -214,7 +214,7 @@ describe('Variables Test Suite', function() {
verifyVariable(children.body.variables[1], '[1]', 'int', '22');
verifyVariable(children.body.variables[2], '[2]', 'int', '33');
// step the program and see that the values were passed to the program and evaluated.
await dc.next({ threadId: scope.threadId }, { path: varsSrc, line: 25 });
await dc.next({ threadId: scope.thread.id }, { path: varsSrc, line: 25 });
scope = await getScopes(dc);
expect(scope.scopes.body.scopes.length, 'Unexpected number of scopes returned').to.equal(1);
vr = scope.scopes.body.scopes[0].variablesReference;
Expand Down
5 changes: 5 additions & 0 deletions src/mi/breakpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,8 @@ export function sendBreakDelete(gdb: GDBBackend, request: {
export function sendBreakList(gdb: GDBBackend): Promise<MIBreakListResponse> {
return gdb.sendCommand('-break-list');
}

export function sendBreakFunctionInsert(gdb: GDBBackend, fn: string) {
const command = `-break-insert --function ${fn}`;
return gdb.sendCommand(command);
}

0 comments on commit 5da76e8

Please sign in to comment.