Skip to content

Commit d004e9d

Browse files
authored
feat: gdb SIGINT interrupt mode (#1124)
1 parent 160b76b commit d004e9d

File tree

5 files changed

+51
-9
lines changed

5 files changed

+51
-9
lines changed

debug_attributes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ If the type is marked as `{...}` it means that it is a complex item can have mul
3232
| debuggerArgs | array | Both | Additional arguments to pass to GDB command line |
3333
| device | string | Both | Target Device Identifier |
3434
| executable | string | Both | Path of executable for symbols and program information. See also `loadFiles`, `symbolFiles` |
35+
| gdbInterruptMode | string | Both | Whether GDB shall be interrupted using "exec-interrupt" (default) or by signaling "SIGINT" |
3536
| gdbPath | string | Both | This setting can be used to override the GDB path user/workspace setting for a particular launch configuration. This should be the full pathname to the executable (or name of the executable if it is in your PATH). Note that other toolchain executables with the configured prefix must still be available. |
3637
| gdbTarget | string | Both | For externally (servertype = "external") controlled GDB Servers you must specify the GDB target to connect to. This can either be a "hostname:port" combination or path to a serial port |
3738
| graphConfig | {object} | Both | Description of how graphing can be done. See our Wiki for details |

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,15 @@
754754
},
755755
"default": null
756756
},
757+
"gdbInterruptMode": {
758+
"default": "exec-interrupt",
759+
"description": "Whether GDB shall be interrupted using \"exec-interrupt\" (default) or by signaling \"SIGINT\"",
760+
"type": "string",
761+
"enum": [
762+
"exec-interrupt",
763+
"SIGINT"
764+
]
765+
},
757766
"gdbTarget": {
758767
"default": null,
759768
"description": "For externally (servertype = \"external\") controlled GDB Servers you must specify the GDB target to connect to. This can either be a \"hostname:port\" combination or path to a serial port",
@@ -1883,6 +1892,15 @@
18831892
},
18841893
"default": null
18851894
},
1895+
"gdbInterruptMode": {
1896+
"default": "exec-interrupt",
1897+
"description": "Whether GDB shall be interrupted using \"exec-interrupt\" (default) or by signaling \"SIGINT\"",
1898+
"type": "string",
1899+
"enum": [
1900+
"exec-interrupt",
1901+
"SIGINT"
1902+
]
1903+
},
18861904
"gdbTarget": {
18871905
"default": null,
18881906
"description": "For externally (servertype = \"external\") controlled GDB Servers you must specify the GDB target to connect to. This can either be a \"hostname:port\" combination or path to a serial port",

src/backend/mi2/mi2.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { posix } from 'path';
77
import * as os from 'os';
88
import { ServerConsoleLog } from '../server';
99
import { hexFormat } from '../../frontend/utils';
10-
import { ADAPTER_DEBUG_MODE } from '../../common';
10+
import { ADAPTER_DEBUG_MODE, GDBInterruptMode } from '../../common';
1111
const path = posix;
1212

1313
export interface ReadMemResults {
@@ -47,6 +47,7 @@ const trace = false;
4747

4848
export class MI2 extends EventEmitter implements IBackend {
4949
public debugOutput: ADAPTER_DEBUG_MODE;
50+
public interruptMode: GDBInterruptMode = GDBInterruptMode.EXEC_INTERRUPT;
5051
public procEnv: any;
5152
protected currentToken: number = 1;
5253
protected nextTokenComing = 1; // This will be the next token output from gdb
@@ -495,11 +496,21 @@ export class MI2 extends EventEmitter implements IBackend {
495496
if (trace) {
496497
this.log('stderr', 'interrupt ' + arg);
497498
}
498-
return new Promise((resolve, reject) => {
499-
this.sendCommand(`exec-interrupt ${arg}`).then((info) => {
500-
resolve(info.resultRecords.resultClass === 'done');
501-
}, reject);
502-
});
499+
if (this.interruptMode == GDBInterruptMode.EXEC_INTERRUPT) {
500+
return new Promise((resolve, reject) => {
501+
this.sendCommand(`exec-interrupt ${arg}`).then((info) => {
502+
resolve(info.resultRecords.resultClass === 'done');
503+
}, reject);
504+
});
505+
} else if (this.interruptMode == GDBInterruptMode.SIGINT) {
506+
if (this.process.kill('SIGINT')) {
507+
return Promise.resolve(true);
508+
} else {
509+
return Promise.reject(new Error('Could not send SIGINT to gdb'));
510+
}
511+
} else {
512+
this.log('stderr', `WARNING: Invalid GDB interrupt mode '${this.interruptMode as string}'`);
513+
}
503514
}
504515

505516
public continue(threadId: number): Thenable<boolean> {

src/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ export interface ConfigurationArguments extends DebugProtocol.LaunchRequestArgum
262262
serverpath: string;
263263
gdbPath: string;
264264
gdbServerConsolePort: number;
265+
gdbInterruptMode: GDBInterruptMode;
265266
objdumpPath: string;
266267
serverArgs: string[];
267268
serverCwd: string;
@@ -365,6 +366,11 @@ export enum CTIAction {
365366
'resume'
366367
}
367368

369+
export enum GDBInterruptMode {
370+
EXEC_INTERRUPT = 'exec-interrupt',
371+
SIGINT = 'SIGINT'
372+
}
373+
368374
export interface GDBServerController extends EventEmitter {
369375
portsNeeded: string[];
370376
name: string;

src/gdb.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,9 @@ export class GDBDebugSession extends LoggingDebugSession {
938938

939939
this.miDebugger = new MI2(gdbExePath, gdbargs);
940940
this.miDebugger.debugOutput = this.args.showDevDebugOutput;
941+
if (this.args.gdbInterruptMode) {
942+
this.miDebugger.interruptMode = this.args.gdbInterruptMode;
943+
}
941944
this.miDebugger.on('launcherror', (err) => {
942945
const msg = 'Could not start GDB process, does the program exist in filesystem?\n' + err.toString() + '\n';
943946
this.launchErrorResponse(response, 103, msg);
@@ -1001,6 +1004,9 @@ export class GDBDebugSession extends LoggingDebugSession {
10011004
liveGdb.setupEvents(mi2);
10021005
const commands = [...this.gdbInitCommands];
10031006
mi2.debugOutput = this.args.showDevDebugOutput;
1007+
if (this.args.gdbInterruptMode) {
1008+
mi2.interruptMode = this.args.gdbInterruptMode;
1009+
}
10041010
commands.push('interpreter-exec console "set stack-cache off"');
10051011
commands.push('interpreter-exec console "set remote interrupt-on-connect off"');
10061012
if (this.serverController) {
@@ -1591,7 +1597,7 @@ export class GDBDebugSession extends LoggingDebugSession {
15911597
}
15921598
});
15931599
try {
1594-
await this.miDebugger.sendCommand('exec-interrupt');
1600+
await this.miDebugger.interrupt();
15951601
} catch (e) {
15961602
// The timeout will take care of it...
15971603
this.handleMsg('log', `Could not interrupt program. Trying to end session anyways ${e}\n`);
@@ -1675,7 +1681,7 @@ export class GDBDebugSession extends LoggingDebugSession {
16751681
restartProcessing();
16761682
} else {
16771683
this.miDebugger.once('generic-stopped', restartProcessing);
1678-
this.miDebugger.sendCommand('exec-interrupt');
1684+
this.miDebugger.interrupt();
16791685
}
16801686
});
16811687
}
@@ -2158,7 +2164,7 @@ export class GDBDebugSession extends LoggingDebugSession {
21582164
this.miDebugger.once('generic-stopped', () => {
21592165
createBreakpoints();
21602166
});
2161-
this.miDebugger.sendCommand('exec-interrupt');
2167+
this.miDebugger.interrupt();
21622168
}
21632169
}
21642170

0 commit comments

Comments
 (0)