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",
"description": "GDB commands to run when starting to debug",
"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 {
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>;
connect(cwd: string, executable: string, target: string): Thenable<any>;
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) => {
this.isSSH = true;
this.sshReady = false;
@ -125,6 +125,8 @@ export class MI2 extends EventEmitter implements IBackend {
}
let sshCMD = this.application + " " + this.preargs.join(" ");
if (args.bootstrap) sshCMD = args.bootstrap + " && " + sshCMD;
if (attach)
sshCMD += " -p " + target;
this.sshConn.exec(sshCMD, execArgs, (err, stream) => {
if (err) {
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.sshConn.end();
}).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) + "\""));
if (procArgs && procArgs.length)
if (procArgs && procArgs.length && !attach)
promises.push(this.sendCommand("exec-arguments " + procArgs));
Promise.all(promises).then(() => {
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 (!path.isAbsolute(target))
target = path.join(cwd, target);
@ -168,11 +170,13 @@ export class MI2 extends EventEmitter implements IBackend {
if (!nativePath.isAbsolute(target))
target = nativePath.join(cwd, target);
}
return [
this.sendCommand("gdb-set target-async on"),
this.sendCommand("environment-directory \"" + escape(cwd) + "\""),
this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")
var cmds = [
this.sendCommand("gdb-set target-async on", true),
this.sendCommand("environment-directory \"" + escape(cwd) + "\"", true)
];
if (!attach)
cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\""));
return cmds;
}
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");
}
sendCommand(command: string): Thenable<MINode> {
sendCommand(command: string, suppressFailure: boolean = false): Thenable<MINode> {
let sel = this.currentToken++;
return new Promise((resolve, reject) => {
this.handlers[sel] = (node: MINode) => {
if (node && node.resultRecords && node.resultRecords.resultClass === "error")
reject(node.result("msg") || "Internal error");
if (node && node.resultRecords && node.resultRecords.resultClass === "error") {
if (suppressFailure) {
this.log("stderr", "WARNING: Error executing command '" + command + "'");
resolve(node);
}
else
reject((node.result("msg") || "Internal error") + " (from " + command + ")");
}
else
resolve(node);
};

View file

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

View file

@ -25,6 +25,7 @@ export interface AttachRequestArguments {
executable: string;
remote: boolean;
autorun: string[];
ssh: SSHArguments;
printCalls: boolean;
showDevDebugOutput: boolean;
}
@ -67,7 +68,7 @@ class GDBDebugSession extends MI2DebugSession {
this.isSSH = true;
this.trimCWD = args.cwd.replace(/\\/g, "/");
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)
args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command);
@ -120,27 +121,56 @@ class GDBDebugSession extends MI2DebugSession {
this.debugReady = false;
this.miDebugger.printCalls = !!args.printCalls;
this.miDebugger.debugOutput = !!args.showDevDebugOutput;
if (args.remote) {
this.miDebugger.connect(args.cwd, args.executable, args.target).then(() => {
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 attach: ${err.toString()}`)
this.sendErrorResponse(response, 102, `Failed to SSH: ${err.toString()}`)
});
}
else {
this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => {
if (args.autorun)
args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command);
});
this.sendResponse(response);
}, err => {
this.sendErrorResponse(response, 101, `Failed to attach: ${err.toString()}`)
});
if (args.remote) {
this.miDebugger.connect(args.cwd, args.executable, args.target).then(() => {
if (args.autorun)
args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command);
});
this.sendResponse(response);
}, err => {
this.sendErrorResponse(response, 102, `Failed to attach: ${err.toString()}`)
});
}
else {
this.miDebugger.attach(args.cwd, args.executable, args.target).then(() => {
if (args.autorun)
args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command);
});
this.sendResponse(response);
}, err => {
this.sendErrorResponse(response, 101, `Failed to attach: ${err.toString()}`)
});
}
}
}
}

View file

@ -63,7 +63,7 @@ class LLDBDebugSession extends MI2DebugSession {
this.isSSH = true;
this.trimCWD = args.cwd.replace(/\\/g, "/");
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)
args.autorun.forEach(command => {
this.miDebugger.sendUserInput(command);