Skip to content

Commit 8bccf78

Browse files
committed
Add multi-process support
1 parent 628492b commit 8bccf78

File tree

5 files changed

+68
-13
lines changed

5 files changed

+68
-13
lines changed

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@
120120
"description": "Prints all GDB calls to the console",
121121
"default": false
122122
},
123+
"multiProcess": {
124+
"type": "boolean",
125+
"description": "Allow multiple process debugging",
126+
"default": false
127+
},
123128
"showDevDebugOutput": {
124129
"type": "boolean",
125130
"description": "Prints all GDB responses to the console",

src/backend/backend.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export interface IBackend {
5656
start(): Thenable<boolean>;
5757
stop();
5858
detach();
59-
interrupt(): Thenable<boolean>;
59+
interrupt(all: boolean): Thenable<boolean>;
6060
continue(): Thenable<boolean>;
6161
next(): Thenable<boolean>;
6262
step(): Thenable<boolean>;

src/backend/mi2/mi2.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,16 @@ export class MI2 extends EventEmitter implements IBackend {
200200
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
201201
if (this.prettyPrint)
202202
cmds.push(this.sendCommand("enable-pretty-printing"));
203+
if (this.multiProcess) {
204+
cmds.push(
205+
this.sendCommand("gdb-set follow-fork-mode parent"),
206+
this.sendCommand("gdb-set detach-on-fork off"),
207+
this.sendCommand("gdb-set non-stop on"),
208+
this.sendCommand("gdb-set schedule-multiple on"),
209+
210+
this.sendCommand("interpreter-exec console \"handle SIGSYS nostop noprint\"")
211+
);
212+
}
203213

204214
return cmds;
205215
}
@@ -358,7 +368,7 @@ export class MI2 extends EventEmitter implements IBackend {
358368
else if (reason == "exited-normally")
359369
this.emit("exited-normally", parsed);
360370
else if (reason == "exited") { // exit with error code != 0
361-
this.log("stderr", "Program exited with code " + parsed.record("exit-code"));
371+
this.log("stderr", "Inferior exited with code " + parsed.record("exit-code"));
362372
this.emit("exited-normally", parsed);
363373
}
364374
else {
@@ -372,6 +382,10 @@ export class MI2 extends EventEmitter implements IBackend {
372382
this.emit("thread-created", parsed);
373383
} else if (record.asyncClass == "thread-exited") {
374384
this.emit("thread-exited", parsed);
385+
} else if (record.asyncClass == "thread-group-started") {
386+
this.emit("thread-group-started", parsed);
387+
} else if (record.asyncClass == "thread-group-exited") {
388+
this.emit("thread-group-exited", parsed);
375389
}
376390
}
377391
}
@@ -434,21 +448,21 @@ export class MI2 extends EventEmitter implements IBackend {
434448
this.sendRaw("-target-detach");
435449
}
436450

437-
interrupt(): Thenable<boolean> {
451+
interrupt(all: boolean = true): Thenable<boolean> {
438452
if (trace)
439453
this.log("stderr", "interrupt");
440454
return new Promise((resolve, reject) => {
441-
this.sendCommand("exec-interrupt").then((info) => {
455+
this.sendCommand("exec-interrupt" + (all ? " --all" : "")).then((info) => {
442456
resolve(info.resultRecords.resultClass == "done");
443457
}, reject);
444458
});
445459
}
446460

447-
continue(reverse: boolean = false): Thenable<boolean> {
461+
continue(reverse: boolean = false, all: boolean = true): Thenable<boolean> {
448462
if (trace)
449463
this.log("stderr", "continue");
450464
return new Promise((resolve, reject) => {
451-
this.sendCommand("exec-continue" + (reverse ? " --reverse" : "")).then((info) => {
465+
this.sendCommand("exec-continue" + (reverse ? " --reverse" : "") + (all ? " --all" : "")).then((info) => {
452466
resolve(info.resultRecords.resultClass == "running");
453467
}, reject);
454468
});
@@ -787,6 +801,7 @@ export class MI2 extends EventEmitter implements IBackend {
787801
}
788802

789803
prettyPrint: boolean = true;
804+
multiProcess: boolean = false;
790805
printCalls: boolean;
791806
debugOutput: boolean;
792807
public procEnv: any;

src/gdb.ts

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArgum
1616
ssh: SSHArguments;
1717
valuesFormatting: ValuesFormattingMode;
1818
printCalls: boolean;
19+
multiProcess: boolean;
1920
showDevDebugOutput: boolean;
2021
}
2122

@@ -31,6 +32,7 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum
3132
ssh: SSHArguments;
3233
valuesFormatting: ValuesFormattingMode;
3334
printCalls: boolean;
35+
multiProcess: boolean;
3436
showDevDebugOutput: boolean;
3537
}
3638

@@ -58,6 +60,7 @@ class GDBDebugSession extends MI2DebugSession {
5860
this.debugReady = false;
5961
this.setValuesFormattingMode(args.valuesFormatting);
6062
this.miDebugger.printCalls = !!args.printCalls;
63+
this.miDebugger.multiProcess = !!args.multiProcess;
6164
this.miDebugger.debugOutput = !!args.showDevDebugOutput;
6265
if (args.ssh !== undefined) {
6366
if (args.ssh.forwardX11 === undefined)
@@ -126,6 +129,7 @@ class GDBDebugSession extends MI2DebugSession {
126129
this.debugReady = false;
127130
this.setValuesFormattingMode(args.valuesFormatting);
128131
this.miDebugger.printCalls = !!args.printCalls;
132+
this.miDebugger.multiProcess = !!args.multiProcess;
129133
this.miDebugger.debugOutput = !!args.showDevDebugOutput;
130134
if (args.ssh !== undefined) {
131135
if (args.ssh.forwardX11 === undefined)

src/mibase.ts

+38-7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export class MI2DebugSession extends DebugSession {
3737
protected debugReady: boolean;
3838
protected miDebugger: MI2;
3939
protected commandServer: net.Server;
40+
protected threadGroupPids = new Map<string, string>();
41+
protected threadToPid = new Map<number, string>();
4042

4143
public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false) {
4244
super(debuggerLinesStartAt1, isServer);
@@ -54,6 +56,8 @@ export class MI2DebugSession extends DebugSession {
5456
this.miDebugger.on("signal-stop", this.handlePause.bind(this));
5557
this.miDebugger.on("thread-created", this.threadCreatedEvent.bind(this));
5658
this.miDebugger.on("thread-exited", this.threadExitedEvent.bind(this));
59+
this.miDebugger.on("thread-group-started", this.threadGroupStartedEvent.bind(this));
60+
this.miDebugger.on("thread-group-exited", this.threadGroupExitedEvent.bind(this));
5761
this.sendEvent(new InitializedEvent());
5862
try {
5963
this.commandServer = net.createServer(c => {
@@ -138,16 +142,35 @@ export class MI2DebugSession extends DebugSession {
138142
}
139143

140144
protected threadCreatedEvent(info: MINode) {
141-
this.sendEvent(new ThreadEvent("started", info.record("id")));
145+
let threadId = parseInt(info.record("id"), 10);
146+
147+
let threadPid = this.threadGroupPids.get(info.record("group-id"));
148+
this.threadToPid.set(threadId, threadPid);
149+
150+
this.sendEvent(new ThreadEvent("started", threadId));
142151
}
143152

144153
protected threadExitedEvent(info: MINode) {
145-
this.sendEvent(new ThreadEvent("exited", info.record("id")));
154+
let threadId = parseInt(info.record("id"), 10);
155+
156+
this.threadToPid.delete(info.record("group-id"));
157+
158+
this.sendEvent(new ThreadEvent("exited", threadId));
159+
}
160+
161+
protected threadGroupStartedEvent(info: MINode) {
162+
this.threadGroupPids.set(info.record("id"), info.record("pid"));
146163
}
147164

148-
protected quitEvent() {
149-
this.quit = true;
150-
this.sendEvent(new TerminatedEvent());
165+
protected threadGroupExitedEvent(info: MINode) {
166+
this.threadGroupPids.delete(info.record("id"));
167+
}
168+
169+
protected quitEvent(info?: MINode) {
170+
if (this.threadGroupPids.size == 0) {
171+
this.quit = true;
172+
this.sendEvent(new TerminatedEvent());
173+
}
151174
}
152175

153176
protected launchError(err: any) {
@@ -276,7 +299,13 @@ export class MI2DebugSession extends DebugSession {
276299
if (threadName === undefined) {
277300
threadName = "<unnamed>";
278301
}
279-
response.body.threads.push(new Thread(thread.id, thread.id + ":" + threadName));
302+
if (this.threadGroupPids.size > 1) {
303+
let pid = this.threadToPid.get(thread.id);
304+
threadName = `(${pid}) ${thread.id}:${threadName}`;
305+
} else {
306+
threadName = `${thread.id}:${threadName}`;
307+
}
308+
response.body.threads.push(new Thread(thread.id, threadName));
280309
}
281310
this.sendResponse(response);
282311
});
@@ -584,7 +613,7 @@ export class MI2DebugSession extends DebugSession {
584613
}
585614
}
586615

587-
protected pauseRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
616+
protected pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments): void {
588617
this.miDebugger.interrupt().then(done => {
589618
this.sendResponse(response);
590619
}, msg => {
@@ -594,6 +623,7 @@ export class MI2DebugSession extends DebugSession {
594623

595624
protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments): void {
596625
this.miDebugger.continue(true).then(done => {
626+
response.body.allThreadsContinued = true;
597627
this.sendResponse(response);
598628
}, msg => {
599629
this.sendErrorResponse(response, 2, `Could not continue: ${msg}`);
@@ -602,6 +632,7 @@ export class MI2DebugSession extends DebugSession {
602632

603633
protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
604634
this.miDebugger.continue().then(done => {
635+
response.body.allThreadsContinued = true;
605636
this.sendResponse(response);
606637
}, msg => {
607638
this.sendErrorResponse(response, 2, `Could not continue: ${msg}`);

0 commit comments

Comments
 (0)