Attach over ssh with gdb (fix #83)

This commit is contained in:
WebFreak001 2016-12-31 15:16:12 +01:00
commit 4f8ae4eb24
6 changed files with 132 additions and 30 deletions

View file

@ -220,6 +220,66 @@
"type": "array", "type": "array",
"description": "GDB commands to run when starting to debug", "description": "GDB commands to run when starting to debug",
"default": [] "default": []
},
"ssh": {
"required": [
"host",
"cwd",
"user"
],
"type": "object",
"description": "If this is set then the extension will connect to an ssh host and run GDB there",
"properties": {
"host": {
"type": "string",
"description": "Remote host name/ip to connect to"
},
"cwd": {
"type": "string",
"description": "Path of project on the remote"
},
"port": {
"type": "number",
"description": "Remote port number",
"default": 22
},
"user": {
"type": "string",
"description": "Username to connect as"
},
"password": {
"type": "string",
"description": "Plain text password (unsafe; if possible use keyfile instead)"
},
"keyfile": {
"type": "string",
"description": "Absolute path to private key"
},
"forwardX11": {
"type": "boolean",
"description": "If true, the server will redirect x11 to the local host",
"default": true
},
"x11port": {
"type": "number",
"description": "Port to redirect X11 data to (by default port = display + 6000)",
"default": 6000
},
"x11host": {
"type": "string",
"description": "Hostname/ip to redirect X11 data to",
"default": "localhost"
},
"remotex11screen": {
"type": "number",
"description": "Screen to start the application on the remote side",
"default": 0
},
"bootstrap": {
"type": "string",
"description": "Content will be executed on the SSH host before the debugger call."
}
}
} }
} }
} }

View file

@ -38,7 +38,7 @@ export interface SSHArguments {
export interface IBackend { export interface IBackend {
load(cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any>; load(cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any>;
ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any>; ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string, attach: boolean): Thenable<any>;
attach(cwd: string, executable: string, target: string): Thenable<any>; attach(cwd: string, executable: string, target: string): Thenable<any>;
connect(cwd: string, executable: string, target: string): Thenable<any>; connect(cwd: string, executable: string, target: string): Thenable<any>;
start(): Thenable<boolean>; start(): Thenable<boolean>;

View file

@ -72,7 +72,7 @@ export class MI2 extends EventEmitter implements IBackend {
}); });
} }
ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string): Thenable<any> { ssh(args: SSHArguments, cwd: string, target: string, procArgs: string, separateConsole: string, attach: boolean): Thenable<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.isSSH = true; this.isSSH = true;
this.sshReady = false; this.sshReady = false;
@ -125,6 +125,8 @@ export class MI2 extends EventEmitter implements IBackend {
} }
let sshCMD = this.application + " " + this.preargs.join(" "); let sshCMD = this.application + " " + this.preargs.join(" ");
if (args.bootstrap) sshCMD = args.bootstrap + " && " + sshCMD; if (args.bootstrap) sshCMD = args.bootstrap + " && " + sshCMD;
if (attach)
sshCMD += " -p " + target;
this.sshConn.exec(sshCMD, execArgs, (err, stream) => { this.sshConn.exec(sshCMD, execArgs, (err, stream) => {
if (err) { if (err) {
this.log("stderr", "Could not run " + this.application + " over ssh!"); this.log("stderr", "Could not run " + this.application + " over ssh!");
@ -141,9 +143,9 @@ export class MI2 extends EventEmitter implements IBackend {
this.emit("quit"); this.emit("quit");
this.sshConn.end(); this.sshConn.end();
}).bind(this)); }).bind(this));
let promises = this.initCommands(target, cwd, true); let promises = this.initCommands(target, cwd, true, attach);
promises.push(this.sendCommand("environment-cd \"" + escape(cwd) + "\"")); promises.push(this.sendCommand("environment-cd \"" + escape(cwd) + "\""));
if (procArgs && procArgs.length) if (procArgs && procArgs.length && !attach)
promises.push(this.sendCommand("exec-arguments " + procArgs)); promises.push(this.sendCommand("exec-arguments " + procArgs));
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
this.emit("debug-ready") this.emit("debug-ready")
@ -159,7 +161,7 @@ export class MI2 extends EventEmitter implements IBackend {
}); });
} }
protected initCommands(target: string, cwd: string, ssh: boolean = false) { protected initCommands(target: string, cwd: string, ssh: boolean = false, attach: boolean = false) {
if (ssh) { if (ssh) {
if (!path.isAbsolute(target)) if (!path.isAbsolute(target))
target = path.join(cwd, target); target = path.join(cwd, target);
@ -168,11 +170,13 @@ export class MI2 extends EventEmitter implements IBackend {
if (!nativePath.isAbsolute(target)) if (!nativePath.isAbsolute(target))
target = nativePath.join(cwd, target); target = nativePath.join(cwd, target);
} }
return [ var cmds = [
this.sendCommand("gdb-set target-async on"), this.sendCommand("gdb-set target-async on", true),
this.sendCommand("environment-directory \"" + escape(cwd) + "\""), this.sendCommand("environment-directory \"" + escape(cwd) + "\"", true)
this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")
]; ];
if (!attach)
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
return cmds;
} }
attach(cwd: string, executable: string, target: string): Thenable<any> { attach(cwd: string, executable: string, target: string): Thenable<any> {
@ -651,12 +655,18 @@ export class MI2 extends EventEmitter implements IBackend {
this.process.stdin.write(raw + "\n"); this.process.stdin.write(raw + "\n");
} }
sendCommand(command: string): Thenable<MINode> { sendCommand(command: string, suppressFailure: boolean = false): Thenable<MINode> {
let sel = this.currentToken++; let sel = this.currentToken++;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.handlers[sel] = (node: MINode) => { this.handlers[sel] = (node: MINode) => {
if (node && node.resultRecords && node.resultRecords.resultClass === "error") if (node && node.resultRecords && node.resultRecords.resultClass === "error") {
reject(node.result("msg") || "Internal error"); if (suppressFailure) {
this.log("stderr", "WARNING: Error executing command '" + command + "'");
resolve(node);
}
else
reject((node.result("msg") || "Internal error") + " (from " + command + ")");
}
else else
resolve(node); resolve(node);
}; };

View file

@ -6,7 +6,7 @@ import * as nativePath from "path"
let path = posix; let path = posix;
export class MI2_LLDB extends MI2 { export class MI2_LLDB extends MI2 {
protected initCommands(target: string, cwd: string, ssh: boolean = false) { protected initCommands(target: string, cwd: string, ssh: boolean = false, attach: boolean = false) {
if (ssh) { if (ssh) {
if (!path.isAbsolute(target)) if (!path.isAbsolute(target))
target = path.join(cwd, target); target = path.join(cwd, target);
@ -15,10 +15,12 @@ export class MI2_LLDB extends MI2 {
if (!nativePath.isAbsolute(target)) if (!nativePath.isAbsolute(target))
target = nativePath.join(cwd, target); target = nativePath.join(cwd, target);
} }
return [ var cmds = [
this.sendCommand("gdb-set target-async on"), this.sendCommand("gdb-set target-async on")
this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")
]; ];
if (!attach)
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
return cmds;
} }
attach(cwd: string, executable: string, target: string): Thenable<any> { attach(cwd: string, executable: string, target: string): Thenable<any> {

View file

@ -25,6 +25,7 @@ export interface AttachRequestArguments {
executable: string; executable: string;
remote: boolean; remote: boolean;
autorun: string[]; autorun: string[];
ssh: SSHArguments;
printCalls: boolean; printCalls: boolean;
showDevDebugOutput: boolean; showDevDebugOutput: boolean;
} }
@ -67,7 +68,7 @@ class GDBDebugSession extends MI2DebugSession {
this.isSSH = true; this.isSSH = true;
this.trimCWD = args.cwd.replace(/\\/g, "/"); this.trimCWD = args.cwd.replace(/\\/g, "/");
this.switchCWD = args.ssh.cwd; this.switchCWD = args.ssh.cwd;
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, args.terminal).then(() => { this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, args.terminal, false).then(() => {
if (args.autorun) if (args.autorun)
args.autorun.forEach(command => { args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command); this.miDebugger.sendUserInput(command);
@ -120,6 +121,34 @@ class GDBDebugSession extends MI2DebugSession {
this.debugReady = false; this.debugReady = false;
this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.miDebugger.debugOutput = !!args.showDevDebugOutput;
if (args.ssh !== undefined) {
if (args.ssh.forwardX11 === undefined)
args.ssh.forwardX11 = true;
if (args.ssh.port === undefined)
args.ssh.port = 22;
if (args.ssh.x11port === undefined)
args.ssh.x11port = 6000;
if (args.ssh.x11host === undefined)
args.ssh.x11host = "localhost";
if (args.ssh.remotex11screen === undefined)
args.ssh.remotex11screen = 0;
this.isSSH = true;
this.trimCWD = args.cwd.replace(/\\/g, "/");
this.switchCWD = args.ssh.cwd;
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, "", undefined, true).then(() => {
if (args.autorun)
args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command);
});
setTimeout(() => {
this.miDebugger.emit("ui-break-done");
}, 50);
this.sendResponse(response);
}, err => {
this.sendErrorResponse(response, 102, `Failed to SSH: ${err.toString()}`)
});
}
else {
if (args.remote) { if (args.remote) {
this.miDebugger.connect(args.cwd, args.executable, args.target).then(() => { this.miDebugger.connect(args.cwd, args.executable, args.target).then(() => {
if (args.autorun) if (args.autorun)
@ -144,5 +173,6 @@ class GDBDebugSession extends MI2DebugSession {
} }
} }
} }
}
DebugSession.run(GDBDebugSession); DebugSession.run(GDBDebugSession);

View file

@ -63,7 +63,7 @@ class LLDBDebugSession extends MI2DebugSession {
this.isSSH = true; this.isSSH = true;
this.trimCWD = args.cwd.replace(/\\/g, "/"); this.trimCWD = args.cwd.replace(/\\/g, "/");
this.switchCWD = args.ssh.cwd; this.switchCWD = args.ssh.cwd;
this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, undefined).then(() => { this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, undefined, false).then(() => {
if (args.autorun) if (args.autorun)
args.autorun.forEach(command => { args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command); this.miDebugger.sendUserInput(command);