From 4e6c1da53e797469a1de899b8361153f4de95918 Mon Sep 17 00:00:00 2001 From: Marcel Ball Date: Tue, 23 Jan 2018 15:52:45 -0400 Subject: [PATCH] Improve handling of breakpoints - Fixes cases where breakpoints could be clear incorrectly - Adds support for breakpoints added through disassembly views --- package.json | 3 +- src/backend/backend.ts | 7 ++-- src/backend/mi2/mi2.ts | 74 +++++++++++--------------------------- src/gdb.ts | 82 ++++++++++++++++++++++++++++++------------ 4 files changed, 85 insertions(+), 81 deletions(-) diff --git a/package.json b/package.json index 3dbffcaa..911006da 100644 --- a/package.json +++ b/package.json @@ -974,7 +974,8 @@ "c", "cpp", "asm", - "arm" + "arm", + "cortex-debug.disassembly" ] }, "initialConfigurations": [ diff --git a/src/backend/backend.ts b/src/backend/backend.ts index 5c000dfb..9356cb0a 100644 --- a/src/backend/backend.ts +++ b/src/backend/backend.ts @@ -7,6 +7,7 @@ export interface Breakpoint { raw?: string; condition: string; countCondition?: string; + number?: number; } export interface Stack { @@ -35,10 +36,8 @@ export interface IBackend { next(): Thenable; step(): Thenable; stepOut(): Thenable; - loadBreakPoints(breakpoints: Breakpoint[]): Thenable<[boolean, Breakpoint][]>; - addBreakPoint(breakpoint: Breakpoint): Thenable<[boolean, Breakpoint]>; - removeBreakPoint(breakpoint: Breakpoint): Thenable; - clearBreakPoints(): Thenable; + addBreakPoint(breakpoint: Breakpoint): Promise; + removeBreakpoints(breakpoints: number[]): Promise; getStack(maxLevels: number): Thenable; getStackVariables(thread: number, frame: number): Thenable; evalExpression(name: string): Thenable; diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts index 3425bca9..b5fc6ec8 100644 --- a/src/backend/mi2/mi2.ts +++ b/src/backend/mi2/mi2.ts @@ -272,28 +272,16 @@ export class MI2 extends EventEmitter implements IBackend { return this.sendCommand("gdb-set var " + name + "=" + rawValue); } - loadBreakPoints(breakpoints: Breakpoint[]): Thenable<[boolean, Breakpoint][]> { - if (trace) - this.log("stderr", "loadBreakPoints"); - let promisses = []; - breakpoints.forEach(breakpoint => { - promisses.push(this.addBreakPoint(breakpoint)); - }); - return Promise.all(promisses); - } - setBreakPointCondition(bkptNum, condition): Thenable { if (trace) this.log("stderr", "setBreakPointCondition"); return this.sendCommand("break-condition " + bkptNum + " " + condition); } - addBreakPoint(breakpoint: Breakpoint): Thenable<[boolean, Breakpoint]> { + addBreakPoint(breakpoint: Breakpoint): Promise { if (trace) this.log("stderr", "addBreakPoint"); return new Promise((resolve, reject) => { - if (this.breakpoints.has(breakpoint)) - return resolve([false, undefined]); let location = ""; if (breakpoint.countCondition) { if (breakpoint.countCondition[0] == ">") @@ -308,69 +296,50 @@ export class MI2 extends EventEmitter implements IBackend { location += "-t -i " + parseInt(match) + " "; } } + if (breakpoint.raw) - location += '"' + escape(breakpoint.raw) + '"'; + location += '*' + escape(breakpoint.raw); else location += '"' + escape(breakpoint.file) + ":" + breakpoint.line + '"'; - this.sendCommand("break-insert -f " + location).then((result) => { + + this.sendCommand(`break-insert ${location}`).then((result) => { if (result.resultRecords.resultClass == "done") { let bkptNum = parseInt(result.result("bkpt.number")); - let newBrk = { - file: result.result("bkpt.file"), - line: parseInt(result.result("bkpt.line")), - condition: breakpoint.condition - }; + breakpoint.number = bkptNum; + if (breakpoint.condition) { this.setBreakPointCondition(bkptNum, breakpoint.condition).then((result) => { if (result.resultRecords.resultClass == "done") { - this.breakpoints.set(newBrk, bkptNum); - resolve([true, newBrk]); + resolve(breakpoint); } else { - resolve([false, null]); + resolve(null); } }, reject); } else { - this.breakpoints.set(newBrk, bkptNum); - resolve([true, newBrk]); + resolve(breakpoint); } } else { - reject(result); + resolve(null); } }, reject); }); } - removeBreakPoint(breakpoint: Breakpoint): Thenable { + removeBreakpoints(breakpoints: number[]): Promise { if (trace) this.log("stderr", "removeBreakPoint"); return new Promise((resolve, reject) => { - if (!this.breakpoints.has(breakpoint)) - return resolve(false); - this.sendCommand("break-delete " + this.breakpoints.get(breakpoint)).then((result) => { - if (result.resultRecords.resultClass == "done") { - this.breakpoints.delete(breakpoint); - resolve(true); - } - else resolve(false); - }); - }); - } - - clearBreakPoints(): Thenable { - if (trace) - this.log("stderr", "clearBreakPoints"); - return new Promise((resolve, reject) => { - this.sendCommand("break-delete").then((result) => { - if (result.resultRecords.resultClass == "done") { - this.breakpoints.clear(); - resolve(true); - } - else resolve(false); - }, () => { - resolve(false); - }); + if (breakpoints.length === 0) { + resolve(true); + } + else { + let cmd = 'break-delete ' + breakpoints.join(' '); + this.sendCommand(cmd).then((result) => { + resolve(result.resultRecords.resultClass == 'done'); + }, reject); + } }); } @@ -563,7 +532,6 @@ export class MI2 extends EventEmitter implements IBackend { public procEnv: any; protected currentToken: number = 1; protected handlers: { [index: number]: (info: MINode) => any } = {}; - protected breakpoints: Map = new Map(); protected buffer: string; protected errbuf: string; protected process: ChildProcess.ChildProcess; diff --git a/src/gdb.ts b/src/gdb.ts index 706d332f..226c2672 100644 --- a/src/gdb.ts +++ b/src/gdb.ts @@ -85,6 +85,8 @@ export class GDBDebugSession extends DebugSession { protected commandServer: net.Server; protected forceDisassembly: boolean = false; + protected breakpointMap: Map = new Map(); + private currentFile: string; public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false, threadID: number = 1) { @@ -605,32 +607,66 @@ export class GDBDebugSession extends DebugSession { this.miDebugger.once("debug-ready", cb); } - protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void { - let cb = (() => { + protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments) { + let cb = (async () => { this.debugReady = true; - this.miDebugger.clearBreakPoints().then(() => { - let path = args.source.path; - let all = []; - args.breakpoints.forEach(brk => { - all.push(this.miDebugger.addBreakPoint({ file: path, line: brk.line, condition: brk.condition, countCondition: brk.hitCondition })); - }); - Promise.all(all).then(brkpoints => { - let finalBrks = []; - brkpoints.forEach(brkp => { - if (brkp[0]) - finalBrks.push({ line: brkp[1].line, verified: true }); - }); - response.body = { - breakpoints: finalBrks - }; - this.sendResponse(response); + let currentBreakpoints = (this.breakpointMap.get(args.source.path) || []).map(bp => bp.number); + + try { + await this.miDebugger.removeBreakpoints(currentBreakpoints); + this.breakpointMap.set(args.source.path, []); + + let all: Promise[] = []; + if (args.source.path.startsWith('disassembly:///')) { + let eidx = args.source.path.indexOf('?'); + let path = args.source.path.substring(15, eidx - 6); // Account for protocol and extension + let parts = path.split('::'); + let func: string; + let file: string; + + if (parts.length == 2) { + func = parts[1]; + file = parts[0]; + } + else { + func = parts[0]; + } + + let symbol: SymbolInformation = await this.getDisassemblyForFunction(func, file); - }, msg => { - this.sendErrorResponse(response, 9, msg.toString()); - }); - }, msg => { + args.breakpoints.forEach(brk => { + if (brk.line <= symbol.instructions.length) { + let line = symbol.instructions[brk.line - 1]; + all.push(this.miDebugger.addBreakPoint({ file: args.source.path, line: brk.line, condition: brk.condition, countCondition: brk.hitCondition, raw: line.address })); + } + }); + } + else { + args.breakpoints.forEach(brk => { + all.push(this.miDebugger.addBreakPoint({ file: args.source.path, line: brk.line, condition: brk.condition, countCondition: brk.hitCondition })); + }); + } + + let brkpoints = await Promise.all(all); + + let finalBrks: Breakpoint[] = brkpoints.filter(bp => bp !== null); + + response.body = { + breakpoints: finalBrks.map(bp => { + return { + line: bp.line, + id: bp.number, + verified: true + }; + }) + }; + + this.breakpointMap.set(args.source.path, finalBrks); + this.sendResponse(response); + } + catch (msg) { this.sendErrorResponse(response, 9, msg.toString()); - }); + }; }).bind(this); if (this.debugReady)