Skip to content

Commit 5ccec73

Browse files
LeszekSwirskiJulianvDoorn
authored andcommitted
Add multi-process support
1 parent c1bef0a commit 5ccec73

5 files changed

Lines changed: 71 additions & 15 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@
204204
"description": "Prints all GDB calls to the console",
205205
"default": false
206206
},
207+
"multiProcess": {
208+
"type": "boolean",
209+
"description": "Allow multiple process debugging",
210+
"default": false
211+
},
207212
"showDevDebugOutput": {
208213
"type": "boolean",
209214
"description": "Prints all GDB responses to the console",

src/backend/backend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export interface IBackend {
6464
start(runToStart: boolean): Thenable<boolean>;
6565
stop(): void;
6666
detach(): void;
67-
interrupt(): Thenable<boolean>;
67+
interrupt(all: boolean): Thenable<boolean>;
6868
continue(): Thenable<boolean>;
6969
next(): Thenable<boolean>;
7070
step(): Thenable<boolean>;

src/backend/mi2/mi2.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,16 @@ export class MI2 extends EventEmitter implements IBackend {
269269
cmds.push(this.sendCommand("enable-pretty-printing"));
270270
if (this.frameFilters)
271271
cmds.push(this.sendCommand("enable-frame-filters"));
272+
if (this.multiProcess) {
273+
cmds.push(
274+
this.sendCommand("gdb-set follow-fork-mode parent"),
275+
this.sendCommand("gdb-set detach-on-fork off"),
276+
this.sendCommand("gdb-set non-stop on"),
277+
this.sendCommand("gdb-set schedule-multiple on"),
278+
279+
this.sendCommand("interpreter-exec console \"handle SIGSYS nostop noprint\"")
280+
);
281+
}
272282
for (const cmd of this.extraCommands) {
273283
cmds.push(this.sendCommand(cmd));
274284
}
@@ -462,7 +472,7 @@ export class MI2 extends EventEmitter implements IBackend {
462472
this.emit("exited-normally", parsed);
463473
break;
464474
case "exited": // exit with error code != 0
465-
this.log("stderr", "Program exited with code " + parsed.record("exit-code"));
475+
this.log("stderr", "Inferior exited with code " + parsed.record("exit-code"));
466476
this.emit("exited-normally", parsed);
467477
break;
468478
// case "exited-signalled": // consider handling that explicit possible
@@ -483,6 +493,10 @@ export class MI2 extends EventEmitter implements IBackend {
483493
this.emit("thread-created", parsed);
484494
} else if (record.asyncClass === "thread-exited") {
485495
this.emit("thread-exited", parsed);
496+
} else if (record.asyncClass == "thread-group-started") {
497+
this.emit("thread-group-started", parsed);
498+
} else if (record.asyncClass == "thread-group-exited") {
499+
this.emit("thread-group-exited", parsed);
486500
}
487501
}
488502
}
@@ -546,21 +560,21 @@ export class MI2 extends EventEmitter implements IBackend {
546560
this.sendRaw("-target-detach");
547561
}
548562

549-
interrupt(): Thenable<boolean> {
563+
interrupt(all: boolean = true): Thenable<boolean> {
550564
if (trace)
551565
this.log("stderr", "interrupt");
552566
return new Promise((resolve, reject) => {
553-
this.sendCommand("exec-interrupt").then((info) => {
567+
this.sendCommand("exec-interrupt" + (all ? " --all" : "")).then((info) => {
554568
resolve(info.resultRecords.resultClass === "done");
555569
}, reject);
556570
});
557571
}
558572

559-
continue(reverse: boolean = false): Thenable<boolean> {
573+
continue(reverse: boolean = false, all: boolean = true): Thenable<boolean> {
560574
if (trace)
561575
this.log("stderr", "continue");
562576
return new Promise((resolve, reject) => {
563-
this.sendCommand("exec-continue" + (reverse ? " --reverse" : "")).then((info) => {
577+
this.sendCommand("exec-continue" + (reverse ? " --reverse" : "") + (all ? " --all" : "")).then((info) => {
564578
resolve(info.resultRecords.resultClass === "running");
565579
}, reject);
566580
});
@@ -1023,6 +1037,7 @@ export class MI2 extends EventEmitter implements IBackend {
10231037

10241038
prettyPrint: boolean = true;
10251039
frameFilters: boolean = true;
1040+
multiProcess: boolean = false;
10261041
printCalls: boolean;
10271042
debugOutput: boolean;
10281043
features: string[];

src/gdb.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArgum
1919
valuesFormatting: ValuesFormattingMode;
2020
frameFilters: boolean;
2121
printCalls: boolean;
22+
multiProcess: boolean;
2223
showDevDebugOutput: boolean;
2324
registerLimit: string;
2425
}
@@ -39,6 +40,7 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum
3940
valuesFormatting: ValuesFormattingMode;
4041
frameFilters: boolean;
4142
printCalls: boolean;
43+
multiProcess: boolean;
4244
showDevDebugOutput: boolean;
4345
registerLimit: string;
4446
}
@@ -75,6 +77,7 @@ class GDBDebugSession extends MI2DebugSession {
7577
this.setValuesFormattingMode(args.valuesFormatting);
7678
this.miDebugger.frameFilters = !!args.frameFilters;
7779
this.miDebugger.printCalls = !!args.printCalls;
80+
this.miDebugger.multiProcess = !!args.multiProcess;
7881
this.miDebugger.debugOutput = !!args.showDevDebugOutput;
7982
this.stopAtEntry = args.stopAtEntry;
8083
this.miDebugger.registerLimit = args.registerLimit ?? "";
@@ -121,6 +124,7 @@ class GDBDebugSession extends MI2DebugSession {
121124
this.setValuesFormattingMode(args.valuesFormatting);
122125
this.miDebugger.frameFilters = !!args.frameFilters;
123126
this.miDebugger.printCalls = !!args.printCalls;
127+
this.miDebugger.multiProcess = !!args.multiProcess;
124128
this.miDebugger.debugOutput = !!args.showDevDebugOutput;
125129
this.stopAtEntry = args.stopAtEntry;
126130
this.miDebugger.registerLimit = args.registerLimit ?? "";

src/mibase.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export class MI2DebugSession extends DebugSession {
4444
protected miDebugger: MI2;
4545
protected commandServer: net.Server;
4646
protected serverPath: string;
47+
protected threadGroupPids = new Map<string, string>();
48+
protected threadToPid = new Map<number, string>();
4749

4850
public constructor(debuggerLinesStartAt1: boolean, isServer: boolean = false) {
4951
super(debuggerLinesStartAt1, isServer);
@@ -64,6 +66,9 @@ export class MI2DebugSession extends DebugSession {
6466
this.miDebugger.on("thread-created", this.threadCreatedEvent.bind(this));
6567
this.miDebugger.on("thread-exited", this.threadExitedEvent.bind(this));
6668
this.miDebugger.once("debug-ready", (() => this.sendEvent(new InitializedEvent())));
69+
this.miDebugger.on("thread-group-started", this.threadGroupStartedEvent.bind(this));
70+
this.miDebugger.on("thread-group-exited", this.threadGroupExitedEvent.bind(this));
71+
this.sendEvent(new InitializedEvent());
6772
try {
6873
this.commandServer = net.createServer(c => {
6974
c.on("data", data => {
@@ -164,22 +169,41 @@ export class MI2DebugSession extends DebugSession {
164169
}
165170

166171
protected threadCreatedEvent(info: MINode) {
167-
this.sendEvent(new ThreadEvent("started", info.record("id")));
172+
let threadId = parseInt(info.record("id"), 10);
173+
174+
let threadPid = this.threadGroupPids.get(info.record("group-id"));
175+
this.threadToPid.set(threadId, threadPid);
176+
177+
this.sendEvent(new ThreadEvent("started", threadId));
168178
}
169179

170180
protected threadExitedEvent(info: MINode) {
171-
this.sendEvent(new ThreadEvent("exited", info.record("id")));
181+
let threadId = parseInt(info.record("id"), 10);
182+
183+
this.threadToPid.delete(info.record("group-id"));
184+
185+
this.sendEvent(new ThreadEvent("exited", threadId));
186+
}
187+
188+
protected threadGroupStartedEvent(info: MINode) {
189+
this.threadGroupPids.set(info.record("id"), info.record("pid"));
190+
}
191+
192+
protected threadGroupExitedEvent(info: MINode) {
193+
this.threadGroupPids.delete(info.record("id"));
172194
}
173195

174-
protected quitEvent() {
175-
this.quit = true;
176-
this.sendEvent(new TerminatedEvent());
196+
protected quitEvent(info?: MINode) {
197+
if (this.threadGroupPids.size == 0) {
198+
this.quit = true;
199+
this.sendEvent(new TerminatedEvent());
177200

178-
if (this.serverPath)
179-
fs.unlink(this.serverPath, (err) => {
201+
if (this.serverPath)
202+
fs.unlink(this.serverPath, (err) => {
180203
// eslint-disable-next-line no-console
181204
console.error("Failed to unlink debug server");
182-
});
205+
});
206+
}
183207
}
184208

185209
protected launchError(err: any) {
@@ -286,7 +310,13 @@ export class MI2DebugSession extends DebugSession {
286310
};
287311
for (const thread of threads) {
288312
const threadName = thread.name || thread.targetId || "<unnamed>";
289-
response.body.threads.push(new Thread(thread.id, thread.id + ":" + threadName));
313+
314+
if (this.threadGroupPids.size > 1) {
315+
let pid = this.threadToPid.get(thread.id);
316+
response.body.threads.push(new Thread(thread.id, `(${pid}) ${thread.id}:${threadName}`));
317+
} else {
318+
response.body.threads.push(new Thread(thread.id, `${thread.id}:${threadName}`));
319+
}
290320
}
291321
this.sendResponse(response);
292322
}).catch((error: MIError) => {
@@ -672,6 +702,7 @@ export class MI2DebugSession extends DebugSession {
672702

673703
protected override reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments): void {
674704
this.miDebugger.continue(true).then(done => {
705+
response.body.allThreadsContinued = true;
675706
this.sendResponse(response);
676707
}, msg => {
677708
this.sendErrorResponse(response, 2, `Could not continue: ${msg}`);
@@ -680,6 +711,7 @@ export class MI2DebugSession extends DebugSession {
680711

681712
protected override continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
682713
this.miDebugger.continue().then(done => {
714+
response.body.allThreadsContinued = true;
683715
this.sendResponse(response);
684716
}, msg => {
685717
this.sendErrorResponse(response, 2, `Could not continue: ${msg}`);

0 commit comments

Comments
 (0)