Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Stage rl/standard debug sockets #876

Merged
merged 21 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
86d3aa7
Standard debugging sockets initial implementation
stage-rl Oct 21, 2023
475a912
Unused imports cleanup
stage-rl Oct 21, 2023
2a4cc9c
Pick testing debuggee from rdbg output
stage-rl Oct 22, 2023
1b0c649
Run rdbg with bundle exec and format
stage-rl Nov 4, 2023
a481500
Update readme file to include rails debugging info
stage-rl Nov 4, 2023
2ef206d
Update readme file to include rails debugging info
stage-rl Nov 4, 2023
1cf5c89
Fixes based on PR review
stage-rl Nov 12, 2023
e13522a
Fixes and refactors based on PR review
stage-rl Nov 12, 2023
17c381b
Merge branch 'stage-rl/standard_debug_sockets' of https://github.com/…
stage-rl Nov 12, 2023
5da1664
Another readme.md correction (revert MD editor change)
stage-rl Nov 12, 2023
b557aee
README update for live processes debugging
stage-rl Nov 26, 2023
0a9bcf1
Merge remote-tracking branch 'upstream/main' into stage-rl/standard_d…
stage-rl Dec 10, 2023
3bcefe1
Migrate socket standardization changes to workspace branch
stage-rl Dec 10, 2023
32e7628
Fix formatting
stage-rl Dec 10, 2023
fdd44fd
Fix TS error
stage-rl Dec 12, 2023
b393f21
Merge remote-tracking branch 'upstream/main' into stage-rl/standard_d…
stage-rl Jan 17, 2024
f9be28e
Debugger merge manual
stage-rl Jan 17, 2024
1a24f1f
Merge remote-tracking branch 'upstream/main' into stage-rl/standard_d…
stage-rl Jan 17, 2024
d7a0d58
Revert readme list order to 1s only
stage-rl Jan 19, 2024
4f98bc0
Merge remote-tracking branch 'upstream/main' into stage-rl/standard_d…
stage-rl Jan 19, 2024
f22b8b3
Merge remote-tracking branch 'upstream/main' into stage-rl/standard_d…
stage-rl Jan 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,20 @@ This command would generate the following configuration:
}
```

### Debugging live processes

Instead of launching a process to debug every time, you may want to attach the VS Code debugger to an existing process, such as a Rails server. Follow these instructions to do so.

Install `debug` gem. Verify by running `bundle exec rdbg -v`
vinistock marked this conversation as resolved.
Show resolved Hide resolved

Run your application with the debugger attached, so that the extension can connect to it.

```shell
bundle exec rdbg -O -n -c -- bin/rails server -p 3000
```

For better integrated rails tests support also install `ruby-lsp-rails` gem.

#### VS Code configurations

In addition to the Ruby LSP's own configuration, there are some VS Code settings that may need to be changed to get the
Expand Down Expand Up @@ -362,12 +376,12 @@ Possible values are:
The `launch.json` contains a 'Minitest - current file' configuration for the debugger.

1. Add a breakpoint using the VS Code UI.
1. Open the relevant test file.
1. Open the **Run and Debug** panel on the sidebar.
1. Ensure `Minitest - current file` is selected in the top dropdown.
1. Press `F5` OR click the green triangle next to the top dropdown. VS Code will then run the test file with debugger activated.
1. When the breakpoint is triggered, the process will pause and VS Code will connect to the debugger and activate the debugger UI.
1. Open the Debug Console view to use the debugger's REPL.
2. Open the relevant test file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) Leaving them all at 1. in the source makes it easier to add or remove steps, without having to renumber the others.

3. Open the **Run and Debug** panel on the sidebar.
4. Ensure `Minitest - current file` is selected in the top dropdown.
5. Press `F5` OR click the green triangle next to the top dropdown. VS Code will then run the test file with debugger activated.
6. When the breakpoint is triggered, the process will pause and VS Code will connect to the debugger and activate the debugger UI.
7. Open the Debug Console view to use the debugger's REPL.

## License

Expand Down
84 changes: 42 additions & 42 deletions src/debugger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from "path";
import fs from "fs";
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { ChildProcessWithoutNullStreams, spawn, execSync } from "child_process";

import * as vscode from "vscode";

Expand Down Expand Up @@ -39,7 +39,7 @@ export class Debugger
if (session.configuration.request === "launch") {
return this.spawnDebuggeeForLaunch(session);
} else if (session.configuration.request === "attach") {
return this.attachDebuggee();
return this.attachDebuggee(session);
} else {
return new Promise((_resolve, reject) =>
reject(
Expand Down Expand Up @@ -129,19 +129,40 @@ export class Debugger
}
}

private async attachDebuggee(): Promise<vscode.DebugAdapterDescriptor> {
private getSockets(session: vscode.DebugSession): string[] {
const cmd = "bundle exec rdbg --util=list-socks";
const workspaceFolder = session.workspaceFolder;
if (!workspaceFolder) {
throw new Error("Debugging requires a workspace folder to be opened");
}
const configuration = session.configuration;
let sockets: string[] = [];
try {
sockets = execSync(cmd, {
cwd: workspaceFolder.uri.fsPath,
env: configuration.env,
})
.toString()
.split("\n")
.filter((socket) => socket.length > 0);
} catch (error: any) {
this.console.append(`Error listing sockets: ${error.message}`);
}
return sockets;
}

private async attachDebuggee(
session: vscode.DebugSession,
): Promise<vscode.DebugAdapterDescriptor> {
// When using attach, a process will be launched using Ruby debug and it will create a socket automatically. We have
// to find the available sockets and ask the user which one they want to attach to
const socketsDir = path.join("/", "tmp", "ruby-lsp-debug-sockets");
const sockets = fs
.readdirSync(socketsDir)
.map((file) => file)
.filter((file) => file.endsWith(".sock"));

const sockets = this.getSockets(session);
if (sockets.length === 0) {
throw new Error(
`No debuggee processes found. Was a socket created in ${socketsDir}?`,
);
throw new Error(`No debuggee processes found. Is the process running?`);
}

if (sockets.length === 1) {
return new vscode.DebugAdapterNamedPipeServer(sockets[0]);
}

const selectedSocketPath = await vscode.window
Expand All @@ -153,7 +174,7 @@ export class Debugger
if (value === undefined) {
throw new Error("No debuggee selected");
}
return path.join(socketsDir, value);
return value;
});

return new vscode.DebugAdapterNamedPipeServer(selectedSocketPath);
Expand All @@ -168,15 +189,13 @@ export class Debugger
const configuration = session.configuration;
const workspaceFolder: vscode.WorkspaceFolder = configuration.targetFolder;
const cwd = workspaceFolder.uri.fsPath;
const sockPath = this.socketPath(workspaceFolder.name);

return new Promise((resolve, reject) => {
const args = [
"exec",
"rdbg",
"--open",
"--command",
`--sock-path=${sockPath}`,
st0012 marked this conversation as resolved.
Show resolved Hide resolved
"--",
configuration.program,
];
Expand Down Expand Up @@ -206,7 +225,14 @@ export class Debugger
initialMessage.includes("DEBUGGER: wait for debugger connection...")
) {
initialized = true;
resolve(new vscode.DebugAdapterNamedPipeServer(sockPath));
const regex =
/DEBUGGER: Debugger can attach via UNIX domain socket \((.*)\)/;
const sockPath = RegExp(regex).exec(initialMessage);
if (sockPath && sockPath.length === 2) {
resolve(new vscode.DebugAdapterNamedPipeServer(sockPath[1]));
} else {
reject(new Error("Debugger not found on UNIX socket"));
}
Comment on lines +231 to +238
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is not necessary if we keep our standard socket path for launch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the previous one.

The point is that we maybe don't want to hardcode those sockets and want to let rdbg decide. We then extract them from debugger output. The implementation provided handles both ends, see your other comment.

}
});

Expand Down Expand Up @@ -234,30 +260,4 @@ export class Debugger
});
});
}

// Generate a socket path so that Ruby debug doesn't have to create one for us. This makes coordination easier since
// we always know the path to the socket
private socketPath(workspaceName: string) {
const socketsDir = path.join("/", "tmp", "ruby-lsp-debug-sockets");
if (!fs.existsSync(socketsDir)) {
fs.mkdirSync(socketsDir);
}

let socketIndex = 0;
const prefix = `ruby-debug-${workspaceName}`;
const existingSockets = fs
.readdirSync(socketsDir)
.map((file) => file)
.filter((file) => file.startsWith(prefix))
.sort();

if (existingSockets.length > 0) {
socketIndex =
Number(
/-(\d+).sock$/.exec(existingSockets[existingSockets.length - 1])![1],
) + 1;
}

return `${socketsDir}/${prefix}-${socketIndex}.sock`;
}
}