# Debugging The Go extension allows you to launch or attach to Go programs for debugging. You can inspect variables and stacks, set breakpoints, and do other debugging activities using [VS Code’s Debugging UI](https://code.visualstudio.com/docs/editor/debugging). These debugging features are possible by using [Delve](https://github.com/go-delve/delve), the Go debugger. Previously, the Go extension communicated with Delve through a custom debug adaptor program (`legacy` mode). Since [`Delve`'s native debug adapter implementation](https://github.com/go-delve/delve/tree/master/service/dap) is available, the Go extension is transitioning to deprecate the legacy debug adapter in favor of direct communication with Delve via [DAP](https://microsoft.github.io/debug-adapter-protocol/overview). 📣 **We are happy to announce that the new _`dlv-dap`_ mode of Delve integration is enabled for _local_ _debugging_ by default. For [_remote_ _debugging_](#remote-debugging) it is the default in [Go Nightly](nightly.md) and is available with stable builds on demand with `"debugAdapter": "dlv-dap"` attribute in `launch.json` or `settings.json`!** Many features and settings described in this document may be available only with the new `dlv-dap` mode. For troubleshooting and configuring the legacy debug adapter, see [the legacy debug adapter documentation](https://github.com/golang/vscode-go/tree/master/docs/debugging-legacy.md). ## Get started Open a file to debug (either `package main` source file or the test file) in the editor, and select the `Run and Debug` button from [the Run view](https://code.visualstudio.com/docs/editor/debugging#_run-view). Alternatively, you can start debugging using `Start Debugging (F5)` command from [the Run menu](https://code.visualstudio.com/docs/editor/debugging#_run-menu) or from [the Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) (Linux/Windows: Ctrl+Shift+P, Mac: ⇧+⌘+P). If you already have launch configurations for the project (`.vscode/launch.json`), the Run view will display the configuration list to choose from. When no configuration is configured yet (no `.vscode/launch.json` file), the extension will choose a default configuration based on the file open in the editor. ❗ When you start debugging for the first time or if the `dlv` executable on your system is too old to support DAP, the Go extension may ask to install or update Delve. Follow the instructions to install it, and then start the debugging session again.
If you already have a `launch.json` for your project, you can open it using the Command Palette **Open launch.json** command. To add a new configuration to an existing `launch.json` file: 1. Open your `launch.json` file using the Command Palette **Open launch.json** command. 1. Click the **Add Configuration** button to invoke the snippet IntelliSense. There are many configuration attributes (see [the Launch.json attributes](#launchjson-attributes) section). IntelliSense in VS Code’s `launch.json` editor will help you navigate available options and documentation. ### Launch To launch and debug your project, select **Run** > **Start Debugging (F5)**. The launch feature uses a `launch` request type configuration in your `launch.json` file. Its `program` attribute needs to be the absolute path to either the Go file, or folder containing the main package or test file. In this mode, the Go extension will start the debug session by building and launching the program. The launched program will be terminated when the debug session ends. * Supported modes * `debug`: build and debug a main package * `test`: build and debug a test * `exec`: debug a precompiled binary * The binary must be built with `go build -gcflags=all="-N -l"` to disable inlining and optimizations that can interfere with debugging. * `auto`: automatically choose between `debug` and `test` depending on the open file ⚠️ For ["Remote Debugging"](#remote-debugging), add the `port` attribute to the launch configuration. VS Code will connect to the external user-specified `dlv dap` server at `host:port` and launch the target there. For remote debugging, the `program` attribute must point to the absolute path to the package or binary to debug in the remote host’s file system even when `substitutePath` is specified. ### Attach You can use this configuration to attach to a running process or a running debug session. * Supported modes * `local`: attaches to a local process. * The binary must be built with `go build -gcflags=all="-N -l"` to disable inlining and optimizations that can interfere with debugging. * `remote`: attaches to an in-progress debug session run by an external server. You can debug an already running program using the `local` mode type configuration. The Go extension will start `dlv dap` and configure it to attach to the specified process. Users can select the process to debug with one of the following options: * Specifying the numeric process id (PID) with the `processId` attribute. * Specifying the target program name in the `processId` attribute. If there are multiple processes matching the specified program name, the extension will show the list of matching processes at the start of the debug session. * Specifying `0` in the `processId` attribute and selecting the process from the drop-down menu at the start of the debug session.
NOTE: For remote debugging, add the `port` attribute to the launch configuration. VS Code will connect to the external user-specified `dlv dap` server at `host:port` and attach to the target there.See ["remote debugging"](#remote-debugging) for more details). You can connect to an already running remote debug session using the `remote` mode. Specify optional `host` and required `port` for the external `dlv --headless` server that already took program or process id details as command-line arguments. See ["Remote Debugging"](#remote-debugging) for more details). When you end an attach debug session, the debug UI allows you to choose to: * [DEFAULT] Disconnect: disconnect the client and: * `local`: leave the target process running (dlv terminates). * `remote`: let dlv decide if it can continue running (`--accept-multiclient` mode only); if so, the target will stay in halted or running state it was in at disconnect. * `dlv debug/test/exec`: terminate the target process if dlv terminates. * `dlv attach`: leave the target process running even if dlv terminates. * Stop: stop the attached server and the target process.
### Debug actions Once a debug session starts, the Debug toolbar will appear on the top of the editor.
The available commands are: * Continue / Pause F5 * Step Over (aka `next` in Delve) F10 * Step Into (aka `step` in Delve) F11 * Step Out (aka `stepout` in Delve) Shift+F11 or ⇧F11 * Restart (currently this is "Stop + Start") Ctrl+Shift+F5 or ⇧⌘F5 * Stop (terminate the debugee. Available in Launch request) Shift+F5 or ⇧F5 * Disconnect (detach from the debugee. Available only in Attach request) Shift+F5 or ⇧F5 * Terminate (terminate the debugee. Available only in Attach request) Alt+Shift+F5 or ⌥⇧F5 ### Breakpoints See [VS Code’s Debug Documentation on Breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints) to get familiar with VS Code’s UI. The Go debugger supports multiple ways to configure breakpoints. * **Breakpoints**: you can set breakpoints by clicking on the editor margin or using F9 on the current line. If the breakpoints can’t be set by Delve, VS Code will show the failure reason and grey out the dot.
* **Conditional breakpoints**: you can specify breakpoint conditions (similar to Delve’s [`condition` command](https://github.com/go-delve/delve/tree/master/Documentation/cli#condition)). * Expression condition: takes a boolean expression. * Hit count: supports comparison operators (`>`, `>=`, `<`, `<=`, `==`, `!=`) with an integer value. `% n` form means we should stop at the breakpoint when the hitcount is a multiple of `n`.
* **Function Breakpoints**: breakpoints can be set based on function names. Press the + button in the BREAKPOINTS section header and enter the location in the form of `
* **Logpoints**: a [logpoint](https://code.visualstudio.com/docs/editor/debugging#_logpoints) is a variant of breakpoint that does not 'break', but instead logs a message to DEBUG CONSOLE and continues execution. Expressions within `{}` are interpolated. For the list of acceptable expressions and syntax, see [Delve's documentation](https://github.com/go-delve/delve/blob/master/Documentation/cli/expr.md#expressions). ### Data inspection You can inspect variables in the VARIABLES section of the Run view or by hovering over their source in the editor. Variable values and expression evaluation are relative to the selected stack frame in the CALL section. By default, the VARIABLES section hides global variables, and shows only local variables and function arguments. However, you can still inspect global variables from the DEBUG CONSOLE panel. If you prefer to have the VARIABLES section show global variables, set the `showGlobalVariables` attribute in the `launch.json` configuration, or set it in the `go.delveConfig` setting. When you select a variable and right click from the VARIABLES section, the context menu will present shortcuts to features such as: * `Set Value`: you can set/modify simple string, numeric, pointer values. Using composite literals, or memory allocation is not supported. * `Copy Value`: this copies the value in clipboard. * `Copy as Expression`: this is useful when you need to query from the REPL in the DEBUG CONSOLE panel. * `Add to Watch`: this will automatically add the expression to the WATCH section. Shadowed variables will be marked with `()`.
⚠️ Delve debugger imposes variable loading limits to prevent loading too many variables at once and negatively impacting debugging latency. The `dlv-dap` mode uses a different approach. It takes advantage of the interactive UI features to provide on-demand loading of individual variables, paging of arrays, slices and maps and increased string limits depending on the context. We continue to explore additional interactive features to balance performance and usability of variable loading and look forward to your feedback. You can inspect variables and evaluate expressions from the DEBUG CONSOLE panel too. Acceptable expressions are either * A valid [Delve expression](https://github.com/go-delve/delve/blob/master/Documentation/cli/expr.md), or * `call
Variables and expressions accepted in DEBUG CONSOLE can be also registered in the Run view’s WATCH section, so they can be evaluated automatically as you debug. The "Add to Watch" feature from the VARIABLES section is convenient when you want to register interesting variables. ⚠️ Function call feature is highly EXPERIMENTAL due to the limitation in Go runtime. Registering function calls in the WATCH section can often be problematic. Pause, stop, and disconnect will not work while a function call is running. Hover over variables in editors during debugging shows the value of the variable. For this feature, VS Code extracts the variable expression and makes a request to the debugger to evaluate the expression. Delve evaluates the expression relative to the highlighted stack frame chosen in the CALL STACK. By default, that is the current top-most frame.
⚠️ Limitation * VS Code heuristically determines the variable expression without full understanding of the scope & the currently selected frame. Delve tries to evaluate the provided expression in the selected frame. As a result, hover over variables outside the selected frame’s function may present incorrect information. ### Call stack You can inspect all goroutines and their stacks in the CALL STACK section. The CALL STACK section UI allows switching between goroutines or selecting a different stack frame. As a different stack frame or different goroutine is selected, the scope shown in the VARIABLE section will be updated for the newly selected stack frame, and the expressions in the WATCH section will be automatically reevaluated relative to the newly selected stack frame.
1. Goroutine stacks are annotated with their internal goroutine IDs. 2. The current goroutine is marked with `*`. If multiple goroutines stop (e.g. hit breakpoints) concurrently, Delve will pick one randomly. There also might not be a current goroutine (e.g. deadlock, pause or internal breakpoint hit by a system thread not running a goroutine). 3. If you click a goroutine call stack from the CALL STACK section, the goroutine is _selected_. 4. You can select a frame of the selected goroutine. The VARIABLE and WATCH sections will be updated accordingly and the cursor in the editor will be moved to the corresponding location in the source code. 5. Runtime stack frames are deemphasized (greyed out or collapsed). 6. Thread IDs are shown for scheduled goroutines. 7. Stop reason. It’s possible that there are multiple reasons goroutines were stopped, but currently only one reason is presented. 8. File name and line number of the frame. 9. You can trigger a debug action with the selected goroutine. Note: Resuming or stopping only a single goroutine (Go Issue [25578](https://github.com/golang/go/issues/25578), [31132](https://github.com/golang/go/issues/31132)) is currently not supported, so the action will cause all the goroutines to get activated or paused. 10. Function name of the frame. When the program stops due to exception, panic, or bad access error, the CALL STACK shows the stop reason and the editor highlights the source location with more details. ## `dlv` command from DEBUG CONSOLE DEBUG CONSOLE accepts commands that allow users to dynamically inspect/change debug configuration, or inspect the list of source code compiled in the debugged binary. Use `dlv help` and `dlv config -list` from the DEBUG CONSOLE panel to see the list of supported commands and dynamically adjustable settings. ## Configuration ### Launch.json attributes There are many attributes that you can adjust in the launch and attach debug configuration. The following general attributes are mandatory for all launch configurations. * `name`: the name of your configuration as it appears in the drop-down in the Run view. * `type`: the debugging type VS Code uses to decide which debugging extension should be used. Always leave this set to `"go"`. * `request`: `launch` or `attach`. Here is the list of attributes specific to Go debugging. | Property | Launch | Attach | | --- | --- | --- | | `args` | Command line arguments passed to the debugged program.
Allowed Values: `"default"`, `"native"`, `"lldb"`, `"rr"`
|
Allowed Values: `"internalConsole"`, `"integratedTerminal"`, `"externalTerminal"`
(Default: `internalConsole`)
| (Experimental) Where to launch the debugger: internal console, integrated terminal, or external terminal. This does not affect tty of the running program. It is ignored in remote debugging.
Allowed Values: `"internalConsole"`, `"integratedTerminal"`, `"externalTerminal"`
(Default: `internalConsole`)
|
| `coreFilePath` | Path to the core dump file to open. For use on 'core' mode only
(Default: `""`)
|
Allowed Values: `"legacy"`, `"dlv-dap"`
(Default: `dlv-dap`)
|
Allowed Values: `"debugger"`, `"gdbwire"`, `"lldbout"`, `"debuglineerr"`, `"rpc"`, `"dap"`
(Default: `"debugger"`)
|
Allowed Values: `"auto"`, `"debug"`, `"test"`, `"exec"`, `"replay"`, `"core"`
(Default: `auto`)
| Indicates local or remote debugging. Local is similar to the `dlv attach` command, remote - to `dlv connect`
Allowed Values: `"local"`, `"remote"`
(Default: `local`)
|
| `output` | Output path for the binary of the debugee.
(Default: `"debug"`)
|
Option 1: Use process picker to select a process to attach, or Process ID as integer.
Allowed Values: `"${command:pickProcess}"`, `"${command:pickGoProcess}"`
Option 2: Attach to a process by name. If more than one process matches the name, use the process picker to select a process.
Option 3: The numeric ID of the process to be debugged. If 0, use the process picker to select a process.
(Default: `0`)
|
| `program` | Path to the program folder (or any go file within that folder) when in `debug` or `test` mode, and to the pre-built binary file to debug in `exec` mode. If it is not an absolute path, the extension interpretes it as a workspace relative path.
(Default: `"${workspaceFolder}"`)
|
Allowed Values: `"verbose"`, `"trace"`, `"log"`, `"info"`, `"warn"`, `"error"`
(Default: `"error"`)
|
## Advanced topics ### Go debug extension architecture overview VS Code implements a generic, language-agnostic debugger UI based on [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/) (DAP), an abstract protocol for communicating with debugger backend. Previously, the Go extension used an intermediary typescript program (legacy debug adapter) to launch Delve and adapt Delve to DAP. With [the new, native DAP implementation in Delve](https://github.com/go-delve/delve/tree/master/service/dap), the intermediary program is no longer necessary, and efficient and tight integration with Delve becomes possible.
For information on debugging using the legacy debug adapter, please see the old [Debugging Documentation](debugging.md). Note that many new or enhanced features discussed in this document may not be available with the legacy debug adapter. ### Handling STDIN The Go extension and `dlv` started as a subprocess of the extension do not have access to `tty`. The Go extension captures and forwards STDOUT/STDERR of the debug program to VS Code, so they can appear in `DEBUG OUTPUT` panel. But this arrangement does not handle STDIN. When the target program needs to read from STDIN or access terminals (`tty`), use the `"console"` launch option that controls where the `dlv` debugger and the target process run: * `integratedTerminal` for the terminal inside VS Code * `externalTerminal` for the terminal outside VS Code The Go extension delegates interaction with terminals to VS Code [using Debug Adapter Protocol's `RunInTerminal` functionality](https://github.com/golang/vscode-go/discussions/1626). For configuring VS Code's terminal related behavior, see VS Code's [documentation](https://code.visualstudio.com/docs/editor/integrated-terminal). ### Debug programs and tests as root In order to run and debug a program or a package test running as root, the debugger (`dlv`) must run with root privilege, too. You can start the debug session with root privilege utilizing the `"asRoot"` AND `"console"` launch options. This is currently supported only on Linux and Mac. When `asRoot` is true, the Go extension will use the `sudo` command to run `dlv`. Since `sudo` may ask you to enter password, the debug session needs [terminal access](#handling-stdin) so set `"console": "integratedTerminal"` or `"console": "externalTerminal"` in the launch configuration. #### Debug a program as root For example, the following launch configuration will start `myprogram` and debug it by running `sudo dlv dap` command in the integrated terminal. ```json { "name": "Launch as Root", "request": "launch", "mode": "exec", "asRoot": true, "program": "${workspaceRoot}/myprogram", "console": "integratedTerminal", ... } ``` The `asRoot` setting can be used with `auto`/`test`/`debug` launch modes that **build** the target binary to debug. That means the `go` command will be invoked as root to compile the binary, too. This can cause issues: * by default, `sudo` does not preserve the user's current environment variables (see documentations about sudo's `--preserve-env` option). For example, `PATH` or library paths required for build may be different. * Go environment variable settings usually associated in the home directory are different. * Module/build caches used during build as root may be different from the caches used in your normal build. If they are the same, you may encounter permission errors due to cache data written to the caches as root. Instead, you can arrange the `exec` launch mode to work with a pre-launch [task](https://code.visualstudio.com/docs/editor/tasks). First, configure a debug build task to compile the target binary. In `.vscode/tasks.json`: ```json { ... "tasks": [ { "label": "go: build (debug)", "type": "shell", "command": "go", "args": [ "build", "-gcflags=all=-N -l", "-o", "${fileDirname}/__debug_bin" ], "options": { "cwd": "${fileDirname}" }, ... } ] } ``` The `-gcflags=all=-N -l` flag tells the `go build` command to preserve the debug information. The `-o` flag causes the compiled binary to be placed in `"${fileDirname}/__debug_bin"`. Extra build flags and environment variables _used for build_ should be configured here as `args` or `options`'s `env` settings. It might be useful to add `__debug_bin` to your `.gitignore` to avoid debugging binaries getting checked-in into your repository. Then, configure the launch config to run the task before starting debugging. In `.vscode/launch.json`: ```json ... "configurations": [ { "name": "Launch Package as root", "type": "go", "request": "launch", "mode": "exec", "asRoot": true, "console": "integratedTerminal", "program": "${fileDirname}/__debug_bin", "preLaunchTask": "go: build (debug)", } ] ``` Settings (`args`, `cwd`, `env`, ...) configured in the above `launch.json` will only apply when _running_ the compiled binary, not when building the binary. #### Debug a package test as root To debug package tests as root add the following launch and task configurations. In `.vscode/tasks.json`: ```json ... "tasks": [ { ... }, { "label": "go test (debug)", "type": "shell", "command": "go", "args": [ "test", "-c", "-o", "${fileDirname}/__debug_bin" ], "options": { "cwd": "${fileDirname}", }, ... } ] ``` In `.vscode/launch.json`: ```json ... "configurations": [ { ... }, { "name": "Debug Package Test as root", "type": "go", "request": "launch", "mode": "exec", "asRoot": true, "program": "${fileDirname}/__debug_bin", "cwd": "${fileDirname}", "console": "integratedTerminal", "preLaunchTask": "go test (debug)" } ] ``` ### Manually install `dlv` On rare occasions, you may want to install `dlv` by yourself instead of letting the extension handle its installation. First, find where the Go extension finds tools. Like [other tools the extension uses](tools.md#tools), the Go extension searches the `dlv` executable from `${GOPATH}/bin`, `${GOBIN}` and `${PATH}` (or `Path` in Windows). So, install `dlv` in the directory. The easiest way to check the tool installation location the Go extension uses is currently by running the `Go: Locate Configured Go Tools` command from the command palette (⇧+⌘+P or Ctrl+Shift+P). If your Go version is 1.16 or newer: ``` GOBIN=
Use `Continue` to resume program execution. If you do not want the step request to be interrupted, you can disable all breakpoints from VS Code from the context menu in the `Breakpoints` view.
### My program does not stop at breakpoints Check the "BREAKPOINTS" section in the debug view and see if the breakpoints are [greyed out](https://code.visualstudio.com/docs/editor/debugging#_breakpoints) when your debug session is active. Setting `stopOnEntry` is a great way to pause execution at the start to _verify_ breakpoints are set correctly. Or [enable logging](#collecting-logs) and see if `setBreakpoints` requests succeeded with all the breakpoints _verified_. This problem often occurs when the source location used in compiling the debugged program and the workspace directory VS Code uses are different. Common culprits are remote debugging where the program is built in the remote location, use of symbolic links, or use of `-trimpath` build flags. In this case, configure the `substitutePath` attribute in your launch configuration. #### Trimpath tips If you are using `-trimpath` to build your program, you need to add entries to substitute path to let the debugger know how to map the package paths that are compiled in the binary to the files that you are looking at in the editor. Here are some tips for configuring substitutePath. This assumes that your program is using module mode, which is the default. One rule that you will need will map your main module. The mapping will map `"from"` the file path to the directory containing the module, `"to"` the module path. You will also need to create a similar mapping for all dependencies. These include modules in the module cache, vendored modules, and the standard library. ```json "substitutePath": [ // Main module. { "from": "${workspaceFolder}", "to": "moduleName", }, // Module cache paths. { "from": "${env:HOME}/go/pkg/mod/github.com", "to": "github.com", }, { "from": "${env:HOME}/go/pkg/mod/golang.org", "to": "golang.org", }, ... // Standard library paths. // This rule should come last since the empty "to" will match every path. { "from": "/path/to/local/goroot/pkg" , "to": ""} ], ``` Since rules are applied both from client to server and server to client, rules with an empty string will be applied to _all_ paths that it sees, so even dependencies will be mapped to `"/path/to/module"`. We plan to make this easier in the future. Progress can be tracked in the issue tracker [golang/vscode-go#1985](https://github.com/golang/vscode-go/issues/1985). ### Debug sessions started with the "debug test" CodeLens or the test UI does not use my `launch.json` configuration The "debug test" CodeLens and the [test UI](features.md#test-and-benchmark) do not use the `launch.json` configuration ([Issue 855](https://github.com/golang/vscode-go/issues/855)). As a workaround, use the `go.delveConfig` setting and the `go.testFlags` setting. Please note that these all apply to all debug sessions unless overwritten by a specific `launch.json` configuration. ### Starting a debug session fails with `decoding dwarf section info at offset 0x0: too short` or `could not open debug info` error These errors indicate that your binary was built with linker flags that stripped the symbol table (`-s`) or the DWARF debug information (`-w`), making debugging impossible. If the binary is built while launching the session, make sure your `launch.json` configuration does not contain `"buildFlags": "--ldflags '-s -w'"`. If you use `debug test` or Test Explorer, check `go.buildFlags` in `settings.json`. If the binary is built externally, check the command-line flags and do not use `go run`. Unlike `go build`, `go run` passes `-s -w` to the linker under the hood. If you try to attach to such a binary with a debugger, it will fail with one of the above errors (see Go Issue [24833](https://github.com/golang/go/issues/24833)). Instead let dlv build the binary for you or use `go build -gcflags=all="-N -l"`. ## Report issues When you are having issues in `dlv-dap` mode, first check if the problems are reproducible after updating `dlv` and using the most recent version of `dlv`. It's possible that the problems are already fixed. You can also try to install `dlv` at tree head. Follow the instruction for [updating `dlv`](#staying-up-to-date) and [updating extension](https://code.visualstudio.com/docs/editor/extension-gallery#\_extension-autoupdate). Please report issues in [our issue tracker](https://github.com/golang/vscode-go/issues) with the following information. * `go version` * `go version -m