diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts index fcc6221c..9bf87384 100644 --- a/src/backend/mi2/mi2.ts +++ b/src/backend/mi2/mi2.ts @@ -419,7 +419,7 @@ export class MI2 extends EventEmitter implements IBackend { return this.sendCommand('gdb-set var ' + name + '=' + rawValue); } - public setBreakPointCondition(bkptNum, condition): Thenable { + private setBreakPointCondition(bkptNum, condition): Thenable { if (trace) { this.log('stderr', 'setBreakPointCondition'); } @@ -431,32 +431,36 @@ export class MI2 extends EventEmitter implements IBackend { this.log('stderr', 'addBreakPoint'); } return new Promise((resolve, reject) => { - let location = ''; + let bkptArgs = ''; if (breakpoint.countCondition) { if (breakpoint.countCondition[0] === '>') { - location += '-i ' + numRegex.exec(breakpoint.countCondition.substr(1))[0] + ' '; + bkptArgs += '-i ' + numRegex.exec(breakpoint.countCondition.substr(1))[0] + ' '; } else { const match = numRegex.exec(breakpoint.countCondition)[0]; if (match.length !== breakpoint.countCondition.length) { // tslint:disable-next-line:max-line-length this.log('stderr', 'Unsupported break count expression: \'' + breakpoint.countCondition + '\'. Only supports \'X\' for breaking once after X times or \'>X\' for ignoring the first X breaks'); - location += '-t '; + bkptArgs += '-t '; } else if (parseInt(match) !== 0) { - location += '-t -i ' + parseInt(match) + ' '; + bkptArgs += '-t -i ' + parseInt(match) + ' '; } } } + if (breakpoint.condition) { + bkptArgs += `-c "${breakpoint.condition}" `; + } + if (breakpoint.raw) { - location += '*' + escape(breakpoint.raw); + bkptArgs += '*' + escape(breakpoint.raw); } else { - location += '"' + escape(breakpoint.file) + ':' + breakpoint.line + '"'; + bkptArgs += '"' + escape(breakpoint.file) + ':' + breakpoint.line + '"'; } - this.sendCommand(`break-insert ${location}`).then((result) => { + this.sendCommand(`break-insert ${bkptArgs}`).then((result) => { if (result.resultRecords.resultClass === 'done') { const bkptNum = parseInt(result.result('bkpt.number')); const line = result.result('bkpt.line'); @@ -467,30 +471,10 @@ export class MI2 extends EventEmitter implements IBackend { const file = result.result('bkpt.fullname') || result.record('bkpt.file'); breakpoint.file = file ? file : undefined; } - - if (breakpoint.condition) { - this.setBreakPointCondition(bkptNum, breakpoint.condition).then((result) => { - if (result.resultRecords.resultClass === 'done') { - resolve(breakpoint); - } else { - reject(new MIError(result.result('msg') || 'Internal error', 'Setting breakpoint condition')); - } - }, - (reason) => { - // Just delete the breakpoint we just created as the condition creation failed - this.sendCommand(`break-delete ${bkptNum}`).then((x) => {}, (e) => { - console.error('Breakpoint delete failed?'); - console.error(e); - }); - reject(reason); // Use this reason as reason for failing to create the breakpoint - }); - } - else { - resolve(breakpoint); - } + resolve(breakpoint); } else { - reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${location}`)); + reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${bkptArgs}`)); } }, reject); }); @@ -518,27 +502,27 @@ export class MI2 extends EventEmitter implements IBackend { this.log('stderr', 'addBreakPoint'); } return new Promise((resolve, reject) => { - let location = ''; + let bkptArgs = ''; if (breakpoint.countCondition) { if (breakpoint.countCondition[0] === '>') { - location += '-i ' + numRegex.exec(breakpoint.countCondition.substr(1))[0] + ' '; + bkptArgs += '-i ' + numRegex.exec(breakpoint.countCondition.substr(1))[0] + ' '; } else { const match = numRegex.exec(breakpoint.countCondition)[0]; if (match.length !== breakpoint.countCondition.length) { // tslint:disable-next-line:max-line-length this.log('stderr', 'Unsupported break count expression: \'' + breakpoint.countCondition + '\'. Only supports \'X\' for breaking once after X times or \'>X\' for ignoring the first X breaks'); - location += '-t '; + bkptArgs += '-t '; } else if (parseInt(match) !== 0) { - location += '-t -i ' + parseInt(match) + ' '; + bkptArgs += '-t -i ' + parseInt(match) + ' '; } } } - location += breakpoint.exp; + bkptArgs += breakpoint.exp; const aType = breakpoint.accessType === 'read' ? '-r' : (breakpoint.accessType === 'readWrite' ? '-a' : ''); - this.sendCommand(`break-watch ${aType} ${location}`).then((result) => { + this.sendCommand(`break-watch ${aType} ${bkptArgs}`).then((result) => { if (result.resultRecords.resultClass === 'done') { const bkptNum = parseInt(result.result('bkpt.number')); const line = result.result('bkpt.line'); @@ -562,7 +546,7 @@ export class MI2 extends EventEmitter implements IBackend { } } else { - reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${location}`)); + reject(new MIError(result.result('msg') || 'Internal error', `Setting breakpoint at ${bkptArgs}`)); } }, reject); }); diff --git a/src/frontend/server_console.ts b/src/frontend/server_console.ts index afd6207c..91423aa9 100644 --- a/src/frontend/server_console.ts +++ b/src/frontend/server_console.ts @@ -106,7 +106,7 @@ export class GDBServerConsole { msg += msg.endsWith('\n') ? '' : '\n'; magentaWrite(msg, this.ptyTerm); } - this.logData(msg, true); + this.logData(msg); } finally {} } @@ -159,16 +159,16 @@ export class GDBServerConsole { }); } - private logData(data: Buffer | string, suppressNotReadMsg = false) { + private logData(data: Buffer | string) { try { if (this.logFd >= 0) { - if (!this.ptyTerm.isReady && !suppressNotReadMsg) { + if (!this.ptyTerm.isReady) { // Maybe we should do our own buffering rather than the pty doing it. This can // help if the user kills the terminal. But we would have lost previous data anyways const date = new Date(); - const msg = `\n[${date.toISOString()}] SERVER CONSOLE DEBUG: ******* Terminal not yet ready, buffering... ******\n`; + const msg = `[${date.toISOString()}] SERVER CONSOLE DEBUG: ******* Terminal not yet ready, buffering... ******`; console.log(msg); - fs.writeFileSync(this.logFd, msg); + // fs.writeFileSync(this.logFd, msg); } fs.writeFileSync(this.logFd, data.toString()); fs.fdatasyncSync(this.logFd); diff --git a/src/gdb.ts b/src/gdb.ts index 869bfac2..ae2285e3 100644 --- a/src/gdb.ts +++ b/src/gdb.ts @@ -1465,6 +1465,7 @@ export class GDBDebugSession extends DebugSession { else { this.miDebugger.once('debug-ready', process); } } + protected pendingBkptResponse = false; protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments) { const createBreakpoints = async (shouldContinue) => { this.debugReady = true; @@ -1557,9 +1558,11 @@ export class GDBDebugSession extends DebugSession { this.breakpointMap.set(args.source.path, brkpoints.filter((bp) => !(bp instanceof MIError)) as Breakpoint[]); this.sendResponse(response); + this.pendingBkptResponse = false; } catch (msg) { this.sendErrorResponse(response, 9, msg.toString()); + this.pendingBkptResponse = false; } if (shouldContinue) { @@ -1578,8 +1581,22 @@ export class GDBDebugSession extends DebugSession { } }; - if (this.debugReady) { process(); } - else { this.miDebugger.once('debug-ready', process); } + // Following will look crazy. VSCode will make this request before we have even finished + // the last one and without any user interaction either. To reproduce the problem, + // see https://github.com/Marus/cortex-debug/issues/525 + // It happens with duplicate breakpoints created by the user and one of them is deleted + // VSCode will delete the first one and then delete the other one too but in a separate + // call + let intervalTime = 0; + const to = setInterval(() => { + if (!this.pendingBkptResponse) { + clearInterval(to); + this.pendingBkptResponse = true; + if (this.debugReady) { process(); } + else { this.miDebugger.once('debug-ready', process); } + } + intervalTime = 5; + }, intervalTime); } protected isVarRefGlobalOrStatic(varRef: number, id: any) {