From f6a8dceabc4e923bc009b36d5bc4da9c055f126e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Sp=C3=B6nemann?= Date: Tue, 25 Feb 2020 15:43:21 +0100 Subject: [PATCH] Improved error detection to catch more cases --- .../arduino-debug-adapter-contribution.ts | 3 +- .../debug-adapter/arduino-debug-session.ts | 1 + .../node/debug-adapter/arduino-gdb-backend.ts | 4 +- .../src/node/debug-adapter/arduino-parser.ts | 46 +++++++++++++------ .../debug-adapter/arduino-variable-handler.ts | 2 +- 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts b/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts index 92ddf504..9f6ee487 100644 --- a/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts +++ b/arduino-debugger-extension/src/node/arduino-debug-adapter-contribution.ts @@ -70,8 +70,7 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution fqbn: '${fqbn}', uploadPort: '${port}', initCommands: [ - `-break-insert -t --function ${startFunction}`, - '-interpreter-exec console "monitor reset halt"' + `-break-insert -t --function ${startFunction}` ] } if (!res.sketch) { diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts index d6c67746..77f85ff0 100644 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts +++ b/arduino-debugger-extension/src/node/debug-adapter/arduino-debug-session.ts @@ -43,6 +43,7 @@ export class ArduinoDebugSession extends GDBDebugSession { protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise { try { + await this.gdb.sendCommand('-interpreter-exec console "monitor reset halt"') await mi.sendExecContinue(this.gdb); this.sendResponse(response); } catch (err) { diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts index c791f1a0..16cf5be6 100644 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts +++ b/arduino-debugger-extension/src/node/debug-adapter/arduino-gdb-backend.ts @@ -48,9 +48,9 @@ export class ArduinoGDBBackend extends GDBBackend { return this.sendCommand(command); } - sendStackInfoFrame(gdb: GDBBackend, threadId: number, frameId: number): Promise<{ frame: MIFrameInfo }> { + sendStackInfoFrame(threadId: number, frameId: number): Promise<{ frame: MIFrameInfo }> { const command = `-stack-info-frame --thread ${threadId} --frame ${frameId}`; - return gdb.sendCommand(command); + return this.sendCommand(command); } sendTargetDetach(): Promise { diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts index 51507da5..3922367b 100644 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts +++ b/arduino-debugger-extension/src/node/debug-adapter/arduino-parser.ts @@ -7,26 +7,35 @@ const LINE_REGEX = /(.*)(\r?\n)/; export class ArduinoParser extends MIParser { + protected rejectReady?: (error: Error) => void; + parseFull(proc: ChildProcessWithoutNullStreams): Promise { return new Promise((resolve, reject) => { + // Detect errors when the child process could not be spawned proc.on('error', reject); - let ready = false; - proc.on('exit', () => { - if (!ready) { - reject(new Error('The gdb debugger terminated unexpectedly.')); - } - }); - + // Detect hanging process (does not print command prompt or error) const timeout = setTimeout(() => { reject(new Error(`No response from gdb after ${READY_TIMEOUT} ms.`)); }, READY_TIMEOUT); + this.waitReady = () => { - ready = true; + this.rejectReady = undefined; clearTimeout(timeout); resolve(); } + this.rejectReady = (error: Error) => { + this.waitReady = undefined; + clearTimeout(timeout); + reject(error); + } + // Detect unexpected termination + proc.on('exit', () => { + if (this.rejectReady) { + this.rejectReady(new Error('The gdb debugger terminated unexpectedly.')); + } + }); this.readInputStream(proc.stdout); - this.readErrorStream(proc.stderr, reject); + this.readErrorStream(proc.stderr); }); } @@ -36,16 +45,25 @@ export class ArduinoParser extends MIParser { buff += chunk.toString(); let regexArray = LINE_REGEX.exec(buff); while (regexArray) { - this.line = regexArray[1]; + const line = regexArray[1]; + this.line = line; this.pos = 0; this.handleLine(); + // Detect error emitted as log message + if (line.startsWith('&"error') && this.rejectReady) { + this.line = line; + this.pos = 0; + this.next(); + this.rejectReady(new Error(this.handleCString() || regexArray[1])); + this.rejectReady = undefined; + } buff = buff.substring(regexArray[1].length + regexArray[2].length); regexArray = LINE_REGEX.exec(buff); } }); } - private readErrorStream(stream: Readable, reject: (reason?: any) => void) { + private readErrorStream(stream: Readable) { let buff = ''; stream.on('data', chunk => { buff += chunk.toString(); @@ -53,8 +71,10 @@ export class ArduinoParser extends MIParser { while (regexArray) { const line = regexArray[1]; this.gdb.emit('consoleStreamOutput', line + '\n', 'stderr'); - if (line.toLowerCase().startsWith('error')) { - reject(new Error(line)); + // Detect error emitted on the stderr stream + if (line.toLowerCase().startsWith('error') && this.rejectReady) { + this.rejectReady(new Error(line)); + this.rejectReady = undefined; } buff = buff.substring(regexArray[1].length + regexArray[2].length); regexArray = LINE_REGEX.exec(buff); diff --git a/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts b/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts index db3c7590..e440c946 100644 --- a/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts +++ b/arduino-debugger-extension/src/node/debug-adapter/arduino-variable-handler.ts @@ -48,7 +48,7 @@ export class ArduinoVariableHandler { async getStaticVariables(frameHandle: number): Promise { throw new Error('Static variables are not supported yet.'); const frame = this.frameHandles.get(frameHandle); - const result = await this.gdb.sendStackInfoFrame(this.gdb, frame.threadId, frame.frameId); + const result = await this.gdb.sendStackInfoFrame(frame.threadId, frame.frameId); const file = path.normalize(result.frame.file || ''); const symbolInfo: any[] = [] // this.symbolTable.getStaticVariables(file); const variables: DebugProtocol.Variable[] = [];