Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
63 changes: 47 additions & 16 deletions lldb/tools/lldb-dap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ The extension requires the `lldb-dap` (formerly `lldb-vscode`) binary.
This binary is not packaged with the VS Code extension.

There are multiple ways to obtain this binary:
* Use the binary provided by your toolchain (for example `xcrun -f lldb-dap` on macOS) or contact your toolchain vendor to include it.
* Download one of the relase packages from the [LLVM release page](https://github.com/llvm/llvm-project/releases/). The `LLVM-19.1.0-{operating_system}.tar.xz` packages contain a prebuilt `lldb-dap` binary.
* Build it from source (see [LLDB's build instructions](https://lldb.llvm.org/resources/build.html)).

- Use the binary provided by your toolchain (for example `xcrun -f lldb-dap` on macOS) or contact your toolchain vendor to include it.
- Download one of the release packages from the [LLVM release page](https://github.com/llvm/llvm-project/releases/). The `LLVM-19.1.0-{operating_system}.tar.xz` packages contain a prebuilt `lldb-dap` binary.
- Build it from source (see [LLDB's build instructions](https://lldb.llvm.org/resources/build.html)).

By default, the VS Code extension will expect to find `lldb-dap` in your `PATH`.
Alternatively, you can explictly specify the location of the `lldb-dap` binary using the `lldb-dap.executable-path` setting.
Alternatively, you can explicitly specify the location of the `lldb-dap` binary using the `lldb-dap.executable-path` setting.

### Usage with other IDEs

Expand Down Expand Up @@ -144,7 +145,7 @@ instead of using the custom command `attachCommands`.
### Connect to a Debug Server on Another Machine

This connects to a debug server running on another machine with hostname
`hostnmame`. Which is debugging the program `/tmp/a.out` and listening on
`hostname`. Which is debugging the program `/tmp/a.out` and listening on
port `5678` of that other machine.

```javascript
Expand All @@ -162,7 +163,6 @@ to send an attach request to a debug server running on a different machine,
instead of custom command `attachCommands`.
The default hostname being used `localhost`.


```javascript
{
"name": "Local Debug Server",
Expand Down Expand Up @@ -212,7 +212,7 @@ specific key/value pairs:
| **customThreadFormat** | string | | Same as `customFrameFormat`, but for threads instead of stack frames.
| **displayExtendedBacktrace** | bool | | Enable language specific extended backtraces.
| **enableAutoVariableSummaries** | bool | | Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.
| **enableSyntheticChildDebugging** | bool | | If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.
| **enableSyntheticChildDebugging** | bool | | If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating synthetic child plug-ins as it lets you see the actual contents of the variable.
| **initCommands** | [string] | | LLDB commands executed upon debugger startup prior to creating the LLDB target.
| **preRunCommands** | [string] | | LLDB commands executed just before launching/attaching, after the LLDB target has been created.
| **stopCommands** | [string] | | LLDB commands executed just after each stop.
Expand All @@ -221,8 +221,9 @@ specific key/value pairs:

All commands and command outputs will be sent to the debugger console when they are executed.
Commands can be prefixed with `?` or `!` to modify their behavior:
* Commands prefixed with `?` are quiet on success, i.e. nothing is written to stdout if the command succeeds.
* Prefixing a command with `!` enables error checking: If a command prefixed with `!` fails, subsequent commands will not be run. This is usefule if one of the commands depends on another, as it will stop the chain of commands.

- Commands prefixed with `?` are quiet on success, i.e. nothing is written to stdout if the command succeeds.
- Prefixing a command with `!` enables error checking: If a command prefixed with `!` fails, subsequent commands will not be run. This is useful if one of the commands depends on another, as it will stop the chain of commands.

For JSON configurations of `"type": "launch"`, the JSON configuration can additionally
contain the following key/value pairs:
Expand All @@ -243,10 +244,40 @@ the following `lldb-dap` specific key/value pairs:
| Parameter | Type | Req | |
|-----------------------------------|-------------|:---:|---------|
| **program** | string | | Path to the executable to attach to. This value is optional but can help to resolve breakpoints prior the attaching to the program.
| **pid** | number | | The process id of the process you wish to attach to. If **pid** is omitted, the debugger will attempt to attach to the program by finding a process whose file name matches the file name from **porgram**. Setting this value to `${command:pickMyProcess}` will allow interactive process selection in the IDE.
| **pid** | number | | The process id of the process you wish to attach to. If **pid** is omitted, the debugger will attempt to attach to the program by finding a process whose file name matches the file name from **program**. Setting this value to `${command:pickMyProcess}` will allow interactive process selection in the IDE.
| **waitFor** | boolean | | Wait for the process to launch.
| **attachCommands** | [string] | | LLDB commands that will be executed after **preRunCommands** which take place of the code that normally does the attach. The commands can create a new target and attach or launch it however desired. This allows custom launch and attach configurations. Core files can use `target create --core /path/to/core` to attach to core files.

### Configuring `lldb-dap` defaults

User settings can set the default value for the following supported
`launch.json` configuration keys:

| Parameter | Type | Default |
| --------------------------------- | -------- | :-----: |
| **commandEscapePrefix** | string | ``"`"`` |
| **customFrameFormat** | string | `""` |
| **customThreadFormat** | string | `""` |
| **detachOnError** | boolean | `false` |
| **disableASLR** | boolean | `true` |
| **disableSTDIO** | boolean | `false` |
| **displayExtendedBacktrace** | boolean | `false` |
| **enableAutoVariableSummaries** | boolean | `false` |
| **enableSyntheticChildDebugging** | boolean | `false` |
| **timeout** | number | `30` |
| **targetTriple** | string | `""` |
| **platformName** | string | `""` |
| **initCommands** | [string] | `[]` |
| **preRunCommands** | [string] | `[]` |
| **postRunCommands** | [string] | `[]` |
| **stopCommands** | [string] | `[]` |
| **exitCommands** | [string] | `[]` |
| **terminateCommands** | [string] | `[]` |

To adjust your settings, open the Settings editor via the
`File > Preferences > Settings` menu or press `Ctrl+`, on Windows/Linux and
`Cmd+`, on Mac.

## Debug Console

The Debug Console allows printing variables / expressions and executing lldb commands.
Expand Down Expand Up @@ -294,13 +325,13 @@ Inspect or adjust the behavior of lldb-dap repl evaluation requests. The
supported modes are `variable`, `command` and `auto`.

- `variable` - Variable mode expressions are evaluated in the context of the
current frame.
current frame.
- `command` - Command mode expressions are evaluated as lldb commands, as a
result, values printed by lldb are always stringified representations of the
expression output.
result, values printed by lldb are always stringified representations of the
expression output.
- `auto` - Auto mode will attempt to infer if the expression represents an lldb
command or a variable expression. A heuristic is used to infer if the input
represents a variable or a command.
command or a variable expression. A heuristic is used to infer if the input
represents a variable or a command.

In all three modes, you can use the `commandEscapePrefix` to ensure an expression
is evaluated as a command.
Expand Down Expand Up @@ -330,7 +361,7 @@ For example you can use a launch configuration hook to trigger custom events lik
"program": "exe",
"stopCommands": [
"lldb-dap send-event MyStopEvent",
"lldb-dap send-event MyStopEvent '{\"key\": 321}",
"lldb-dap send-event MyStopEvent '{\"key\": 321}"
]
}
```
Expand Down
107 changes: 107 additions & 0 deletions lldb/tools/lldb-dap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"title": "lldb-dap",
"properties": {
"lldb-dap.executable-path": {
"type": "string",
"scope": "machine-overridable",
"type": "string",
"description": "The path to the lldb-dap binary, e.g. /usr/local/bin/lldb-dap"
Expand Down Expand Up @@ -105,6 +106,112 @@
"type": "boolean",
"markdownDescription": "Run lldb-dap in server mode.\n\nWhen enabled, lldb-dap will start a background server that will be reused between debug sessions. This allows caching of debug symbols between sessions and improves launch performance.",
"default": false
},
"lldb-dap.defaults.commandEscapePrefix": {
"type": "string",
"description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.",
"default": "`"
},
"lldb-dap.defaults.customFrameFormat": {
"type": "string",
"description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
"default": ""
},
"lldb-dap.defaults.customThreadFormat": {
"type": "string",
"description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
"default": ""
},
"lldb-dap.defaults.detachOnError": {
"type": "boolean",
"description": "Detach from the program.",
"default": false
},
"lldb-dap.defaults.disableASLR": {
"type": "boolean",
"description": "Enable or disable Address space layout randomization if the debugger supports it.",
"default": true
},
"lldb-dap.defaults.disableSTDIO": {
"type": "boolean",
"description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.",
"default": false
},
"lldb-dap.defaults.displayExtendedBacktrace": {
"type": "boolean",
"description": "Enable language specific extended backtraces.",
"default": false
},
"lldb-dap.defaults.enableAutoVariableSummaries": {
"type": "boolean",
"description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.",
"default": false
},
"lldb-dap.defaults.enableSyntheticChildDebugging": {
"type": "boolean",
"description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.",
"default": false
},
"lldb-dap.defaults.timeout": {
"type": "number",
"description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds.",
"default": 30
},
"lldb-dap.defaults.targetTriple": {
"type": "string",
"description": "Triplet of the target architecture to override value derived from the program file."
},
"lldb-dap.defaults.platformName": {
"type": "string",
"description": "Name of the execution platform to override value derived from the program file."
},
"lldb-dap.defaults.initCommands": {
"type": "array",
"items": {
"type": "string"
},
"description": "Initialization commands executed upon debugger startup.",
"default": []
},
"lldb-dap.defaults.preRunCommands": {
"type": "array",
"items": {
"type": "string"
},
"description": "Commands executed just before the program is launched.",
"default": []
},
"lldb-dap.defaults.postRunCommands": {
"type": "array",
"items": {
"type": "string"
},
"description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.",
"default": []
},
"lldb-dap.defaults.stopCommands": {
"type": "array",
"items": {
"type": "string"
},
"description": "Commands executed each time the program stops.",
"default": []
},
"lldb-dap.defaults.exitCommands": {
"type": "array",
"items": {
"type": "string"
},
"description": "Commands executed when the program exits.",
"default": []
},
"lldb-dap.defaults.terminateCommands": {
"type": "array",
"items": {
"type": "string"
},
"description": "Commands executed when the debugging session ends.",
"default": []
}
}
},
Expand Down
84 changes: 81 additions & 3 deletions lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,88 @@ async function isServerModeSupported(exe: string): Promise<boolean> {
return /--connection/.test(stdout);
}

interface BoolConfig {
type: 'boolean';
default: boolean;
}
interface StringConfig {
type: 'string';
default: string;
}
interface NumberConfig {
type: 'number';
default: number;
}
interface StringArrayConfig {
type: 'stringArray';
default: string[];
}
type DefaultConfig = BoolConfig | NumberConfig | StringConfig | StringArrayConfig;

const configurations: Record<string, DefaultConfig> = {
// Keys for debugger configurations.
"commandEscapePrefix": { type: "string", default: "`" },
"customFrameFormat": { type: "string", default: "" },
"customThreadFormat": { type: "string", default: "" },
"detachOnError": { type: "boolean", default: false },
"disableASLR": { type: "boolean", default: true },
"disableSTDIO": { type: "boolean", default: false },
"displayExtendedBacktrace": { type: "boolean", default: false },
"enableAutoVariableSummaries": { type: "boolean", default: false },
"enableSyntheticChildDebugging": { type: "boolean", default: false },
"timeout": { type: "number", default: 30 },

// Keys for platform / target configuration.
"platformName": { type: "string", default: "" },
"targetTriple": { type: "string", default: "" },

// Keys for debugger command hooks.
"initCommands": { type: "stringArray", default: [] },
"preRunCommands": { type: "stringArray", default: [] },
"postRunCommands": { type: "stringArray", default: [] },
"stopCommands": { type: "stringArray", default: [] },
"exitCommands": { type: "stringArray", default: [] },
"terminateCommands": { type: "stringArray", default: [] },
};

export class LLDBDapConfigurationProvider
implements vscode.DebugConfigurationProvider
{
constructor(private readonly server: LLDBDapServer) {}
implements vscode.DebugConfigurationProvider {
constructor(private readonly server: LLDBDapServer) { }

async resolveDebugConfiguration(
folder: vscode.WorkspaceFolder | undefined,
debugConfiguration: vscode.DebugConfiguration,
token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration> {
let config = vscode.workspace.getConfiguration('lldb-dap.defaults');
for (const [key, cfg] of Object.entries(configurations)) {
if (Reflect.has(debugConfiguration, key)) continue;
const value = config.get(key);
if (value === cfg.default) continue;
switch (cfg.type) {
case 'string':
if (typeof value !== 'string')
throw new Error(`Expected ${key} to be a string, got ${value}`);
break;
case 'number':
if (typeof value !== 'number')
throw new Error(`Expected ${key} to be a number, got ${value}`);
break;
case 'boolean':
if (typeof value !== 'boolean')
throw new Error(`Expected ${key} to be a boolean, got ${value}`);
break;
case 'stringArray':
if (typeof value !== 'object' && Array.isArray(value))
throw new Error(`Expected ${key} to be a array of strings, got ${value}`);
if ((value as string[]).length === 0) continue;
break;
}

debugConfiguration[key] = value;
}

return debugConfiguration;
}

async resolveDebugConfigurationWithSubstitutedVariables(
folder: vscode.WorkspaceFolder | undefined,
Expand Down
Loading