Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Commit

Permalink
Added support for remote debugging.
Browse files Browse the repository at this point in the history
  • Loading branch information
Raul Giacoman committed Mar 20, 2016
1 parent 529dddc commit ab5166b
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 72 deletions.
20 changes: 19 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@
"type": "go",
"request": "launch",
"mode": "debug",
"remotePath": "",
"port": 2345,
"host": "127.0.0.1",
"program": "${workspaceRoot}",
"env": {},
"args": []
Expand All @@ -140,7 +143,7 @@
},
"mode": {
"type": "string",
"description": "One of 'debug', 'test', 'exec'.",
"description": "One of 'debug', 'debug_remote', 'test', 'exec'.",
"default": "debug"
},
"stopOnEntry": {
Expand Down Expand Up @@ -175,6 +178,21 @@
"type": "string",
"description": "Init file, executed by the terminal client.",
"default": ""
},
"remotePath": {
"type": "string",
"description": "If remote debugging, the path to the source code on the remote machine, if different from the local machine.",
"default": ""
},
"port": {
"type": "number",
"description": "The port that the delve debugger will be listening on.",
"default": 2345
},
"host": {
"type": "string",
"description": "The host name of the machine the delve debugger will be listening on.",
"default": "127.0.0.1"
}
}
}
Expand Down
193 changes: 122 additions & 71 deletions src/debugAdapter/goDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
cwd?: string;
env?: { [key: string]: string; };
mode?: string;
remotePath?: string;
port?: number;
host?: string;
buildFlags?: string;
init?: string;
}
Expand All @@ -141,57 +144,69 @@ function log(msg?: any, ...args) {
}

class Delve {
program: string;
remotePath: string;
debugProcess: ChildProcess;
connection: Promise<RPCConnection>;
onstdout: (str: string) => void;
onstderr: (str: string) => void;

constructor(mode: string, program: string, args: string[], cwd: string, env: { [key: string]: string }, buildFlags: string, init: string) {
this.connection = new Promise((resolve, reject) => {
let serverRunning = false;
let dlv = getBinPath('dlv');
log('Using dlv at: ', dlv);
if (!existsSync(dlv)) {
return reject('Cannot find Delve debugger. Ensure it is in your `GOPATH/bin` or `PATH`.');
}
let dlvEnv: Object = null;
if (env) {
dlvEnv = {};
for (let k in process.env) {
dlvEnv[k] = process.env[k];
}
for (let k in env) {
dlvEnv[k] = env[k];
}
}
let dlvArgs = [mode || 'debug'];
if (mode === 'exec') {
dlvArgs = dlvArgs.concat([program]);
}
dlvArgs = dlvArgs.concat(['--headless=true', '--listen=127.0.0.1:2345', '--log']);
if (buildFlags) {
dlvArgs = dlvArgs.concat(['--build-flags=' + buildFlags]);
}
if (init) {
dlvArgs = dlvArgs.concat(['--init=' + init]);
}
if (args) {
dlvArgs = dlvArgs.concat(['--', ...args]);
}

let dlvCwd = dirname(program);
try {
if (lstatSync(program).isDirectory()) {
dlvCwd = program;
}
} catch (e) { }
this.debugProcess = spawn(dlv, dlvArgs, {
cwd: dlvCwd,
env: dlvEnv,
});

function connectClient() {
let client = Client.$create(2345, '127.0.0.1');
constructor(mode: string, remotePath: string, port: number, host:string, program: string, args: string[], cwd: string, env: { [key: string]: string }, buildFlags: string, init: string) {
this.program = program;
this.remotePath = remotePath;
this.connection = new Promise((resolve, reject) => {
let serverRunning = false;
if (mode === 'debug_remote') {
this.debugProcess = null;
serverRunning = true; //assume server is running when in debug_remote mode
connectClient(port, host);
}
else
{
let dlv = getBinPath('dlv');
log('Using dlv at: ', dlv);
if (!existsSync(dlv)) {
return reject('Cannot find Delve debugger. Ensure it is in your `GOPATH/bin` or `PATH`.');
}
let dlvEnv: Object = null;
if (env) {
dlvEnv = {};
for (let k in process.env) {
dlvEnv[k] = process.env[k];
}
for (let k in env) {
dlvEnv[k] = env[k];
}
}
let dlvArgs = [mode || 'debug'];
if (mode === 'exec') {
dlvArgs = dlvArgs.concat([program]);
}
dlvArgs = dlvArgs.concat(['--headless=true', '--listen=' + host + ':' + port.toString(), '--log']);
if (buildFlags) {
dlvArgs = dlvArgs.concat(['--build-flags=' + buildFlags]);
}
if (init) {
dlvArgs = dlvArgs.concat(['--init=' + init]);
}
if (args) {
dlvArgs = dlvArgs.concat(['--', ...args]);
}

let dlvCwd = dirname(program);
try {
if (lstatSync(program).isDirectory()) {
dlvCwd = program;
}
} catch (e) { }
this.debugProcess = spawn(dlv, dlvArgs, {
cwd: dlvCwd,
env: dlvEnv,
});
}

function connectClient(port: number, host: string) {
let client = Client.$create(port, host);
client.connectSocket((err, conn) => {
if (err) return reject(err);
// Add a slight delay to avoid issues on Linux with
Expand All @@ -200,27 +215,29 @@ class Delve {
resolve(conn),
200);
});
}

if (this.debugProcess != null) {
this.debugProcess.stderr.on('data', chunk => {
let str = chunk.toString();
if (this.onstderr) { this.onstderr(str); }
if (!serverRunning) {
serverRunning = true;
connectClient(port, host);
}
});
this.debugProcess.stdout.on('data', chunk => {
let str = chunk.toString();
if (this.onstdout) { this.onstdout(str); }
});
this.debugProcess.on('close', function(code) {
// TODO: Report `dlv` crash to user.
console.error('Process exiting with code: ' + code);
});
this.debugProcess.on('error', function(err) {
reject(err);
});
}

this.debugProcess.stderr.on('data', chunk => {
let str = chunk.toString();
if (this.onstderr) { this.onstderr(str); }
if (!serverRunning) {
serverRunning = true;
connectClient();
}
});
this.debugProcess.stdout.on('data', chunk => {
let str = chunk.toString();
if (this.onstdout) { this.onstdout(str); }
});
this.debugProcess.on('close', function(code) {
// TODO: Report `dlv` crash to user.
console.error('Process exiting with code: ' + code);
});
this.debugProcess.on('error', function(err) {
reject(err);
});
});
}

Expand All @@ -246,7 +263,22 @@ class Delve {
}

close() {
this.debugProcess.kill();
if (this.debugProcess === null) {
this.call<DebuggerState>('Command', [{ name: 'halt' }], (err, state) => {
if (err) {
console.error('Failed to halt.');
}
// else TODO: Can't find a way to kill remote delve.
// {
// this.call<number>('Detach', [true], (err, ret) => {
// if (err) {
// console.error('Failed to detach.');
// }
// });
// }
});
}
else this.debugProcess.kill();
}
}

Expand Down Expand Up @@ -280,7 +312,10 @@ class GoDebugSession extends DebugSession {

protected launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void {
// Launch the Delve debugger on the program
this.delve = new Delve(args.mode, args.program, args.args, args.cwd, args.env, args.buildFlags, args.init);
let remotePath = typeof args.remotePath === 'undefined' ? "":args.remotePath;
let port = typeof args.port === 'undefined' ? 2345:args.port;
let host = typeof args.host === 'undefined' ? "127.0.0.1":args.host;
this.delve = new Delve(args.mode, remotePath, port, host, args.program, args.args, args.cwd, args.env, args.buildFlags, args.init);
this.delve.onstdout = (str: string) => {
this.sendEvent(new OutputEvent(str, 'stdout'));
};
Expand Down Expand Up @@ -321,22 +356,37 @@ class GoDebugSession extends DebugSession {
this.sendResponse(response);
log('ExceptionBreakPointsResponse');
}

protected toDebuggerPath(path): string {
if (this.delve.remotePath.length === 0)
return this.convertClientPathToDebugger(path);
return path.replace(this.delve.program, this.delve.remotePath);
}

protected toLocalPath(path): string {
if (this.delve.remotePath.length === 0)
return this.convertDebuggerPathToClient(path);
return path.replace(this.delve.remotePath, this.delve.program);
}

protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void {
log('SetBreakPointsRequest');
if (!this.breakpoints.get(args.source.path)) {
this.breakpoints.set(args.source.path, []);
}
let file = args.source.path;
let remotePath = this.toDebuggerPath(file)
let existingBPs = this.breakpoints.get(file);
Promise.all(this.breakpoints.get(file).map(existingBP => {
log('Clearing: ' + existingBP.id);
return this.delve.callPromise<DebugBreakpoint>('ClearBreakpoint', [existingBP.id]);
})).then(() => {
log('All cleared');
return Promise.all(args.lines.map(line => {
log('Creating on: ' + file + ':' + line);
return this.delve.callPromise<DebugBreakpoint>('CreateBreakpoint', [{ file, line }]).catch(err => null);
if (this.delve.remotePath.length == 0)
log('Creating on: ' + file + ':' + line);
else log('Creating on: ' + file + ' (' + remotePath + ') :' + line);
return this.delve.callPromise<DebugBreakpoint>('CreateBreakpoint', [{ file: remotePath, line }]).catch(err => null);
}));
}).then(newBreakpoints => {
log('All set:' + JSON.stringify(newBreakpoints));
Expand Down Expand Up @@ -394,7 +444,8 @@ class GoDebugSession extends DebugSession {
location.function ? location.function.name : '<unknown>',
new Source(
basename(location.file),
this.convertDebuggerPathToClient(location.file)
this.toLocalPath(location.file)
//this.convertDebuggerPathToClient(location.file)
),
location.line,
0
Expand Down

0 comments on commit ab5166b

Please sign in to comment.