Skip to content

[lldb-dap] Support vscode launch URLs #125843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 20 additions & 1 deletion lldb/tools/lldb-dap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,25 @@ The default hostname being used `localhost`.
}
```

### Launching via `vscode://` URIs

Debugging sessions can also be starting using special URIs.

The `vscode://llvm-vs-code-extensions.lldb-dap/start?config={launch-config}`
URI accepts a [URL-encoded](https://en.wikipedia.org/wiki/Percent-encoding)
JSON launch config. The most frequently used arguments (`request`, `program`,
`args`, `cwd`, `pid`, ...) can also specified directly, e.g.
`vscode://llvm-vs-code-extensions.lldb-dap/start?request=attach&pid=1234`, or
`vscode://llvm-vs-code-extensions.lldb-dap/start?program=ls&args=-a&args=/etc`.

This is useful for integration with custom scripts to start debugging
sessions. The URI might be printed to the terminal, potentially using
[OSC-8 hyperlinks](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda),
or passed to `code --open-url` or `xdg-open`, although mileage may vary depending
on your specific debugging setup. E.g., `code --open-url` will not work when using a
SSH remote session. Furthermore, placeholders such as `${workspaceFolder}` are not
supported within launch URLs.

### Configuration Settings Reference

For both launch and attach configurations, lldb-dap accepts the following `lldb-dap`
Expand Down Expand Up @@ -328,4 +347,4 @@ The source code is part of the [LLVM repository](https://github.com/llvm/llvm-pr
We use Github's [issue tracker](https://github.com/llvm/llvm-project/issues?q=label%3Alldb-dap) and patches can be submitted via [pull requests](https://github.com/llvm/llvm-project/pulls?q=label%3Alldb-dap).
Furthermore, there is a [LLDB category](https://discourse.llvm.org/c/subprojects/lldb/8) on the LLVM discourse forum.

For instructions on how to get started with development on lldb-dap, see the "[Contributing to lldb-dap](https://lldb.llvm.org/resources/lldbdap.html)"
For instructions on how to get started with development on lldb-dap, see the "[Contributing to lldb-dap](https://lldb.llvm.org/resources/lldbdap.html)" guide.
3 changes: 2 additions & 1 deletion lldb/tools/lldb-dap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"typescript": "^5.7.3"
},
"activationEvents": [
"onDebug"
"onDebug",
"onUri"
],
"main": "./out/extension",
"scripts": {
Expand Down
5 changes: 5 additions & 0 deletions lldb/tools/lldb-dap/src-ts/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
isExecutable,
} from "./debug-adapter-factory";
import { DisposableContext } from "./disposable-context";
import { LaunchUriHandler } from "./uri-launch-handler";

/**
* This class represents the extension and manages its life cycle. Other extensions
Expand Down Expand Up @@ -37,6 +38,10 @@ export class LLDBDapExtension extends DisposableContext {
}
}),
);

this.pushSubscription(
vscode.window.registerUriHandler(new LaunchUriHandler())
);
}
}

Expand Down
78 changes: 78 additions & 0 deletions lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as vscode from "vscode";

export class LaunchUriHandler implements vscode.UriHandler {
async handleUri(uri: vscode.Uri) {
try {
const params = new URLSearchParams(uri.query);
if (uri.path == '/start') {
// Some properties have default values
let debugConfig: vscode.DebugConfiguration = {
type: 'lldb-dap',
request: 'launch',
name: '',
};
// The `config` parameter allows providing a complete JSON-encoded configuration
const configJson = params.get("config");
if (configJson !== null) {
Object.assign(debugConfig, JSON.parse(configJson));
}
// Furthermore, some frequently used parameters can also be provided as separate parameters
const stringKeys = ["name", "request", "program", "cwd", "debuggerRoot"];
const numberKeys = ["pid"];
const arrayKeys = [
"args", "initCommands", "preRunCommands", "stopCommands", "exitCommands",
"terminateCommands", "launchCommands", "attachCommands"
];
for (const key of stringKeys) {
const value = params.get(key);
if (value) {
debugConfig[key] = value;
}
}
for (const key of numberKeys) {
const value = params.get(key);
if (value) {
debugConfig[key] = Number(value);
}
}
for (const key of arrayKeys) {
// `getAll()` returns an array of strings.
const value = params.getAll(key);
if (value) {
debugConfig[key] = value;
}
}
// Report an error if we received any unknown parameters
const supportedKeys = new Set<string>(["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys));
const presentKeys = new Set<string>(params.keys());
// FIXME: Use `Set.difference` as soon as ES2024 is widely available
const unknownKeys = new Set<string>();
for (const k of presentKeys.keys()) {
if (!supportedKeys.has(k)) {
unknownKeys.add(k);
}
}
if (unknownKeys.size > 0) {
throw new Error(`Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`);
}
// Prodide a default for the config name
const defaultName = debugConfig.request == 'launch' ? "URL-based Launch" : "URL-based Attach";
debugConfig.name = debugConfig.name || debugConfig.program || defaultName;
// Force the type to `lldb-dap`. We don't want to allow launching any other
// Debug Adapters using this URI scheme.
if (debugConfig.type != "lldb-dap") {
throw new Error(`Unsupported debugger type: ${debugConfig.type}`);
}
await vscode.debug.startDebugging(undefined, debugConfig);
} else {
throw new Error(`Unsupported Uri path: ${uri.path}`);
}
} catch (err) {
if (err instanceof Error) {
await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${err.message}`);
} else {
await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`);
}
}
}
}