[RFC] Improvements to Swift Debugging in VS Code

Hello everyone!

I’m creating this forum post to propose changes to the Swift extension for VS Code that will improve the out-of-box Swift debugging experience. Any feedback is greatly appreciated.

Motivation

The Swift extension currently relies on the CodeLLDB extension to provide debugging features which is maintained by an independent developer and includes a pre-built lldb executable. However, this pre-built executable does not support Swift and requires User/Workspace settings to be applied in order to show variables and use the Swift REPL while debugging. The Swift extension displays a warning on startup if these settings are not applied, but this can be annoying since they need to be updated any time the Swift toolchain changes. Additionally, the auto-generated launch configurations created by the Swift extension frequently need to be updated when workspace settings change. This also pops up an annoying dialog asking for them to be updated.

The LLVM community has recently created their own official LLDB DAP extension for VS Code which can be used to launch debugging sessions via the lldb-dap executable. The Swift extension can be modified to use this extension instead and point it at the lldb-dap executable that is now available in Swift toolchain versions >=6.0.0. This will require some minor changes to the LLDB DAP extension, but this is a fantastic opportunity to collaborate.

New Launch Configuration Type

I would like to create a new launch configuration type called “swift” that will be managed by the Swift extension. This new launch configuration type will be toolchain and debugger backend agnostic to make it more easily shareable via source control. The VS Code extension will delegate to the appropriate debug extension based on the current toolchain:

  • lldb-dap: Will be the default option for toolchains >=6.0.0 as it is provided by the verified LLVM publisher and can be configured to use binaries from the toolchain directly.
  • CodeLLDB: Will be used for toolchains older than 6.0.0. Needs some global configuration that requires paths within the toolchain to be set which we can prompt the user for when they try to launch a debug session for the first time. This message used to show on startup, but I think it makes more sense to prompt lazily.

This new launch configuration type will allow for three different launch types:

  1. Launch a Swift executable
  2. Attach to a Swift process by its PID
  3. Attach to a Swift process by name

Launch Executable

Launches and attaches to a Swift executable for debugging. The user must specify a program and any arguments/environment variables.

{
    "type": "swift",
    "request": "launch",
    "args": [],
    "env": {},
    "cwd": "${workspaceFolder}",
    "name": "Launch swift-executable",
    "program": "${workspaceFolder}/.build/debug/swift-executable",
    "preLaunchTask": "swift: Build Debug swift-executable",
}

Attach to Process

Attaches to a given process ID. Can use the built-in VS Code process selector to choose a process on launch.

{
    "type": "swift",
    "request": "attach",
    "name": "Attach to Process",
    "processId": "${command:PickProcess}",
}

Attach to Executable

Attaches to an already running Swift executable by name. Searches for the active program and attaches LLDB to it.

{
    "type": "swift",
    "request": "attach",
    "name": "Attach to swift-executable",
    "program": "${workspaceFolder}/.build/debug/swift-executable"
}

Additional Debugging Entry Points

The Swift extension should not automatically generate launch configurations. The standard point of entry for debugging in VS Code is to provide snippets to create launch configurations in the launch.json. However, it would also be nice to provide additional entry points to make debugging as discoverable as possible.

Code Lenses

SourceKit-LSP already provides code lenses for classes and structures decorated with @main. However, with a few minor changes it can be made to look a little bit cleaner:

  1. Include the name of the executable in the code lens: SourceKit-LSP knows about SwiftPM projects already and can provide this information easily.
  2. Add VS Code iconography to the code lens title: Can be done easily with middleware in the Language Client.

Editor Actions

VS Code provides a menu contribution point that can be used to insert commands that run/debug the active file. These commands will only appear when editing a Swift file that is part of an executable target and will launch a debug session of said target when clicked:

Extension Dependencies

Right now the Swift extension directly depends on CodeLLDB for debugging support. Given the switch to the LLDB-DAP extension, I propose that we remove the dependency on CodeLLDB and instead depend on the LLDB-DAP extension. Users on older Swift toolchains (<6.0.0) will get an error notification when they try to debug without CodeLLDB asking them to install CodeLLDB in order to do so. This notification will also mention that upgrading to Swift 6 is the recommended approach:

Final Thoughts

There are some open issues on the LLDB DAP extension that will need to be resolved before it can be used as a drop-in replacement for CodeLLDB:

17 Likes

Thanks for posting this, I can't say much about the implementation details of the extension, but paving the way for switching to the official lldb-dap is definitely the right path forward.

2 Likes

I wholeheartedly support this direction. The lldb-dap binary being part of the toolchain is a big selling point and eliminates a bunch of pitfalls where the compiler and debugger risk getting out of sync.

Both lldb-dap and the corresponding VSCode extension are seeing a lot of contributions these days so I'm excited the Swift extension will start benefiting from that as well.

2 Likes

As someone who originally proposed including lldb-dap in all swift toolchains I fully agree with the direction of this proposal.

I not certain what some of the details of this proposal are though.

  • I'm guessing the lldb extension will need to be told where to find lldb-dap, you mention this in the open issues. Once that is resolved will this be updated automatically by the swift extension in a similar manner to how the CodeLLDB extension is updated (maybe silently).
  • As far as I can tell the LLVM extension currently consists of just the executable and a package.json. Are you proposing to include typescript to implement things like process pickers?
  • Given there are still issues with the lldb-dap executable, eg unverified breakpoints, would it be preferable to default to that in a version of swift where those are fixed.
1 Like

Thank you for the feedback, Adam!

I'm guessing the lldb extension will need to be told where to find lldb-dap, you mention this in the open issues. Once that is resolved will this be updated automatically by the swift extension in a similar manner to how the CodeLLDB extension is updated (maybe silently).

Yeah I should have explained this in a little more detail. My plan is to avoid dealing with global settings and instead add the option to specify an lldb-dap executable directly in its launch configuration (similar to the Node launch config's runtimeExecutable option). This way the extension can simply launch a debug session with lldb-dap and include all the information it needs without having to prompt the user or mess with their settings.

As far as I can tell the LLVM extension currently consists of just the executable and a package.json. Are you proposing to include typescript to implement things like process pickers?

Yes, this would be added in TypeScript the same way that CodeLLDB does it. Just to make sure we don't lose that functionality.

Given there are still issues with the lldb-dap executable, eg unverified breakpoints, would it be preferable to default to that in a version of swift where those are fixed.

There might be a way to fix the breakpoints issue on older lldb-dap versions in the extension (e.g. by sending a breakpoint request via dap which will force it to resolve known breakpoints). However, if it can't be resolved then yes I would suggest holding off on switching to lldb-dap until a version that has the needed fixes is in the toolchain. I'll have more information when I actually get to implementing this, but I definitely don't want to make the experience worse for users of the extension.

1 Like