Skip to content

Commit

Permalink
support debugPort option
Browse files Browse the repository at this point in the history
With this option, we can specify TCP/IP host:port or UNIX domain
socket name. This is mandatory for Windows platform because
(default) UNIX domain socket is not work on it.
  • Loading branch information
ko1 committed Mar 24, 2022
1 parent b707ae7 commit 910c647
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 42 deletions.
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ See also: [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/e

For developers: `RUBY_DEBUG_DAP_SHOW_PROTOCOL=1` on `rdbg` terminal will show the all DAP protocol.

### Configuration
### Launch with configurations

You can write your favorite setting in `.vscode/launch.json`.

Expand Down Expand Up @@ -74,7 +74,8 @@ To make a `.vscode/launch.json` with default settings, you only need to click "c
}
```

It contains "Debug current file with rdbg" (launch) configuration and "Attach with rdbg" (attach) configuration.You can modify this configuration, and also you can add your favorite configuration like:
It contains "Debug current file with rdbg" (launch) configuration and "Attach with rdbg" (attach) configuration.
You can modify this configuration, and also you can add your favorite configuration like:

```JSON
{
Expand All @@ -97,19 +98,35 @@ You can use the following "launch" configurations.
* `env`: Additional environment variables to pass to the debugging (and debugged) process.
* `useBundler`: Execute Ruby programs with `bundle exec` if `command` configuration is not given and `Gemfile` is available in the workspace.
* `askParameters`: Ask "Debug command line" before debugging (default: `true`)
* `rdbgPath`: Location of the rdbg executable.
* `rdbgPath`: Location of the rdbg executable (default: `rdbg`).
* `debugPort`: On default, open a UNIX Domain Socket with default name to communicate with debuggee. If you want to use another debug port, set this configuration.
* `12345`: open a TCP/IP debug port with port `12345`
* `host:12345`: open a TCP/IP port `12345` and hostname `host`
* Otherwize, open a UNIX Domain socket with given filename.
* `launchWiatTime`: If you want to open TCP/IP debug port, you may need to wait for opening debug port. On default, it waits 1000 milli seconds (1 sec) but if it is not enough, please specify more wait time (default: `1000` in milli seconds).

### Attach configuration
### Attach to the running Ruby process

You can attach to a Ruby process which run with opening debugger port.
You can attach to a Ruby process which run with an opening debugger port.

The following command starts the `foo.rb` with opening debug port. There are more methods to open the port. See more for [ruby/debug: Debugging functionality for Ruby](https://github.com/ruby/debug).

```shell
$ rdbg --open foo.rb
```

After that, you can connect to the debug port. This extension searches opening debugger port and attach to that port by running "Attach with rdbg" (select it on the top of "RUN AND DEBUG" pane and push the green "Start Debugging" button).
After that, you can connect to the debug port. This extension searches opening debugger port and attach to that port by running `Attach with rdbg` (select it on the top of "RUN AND DEBUG" pane and push the green "Start Debugging" button).

You can specify the following "attach" configrations.

* `rdbgPath`: Same as `launch` request.
* `debugPort`: Same as `launch` request.

With `debugPort`, you can attach to TCP/IP debug port.

* Start with a TCP/IP debug port with `rdbg --open --port 12345 foo.rb`
* Add `debugPort: '12345'` attach configration.
* Choose `Attach with rdbg` and start attach debugging

## Acknowledgement

Expand Down
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@
"askParameters": {
"type": "boolean",
"description": "Ask parameters at first."
},
"debugPort": {
"type": "string",
"description": "UNIX domain socket name or TPC/IP host:port"
},
"waitLaunchTime": {
"type": "number",
"description": "Wait time before connection in milliseconds"
}
}
}
Expand Down
141 changes: 105 additions & 36 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as net from 'net';
import * as path from 'path';
import { stringify } from 'querystring';
import * as util from 'util';
import * as vscode from 'vscode';

import {
CancellationToken,
DebugAdapterDescriptor,
Expand Down Expand Up @@ -255,17 +258,33 @@ class RdbgAdapterDescriptorFactory implements DebugAdapterDescriptorFactory {
return f();
}

parse_port(port: string) : [string | undefined, number | undefined, string | undefined] {
var m;

if (port.match(/^\d+$/)) {
return ["localhost", parseInt(port), undefined];
}
else if ((m = port.match(/^(.+):(\d+)$/))) {
return [m[1], parseInt(m[2]), undefined];
}
else {
return [undefined, undefined, port];
}
}

async attach(session: DebugSession): Promise<DebugAdapterDescriptor> {
const config = session.configuration as AttachConfiguration;
let port: string;
let port: number | undefined;
let host: string | undefined;
let sock_path: string | undefined;

if (config.noDebug) {
vscode.window.showErrorMessage("Can not attach \"Without debugging\".");
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}

if (config.debugPort) {
port = config.debugPort;
[host, port, sock_path] = this.parse_port(config.debugPort);
}
else {
const list = await this.get_sock_list(config);
Expand All @@ -276,22 +295,32 @@ class RdbgAdapterDescriptorFactory implements DebugAdapterDescriptorFactory {
vscode.window.showErrorMessage("Can not find attachable Ruby process.");
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
case 1:
port = list[0];
sock_path = list[0];
break;
default:
const sock = await vscode.window.showQuickPick(list);
if (sock) {
port = sock;
sock_path = sock;
}
else {
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}
}
}
return new DebugAdapterNamedPipeServer(port);

if (sock_path) {
return new DebugAdapterNamedPipeServer(sock_path);
}
else if (port) {
return new vscode.DebugAdapterServer(port, host);
}
else {
vscode.window.showErrorMessage("Unrechable.");
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}
}

async get_sock_path(config: LaunchConfiguration): Promise<string | null> {
async get_sock_path(config: LaunchConfiguration): Promise<string | undefined> {
return new Promise((resolve) => {
const rdbg = config.rdbgPath || "rdbg";
const command = this.make_shell_command(rdbg + " --util=gen-sockpath");
Expand All @@ -300,12 +329,12 @@ class RdbgAdapterDescriptorFactory implements DebugAdapterDescriptorFactory {

p.on('error', e => {
this.show_error(e.message);
resolve(null);
resolve(undefined);
});
p.on('exit', (code) => {
if (code != 0) {
this.show_error("exit code is " + code);
resolve(null);
resolve(undefined);
}
else {
resolve(path);
Expand Down Expand Up @@ -379,16 +408,25 @@ class RdbgAdapterDescriptorFactory implements DebugAdapterDescriptorFactory {

// outputChannel.appendLine(JSON.stringify(session));

const sock_path = await this.get_sock_path(config);
// setup debugPort
let sock_path : string | undefined;
let tcp_host : string | undefined;
let tcp_port : number | undefined;

if (!sock_path) {
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
if (config.debugPort) {
[tcp_host, tcp_port, sock_path] = this.parse_port(config.debugPort);
}
if (fs.existsSync(sock_path)) {
vscode.window.showErrorMessage("already exists: " + sock_path);
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
else {
sock_path = await this.get_sock_path(config);
if (!sock_path) {
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}
if (fs.existsSync(sock_path)) {
vscode.window.showErrorMessage("already exists: " + sock_path);
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}
outputChannel.appendLine("sock-path: <" + sock_path + ">");
}
outputChannel.appendLine("sock-path: <" + sock_path + ">");

// setup terminal
outputTerminal = undefined;
Expand All @@ -410,7 +448,21 @@ class RdbgAdapterDescriptorFactory implements DebugAdapterDescriptorFactory {
});
}

const rdbg_args = rdbg + " --command --open --stop-at-load --sock-path=" + sock_path + " -- ";
const connection_parameter = () => {
if (sock_path) {
return "--sock-path=" + sock_path;
}
else {
if (tcp_host) {
return "--port=" + tcp_port + " --host=" + tcp_host;
}
else {
return "--port=" + tcp_port;
}
}
}

const rdbg_args = rdbg + " --command --open --stop-at-load " + connection_parameter() + " -- ";
const useBundlerFlag = (config.useBundler != undefined) ? config.useBundler : vscode.workspace.getConfiguration("rdbg").get("useBundler");
const useBundler = useBundlerFlag && fs.existsSync(workspace_folder() + '/Gemfile');
const ruby_command = config.command ? config.command : (useBundler ? 'bundle exec ruby' : 'ruby');
Expand Down Expand Up @@ -450,34 +502,48 @@ class RdbgAdapterDescriptorFactory implements DebugAdapterDescriptorFactory {
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}

// check sock-path
const start_time = Date.now();
let i = 0;
while (!fs.existsSync(sock_path)) {
i++;
if (i > 30) {
const version: string | null = await this.get_version(config);

if (version && this.vernum(version) < this.vernum("rdbg 1.2.0")) {
vscode.window.showErrorMessage("rdbg 1.2.0 is required (" + version + " is used). Please update debug.gem.");
}
else {
vscode.window.showErrorMessage("Couldn't start debug session (wait for " + (Date.now() - start_time) + " ms). Please install debug.gem.");
// use NamedPipe
if (sock_path) {
// check sock-path
const start_time = Date.now();
let i = 0;
while (!fs.existsSync(sock_path)) {
i++;
if (i > 30) {
const version: string | null = await this.get_version(config);

if (version && this.vernum(version) < this.vernum("rdbg 1.2.0")) {
vscode.window.showErrorMessage("rdbg 1.2.0 is required (" + version + " is used). Please update debug.gem.");
}
else {
vscode.window.showErrorMessage("Couldn't start debug session (wait for " + (Date.now() - start_time) + " ms). Please install debug.gem.");
}
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(0);
}, 100); // ms
});
}

return new DebugAdapterNamedPipeServer(sock_path);
}
else if (tcp_port) {
// TODO: synchronize technique
const wait_ms = config.waitLaunchTime ? config.waitLaunchTime : 1000 /* 1 sec */;

await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(0);
}, 100); // ms
}, wait_ms); // ms
});
return new vscode.DebugAdapterServer(tcp_port, tcp_host);
}

return new DebugAdapterNamedPipeServer(sock_path);
}
else {
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}

// failed
return new DebugAdapterInlineImplementation(new StopDebugAdapter);
}
}

Expand All @@ -502,6 +568,9 @@ interface LaunchConfiguration extends DebugConfiguration {
args?: string[];
env?: { [key: string]: string };

debugPort?: string;
waitLaunchTime?: number;

useBundler?: boolean;
askParameters?: boolean;

Expand Down

0 comments on commit 910c647

Please sign in to comment.