Skip to content

Commit 4f8ae4e

Browse files
author
WebFreak001
committed
Attach over ssh with gdb (fix WebFreak001#83)
1 parent c9be6b0 commit 4f8ae4e

File tree

6 files changed

+132
-30
lines changed

6 files changed

+132
-30
lines changed

package.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,66 @@
220220
"type": "array",
221221
"description": "GDB commands to run when starting to debug",
222222
"default": []
223+
},
224+
"ssh": {
225+
"required": [
226+
"host",
227+
"cwd",
228+
"user"
229+
],
230+
"type": "object",
231+
"description": "If this is set then the extension will connect to an ssh host and run GDB there",
232+
"properties": {
233+
"host": {
234+
"type": "string",
235+
"description": "Remote host name/ip to connect to"
236+
},
237+
"cwd": {
238+
"type": "string",
239+
"description": "Path of project on the remote"
240+
},
241+
"port": {
242+
"type": "number",
243+
"description": "Remote port number",
244+
"default": 22
245+
},
246+
"user": {
247+
"type": "string",
248+
"description": "Username to connect as"
249+
},
250+
"password": {
251+
"type": "string",
252+
"description": "Plain text password (unsafe; if possible use keyfile instead)"
253+
},
254+
"keyfile": {
255+
"type": "string",
256+
"description": "Absolute path to private key"
257+
},
258+
"forwardX11": {
259+
"type": "boolean",
260+
"description": "If true, the server will redirect x11 to the local host",
261+
"default": true
262+
},
263+
"x11port": {
264+
"type": "number",
265+
"description": "Port to redirect X11 data to (by default port = display + 6000)",
266+
"default": 6000
267+
},
268+
"x11host": {
269+
"type": "string",
270+
"description": "Hostname/ip to redirect X11 data to",
271+
"default": "localhost"
272+
},
273+
"remotex11screen": {
274+
"type": "number",
275+
"description": "Screen to start the application on the remote side",
276+
"default": 0
277+
},
278+
"bootstrap": {
279+
"type": "string",
280+
"description": "Content will be executed on the SSH host before the debugger call."
281+
}
282+
}
223283
}
224284
}
225285
}

src/backend/backend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface SSHArguments {
3838

3939
export interface IBackend {
4040
load(cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any>;
41-
ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any>;
41+
ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string, attach: boolean): Thenable<any>;
4242
attach(cwd: string, executable: string, target: string): Thenable<any>;
4343
connect(cwd: string, executable: string, target: string): Thenable<any>;
4444
start(): Thenable<boolean>;

src/backend/mi2/mi2.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class MI2 extends EventEmitter implements IBackend {
7272
});
7373
}
7474

75-
ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any> {
75+
ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string, attach: boolean): Thenable<any> {
7676
return new Promise((resolve, reject) => {
7777
this.isSSH = true;
7878
this.sshReady = false;
@@ -125,6 +125,8 @@ export class MI2 extends EventEmitter implements IBackend {
125125
}
126126
let sshCMD = this.application + " " + this.preargs.join(" ");
127127
if (args.bootstrap) sshCMD = args.bootstrap + " && " + sshCMD;
128+
if (attach)
129+
sshCMD += " -p " + target;
128130
this.sshConn.exec(sshCMD, execArgs, (err, stream) => {
129131
if (err) {
130132
this.log("stderr", "Could not run " + this.application + " over ssh!");
@@ -141,9 +143,9 @@ export class MI2 extends EventEmitter implements IBackend {
141143
this.emit("quit");
142144
this.sshConn.end();
143145
}).bind(this));
144-
let promises = this.initCommands(target, cwd, true);
146+
let promises = this.initCommands(target, cwd, true, attach);
145147
promises.push(this.sendCommand("environment-cd \"" + escape(cwd) + "\""));
146-
if (procArgs && procArgs.length)
148+
if (procArgs && procArgs.length && !attach)
147149
promises.push(this.sendCommand("exec-arguments " + procArgs));
148150
Promise.all(promises).then(() => {
149151
this.emit("debug-ready")
@@ -159,7 +161,7 @@ export class MI2 extends EventEmitter implements IBackend {
159161
});
160162
}
161163

162-
protected initCommands(target: string, cwd: string, ssh: boolean = false) {
164+
protected initCommands(target: string, cwd: string, ssh: boolean = false, attach: boolean = false) {
163165
if (ssh) {
164166
if (!path.isAbsolute(target))
165167
target = path.join(cwd, target);
@@ -168,11 +170,13 @@ export class MI2 extends EventEmitter implements IBackend {
168170
if (!nativePath.isAbsolute(target))
169171
target = nativePath.join(cwd, target);
170172
}
171-
return [
172-
this.sendCommand("gdb-set target-async on"),
173-
this.sendCommand("environment-directory \"" + escape(cwd) + "\""),
174-
this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")
173+
var cmds = [
174+
this.sendCommand("gdb-set target-async on", true),
175+
this.sendCommand("environment-directory \"" + escape(cwd) + "\"", true)
175176
];
177+
if (!attach)
178+
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
179+
return cmds;
176180
}
177181

178182
attach(cwd: string, executable: string, target: string): Thenable<any> {
@@ -651,12 +655,18 @@ export class MI2 extends EventEmitter implements IBackend {
651655
this.process.stdin.write(raw + "\n");
652656
}
653657

654-
sendCommand(command: string): Thenable<MINode> {
658+
sendCommand(command: string, suppressFailure: boolean = false): Thenable<MINode> {
655659
let sel = this.currentToken++;
656660
return new Promise((resolve, reject) => {
657661
this.handlers[sel] = (node: MINode) => {
658-
if (node && node.resultRecords && node.resultRecords.resultClass === "error")
659-
reject(node.result("msg") || "Internal error");
662+
if (node && node.resultRecords && node.resultRecords.resultClass === "error") {
663+
if (suppressFailure) {
664+
this.log("stderr", "WARNING: Error executing command '" + command + "'");
665+
resolve(node);
666+
}
667+
else
668+
reject((node.result("msg") || "Internal error") + " (from " + command + ")");
669+
}
660670
else
661671
resolve(node);
662672
};

src/backend/mi2/mi2lldb.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as nativePath from "path"
66
let path = posix;
77

88
export class MI2_LLDB extends MI2 {
9-
protected initCommands(target: string, cwd: string, ssh: boolean = false) {
9+
protected initCommands(target: string, cwd: string, ssh: boolean = false, attach: boolean = false) {
1010
if (ssh) {
1111
if (!path.isAbsolute(target))
1212
target = path.join(cwd, target);
@@ -15,10 +15,12 @@ export class MI2_LLDB extends MI2 {
1515
if (!nativePath.isAbsolute(target))
1616
target = nativePath.join(cwd, target);
1717
}
18-
return [
19-
this.sendCommand("gdb-set target-async on"),
20-
this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")
18+
var cmds = [
19+
this.sendCommand("gdb-set target-async on")
2120
];
21+
if (!attach)
22+
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
23+
return cmds;
2224
}
2325

2426
attach(cwd: string, executable: string, target: string): Thenable<any> {

src/gdb.ts

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface AttachRequestArguments {
2525
executable: string;
2626
remote: boolean;
2727
autorun: string[];
28+
ssh: SSHArguments;
2829
printCalls: boolean;
2930
showDevDebugOutput: boolean;
3031
}
@@ -67,7 +68,7 @@ class GDBDebugSession extends MI2DebugSession {
6768
this.isSSH = true;
6869
this.trimCWD = args.cwd.replace(/\\/g, "/");
6970
this.switchCWD = args.ssh.cwd;
70-
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, args.terminal).then(() => {
71+
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, args.terminal, false).then(() => {
7172
if (args.autorun)
7273
args.autorun.forEach(command => {
7374
this.miDebugger.sendUserInput(command);
@@ -120,27 +121,56 @@ class GDBDebugSession extends MI2DebugSession {
120121
this.debugReady = false;
121122
this.miDebugger.printCalls = !!args.printCalls;
122123
this.miDebugger.debugOutput = !!args.showDevDebugOutput;
123-
if (args.remote) {
124-
this.miDebugger.connect(args.cwd, args.executable, args.target).then(() => {
124+
if (args.ssh !== undefined) {
125+
if (args.ssh.forwardX11 === undefined)
126+
args.ssh.forwardX11 = true;
127+
if (args.ssh.port === undefined)
128+
args.ssh.port = 22;
129+
if (args.ssh.x11port === undefined)
130+
args.ssh.x11port = 6000;
131+
if (args.ssh.x11host === undefined)
132+
args.ssh.x11host = "localhost";
133+
if (args.ssh.remotex11screen === undefined)
134+
args.ssh.remotex11screen = 0;
135+
this.isSSH = true;
136+
this.trimCWD = args.cwd.replace(/\\/g, "/");
137+
this.switchCWD = args.ssh.cwd;
138+
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, "", undefined, true).then(() => {
125139
if (args.autorun)
126140
args.autorun.forEach(command => {
127141
this.miDebugger.sendUserInput(command);
128142
});
143+
setTimeout(() => {
144+
this.miDebugger.emit("ui-break-done");
145+
}, 50);
129146
this.sendResponse(response);
130147
}, err => {
131-
this.sendErrorResponse(response, 102, `Failed to attach: ${err.toString()}`)
148+
this.sendErrorResponse(response, 102, `Failed to SSH: ${err.toString()}`)
132149
});
133150
}
134151
else {
135-
this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => {
136-
if (args.autorun)
137-
args.autorun.forEach(command => {
138-
this.miDebugger.sendUserInput(command);
139-
});
140-
this.sendResponse(response);
141-
}, err => {
142-
this.sendErrorResponse(response, 101, `Failed to attach: ${err.toString()}`)
143-
});
152+
if (args.remote) {
153+
this.miDebugger.connect(args.cwd, args.executable, args.target).then(() => {
154+
if (args.autorun)
155+
args.autorun.forEach(command => {
156+
this.miDebugger.sendUserInput(command);
157+
});
158+
this.sendResponse(response);
159+
}, err => {
160+
this.sendErrorResponse(response, 102, `Failed to attach: ${err.toString()}`)
161+
});
162+
}
163+
else {
164+
this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => {
165+
if (args.autorun)
166+
args.autorun.forEach(command => {
167+
this.miDebugger.sendUserInput(command);
168+
});
169+
this.sendResponse(response);
170+
}, err => {
171+
this.sendErrorResponse(response, 101, `Failed to attach: ${err.toString()}`)
172+
});
173+
}
144174
}
145175
}
146176
}

src/lldb.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class LLDBDebugSession extends MI2DebugSession {
6363
this.isSSH = true;
6464
this.trimCWD = args.cwd.replace(/\\/g, "/");
6565
this.switchCWD = args.ssh.cwd;
66-
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, undefined).then(() => {
66+
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, undefined, false).then(() => {
6767
if (args.autorun)
6868
args.autorun.forEach(command => {
6969
this.miDebugger.sendUserInput(command);

0 commit comments

Comments
 (0)