Improved error detection to catch more cases

This commit is contained in:
Miro Spönemann 2020-02-25 15:43:21 +01:00
parent 3ed72de810
commit f6a8dceabc
5 changed files with 38 additions and 18 deletions

View File

@ -70,8 +70,7 @@ export class ArduinoDebugAdapterContribution implements DebugAdapterContribution
fqbn: '${fqbn}', fqbn: '${fqbn}',
uploadPort: '${port}', uploadPort: '${port}',
initCommands: [ initCommands: [
`-break-insert -t --function ${startFunction}`, `-break-insert -t --function ${startFunction}`
'-interpreter-exec console "monitor reset halt"'
] ]
} }
if (!res.sketch) { if (!res.sketch) {

View File

@ -43,6 +43,7 @@ export class ArduinoDebugSession extends GDBDebugSession {
protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise<void> { protected async configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): Promise<void> {
try { try {
await this.gdb.sendCommand('-interpreter-exec console "monitor reset halt"')
await mi.sendExecContinue(this.gdb); await mi.sendExecContinue(this.gdb);
this.sendResponse(response); this.sendResponse(response);
} catch (err) { } catch (err) {

View File

@ -48,9 +48,9 @@ export class ArduinoGDBBackend extends GDBBackend {
return this.sendCommand(command); 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}`; const command = `-stack-info-frame --thread ${threadId} --frame ${frameId}`;
return gdb.sendCommand(command); return this.sendCommand(command);
} }
sendTargetDetach(): Promise<void> { sendTargetDetach(): Promise<void> {

View File

@ -7,26 +7,35 @@ const LINE_REGEX = /(.*)(\r?\n)/;
export class ArduinoParser extends MIParser { export class ArduinoParser extends MIParser {
protected rejectReady?: (error: Error) => void;
parseFull(proc: ChildProcessWithoutNullStreams): Promise<void> { parseFull(proc: ChildProcessWithoutNullStreams): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Detect errors when the child process could not be spawned
proc.on('error', reject); proc.on('error', reject);
let ready = false; // Detect hanging process (does not print command prompt or error)
proc.on('exit', () => {
if (!ready) {
reject(new Error('The gdb debugger terminated unexpectedly.'));
}
});
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
reject(new Error(`No response from gdb after ${READY_TIMEOUT} ms.`)); reject(new Error(`No response from gdb after ${READY_TIMEOUT} ms.`));
}, READY_TIMEOUT); }, READY_TIMEOUT);
this.waitReady = () => { this.waitReady = () => {
ready = true; this.rejectReady = undefined;
clearTimeout(timeout); clearTimeout(timeout);
resolve(); 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.readInputStream(proc.stdout);
this.readErrorStream(proc.stderr, reject); this.readErrorStream(proc.stderr);
}); });
} }
@ -36,16 +45,25 @@ export class ArduinoParser extends MIParser {
buff += chunk.toString(); buff += chunk.toString();
let regexArray = LINE_REGEX.exec(buff); let regexArray = LINE_REGEX.exec(buff);
while (regexArray) { while (regexArray) {
this.line = regexArray[1]; const line = regexArray[1];
this.line = line;
this.pos = 0; this.pos = 0;
this.handleLine(); 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); buff = buff.substring(regexArray[1].length + regexArray[2].length);
regexArray = LINE_REGEX.exec(buff); regexArray = LINE_REGEX.exec(buff);
} }
}); });
} }
private readErrorStream(stream: Readable, reject: (reason?: any) => void) { private readErrorStream(stream: Readable) {
let buff = ''; let buff = '';
stream.on('data', chunk => { stream.on('data', chunk => {
buff += chunk.toString(); buff += chunk.toString();
@ -53,8 +71,10 @@ export class ArduinoParser extends MIParser {
while (regexArray) { while (regexArray) {
const line = regexArray[1]; const line = regexArray[1];
this.gdb.emit('consoleStreamOutput', line + '\n', 'stderr'); this.gdb.emit('consoleStreamOutput', line + '\n', 'stderr');
if (line.toLowerCase().startsWith('error')) { // Detect error emitted on the stderr stream
reject(new Error(line)); if (line.toLowerCase().startsWith('error') && this.rejectReady) {
this.rejectReady(new Error(line));
this.rejectReady = undefined;
} }
buff = buff.substring(regexArray[1].length + regexArray[2].length); buff = buff.substring(regexArray[1].length + regexArray[2].length);
regexArray = LINE_REGEX.exec(buff); regexArray = LINE_REGEX.exec(buff);

View File

@ -48,7 +48,7 @@ export class ArduinoVariableHandler {
async getStaticVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> { async getStaticVariables(frameHandle: number): Promise<DebugProtocol.Variable[]> {
throw new Error('Static variables are not supported yet.'); throw new Error('Static variables are not supported yet.');
const frame = this.frameHandles.get(frameHandle); 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 file = path.normalize(result.frame.file || '');
const symbolInfo: any[] = [] // this.symbolTable.getStaticVariables(file); const symbolInfo: any[] = [] // this.symbolTable.getStaticVariables(file);
const variables: DebugProtocol.Variable[] = []; const variables: DebugProtocol.Variable[] = [];