Delve Debugger for Go
repository·master·Indexed 12 days ago
https://github.com/go-delve/delveA specialized debugger for the Go programming language designed to be simple, full-featured, and unobtrusive. Delve provides a CLI, a JSON-RPC API (supporting API version 2), and a Debug Adapter Protocol (DAP) interface for integration with IDEs like VS Code. It supports headless mode for remote debugging, multi-client sessions, and advanced breakpoint management including conditional triggers and hitcounts.
What's inside Delve
- Delve is a debugger specifically designed for the Go programming language. Its primary goal is to provide a simple, full-featured debugging tool that is easy to invoke and stays out of the developer's way during the debugging process.
Overview of Delve debug modes
masterDelve provides several different modes of operation depending on whether you are starting a new process, attaching to an existing one, or examining a crash dump. Key modes include:
dlv debug: Compiles and begins debugging the main package in the current directory (or a specified package).dlv exec: Executes a precompiled binary and begins a debug session.dlv attach: Attaches to a currently running process.dlv test: Compiles a test binary and begins debugging the program.dlv core: Examines a core dump file.dlv connect: Connects to a headless debug server using a terminal client.dlv dap: Starts a headless TCP server communicating via the Debug Adaptor Protocol (DAP).dlv replay: Replays anrrtrace.dlv trace: Compiles and begins tracing a program.
Supported Go expressions in Delve
masterDelve supports a subset of the Go expression language for evaluation during debugging. Supported features include:
- Arithmetic/Logic: All binary and unary operators on basic types (except
<-,++, and--). - Comparison: Comparison operators on any type.
- Type Casting:
- Between numeric types.
- Integer constants to/from any pointer type.
- Between
string,[]byte, and[]rune.
- Accessors: Struct member access (
somevar.memberfield), slicing/indexing for arrays, slices, and strings, and map access. - Pointers: Pointer dereferencing.
- Built-in Functions:
cap,len,complex,imag, andreal. - Interfaces: Type assertion on interface variables (e.g.,
somevar.(concretetype)).
- Arithmetic/Logic: All binary and unary operators on basic types (except
Control the Delve backend via JSON-RPC
masterDelve's API is implemented using the JSON-RPC 1.0 specification. The methods of the
service/rpc2.RPCServerare exposed through this connection.Every request follows a pattern where you provide
args(the input) and receiveout(the result). For example, to create a breakpoint, you wrap aCreateBreakpointInobject into a JSON-RPC envelope and send it to theRPCServer.CreateBreakpointmethod.{ "method": "RPCServer.CreateBreakpoint", "params": [ { "Breakpoint": { "file": "/User/you/some/file.go", "line": 16 } } ], "id": 27 }Rules for using build tags and runtime checks
masterDelve supports cross-platform core file reading (e.g., reading a Linux/arm64 core file on Windows/amd64). Because of this, using
runtime.GOOS,runtime.GOARCH, or standard build tags is generally forbidden outside ofpkg/proc/nativeand test files.When writing code for other packages, use these abstractions instead:
- OS: Use
proc.BinaryInfo.GOOSinstead ofruntime.GOOS. - Architecture: Use
proc.BinaryInfo.Arch.Nameinstead ofruntime.GOARCH. - Pointer Size: Use
proc.BinaryInfo.Arch.PtrSize()instead ofunsafe.Sizeof. - Addresses: Use
uint64for address-sized integers instead ofuintptr. - File Naming: Use
amd64_filename.goinstead of the build tag versionfilename_amd64.go.
- OS: Use
Caveats when debugging the Go runtime
masterDebugging the Go runtime is possible but has specific limitations:
- Optimizations: The
runtimepackage is compiled with optimizations and inlining. Variables may be unavailable or stale, and line numbers may occasionally be inaccurate. - Stepping through runtime functions: Standard
next,step, andstepoutcommands may fail if the runtime function modifies thecurgpointer. Use thestep-instructioncommand instead. - Viewing g0 stacktraces: When executing a stacktrace from
g0, Delve may automatically switch to the goroutine stack. To see theg0stacktrace specifically, usestack -mode simple. - Stepping into private runtime functions: To step into a private runtime function inserted by the compiler into user code, set a breakpoint and use the condition:
runtime.curg.goid == <current goroutine id>.
- Optimizations: The
Create custom commands in Starlark
masterYou can define custom commands in Starlark by defining functions with the prefix
command_.Argument Handling:
- Single Argument: If your function takes one argument (e.g.,
def command_echo(args):),argsis received as a single Starlark string containing all arguments passed from the CLI. - Expression Arguments: If your function defines multiple parameters (e.g.,
def command_echo_expr(a, b, c):), Delve will parse the CLI arguments as Starlark expressions, allowing you to pass math or logic (like2+2) directly.
To use your commands, load your script using the
sourcecommand.# Single string argument def command_echo(args): print(args) # Multiple expression arguments def command_echo_expr(a, b, c): print("a", a, "b", b, "c", c)- Single Argument: If your function takes one argument (e.g.,
Handle simultaneous breakpoints and goroutines
masterBecause of Go's concurrency, multiple goroutines may hit breakpoints at the same time.
Best Practices for Clients:
- Signal all hits: Do not just signal the first breakpoint. Iterate through the
Threadsarray in theDebuggerStateand identify all threads where theBreakpointmember is non-nil. - Identify the Selected Goroutine: When multiple breakpoints are hit, Delve chooses a
SelectedGoroutinerandomly among those stopped. - Prefer
SelectedGoroutine: Always use theSelectedGoroutinefield fromDebuggerStateto identify the active goroutine. IgnoreCurrentThreadunlessSelectedGoroutineisnil.
- Signal all hits: Do not just signal the first breakpoint. Iterate through the
Understand Single-Client vs Multi-Client DAP modes
masterHow the Delve server behaves upon disconnection depends on the mode it was started in:
Single-Client Mode
Triggered by:
dlv dapordlv --headless --accept-multiclient=false(default).- On Disconnect: The DAP server shuts down when the client sends a
disconnectrequest. If the debuggee was launched by Delve, it is also terminated. If attached, theterminateDebuggeeoption determines if the process is killed. - On Program Termination: Delve sends a
terminatedevent. The client is expected to follow up with adisconnectrequest to shut down the server. - On Error/SIGTERM: The server shuts down, taking down launched processes but leaving attached processes running.
Multi-Client Mode
Triggered by:
dlv --headless --accept-multiclient=true.- On Disconnect: If a client disconnects or a connection fails, the server remains running. The debuggee stays in its current state (running or halted), allowing new clients to connect.
- On Shutdown: The client must explicitly request full shutdown of the server and debuggee using the
terminateDebuggeeoption. - On SIGTERM: The server shuts down, taking down launched processes but leaving attached processes running.
- On Disconnect: The DAP server shuts down when the client sends a
Handle nesting and element limits in output
masterTo prevent massive output, Delve imposes limits on how much data is returned during evaluation:
Nesting Limit
By default, Delve limits evaluation to two levels deep. Beyond two levels, only the memory address is returned. To inspect deeper levels, you must explicitly target them:
- Use a specific index:
print c1.sa[0] - Use a direct pointer dereference:
print *(*main.astruct)(0xc82000a440)
Elements Limit
For arrays, slices, strings, and maps, Delve returns a maximum of 64 elements at a time. To view more, use the slice operator:
print ba[64:](to see elements from index 64 onwards).- For maps,
m[64:]returns the key/value pairs following the first 64 pairs (Delve uses a fixed ordering for map iteration).
These limits can be adjusted using the
max-string-lenandmax-array-valuesconfiguration settings.# Example: Accessing elements beyond the 64-element limit (dlv) print ba[64:]- Use a specific index:
Choose between raw_command and dlv_command
masterWhen resuming execution in a Starlark script, you have two options:
dlv_command("command"): Equivalent to typing the command directly into the(dlv)prompt. This is generally preferred as it behaves as expected for standard Delve commands likecontinue.raw_command("command"): Maps directly to the underlying RPCCommandAPI. Use this only if you need behavior that diverges from the standard command-line interface.
# Preferred way to continue execution dlv_command("continue") # Alternative (direct API call) raw_command("continue", ...)Understand Delve API interfaces
masterDelve provides two primary API interfaces to allow external frontends (such as IDEs and editors) to interact with the debugger programmatically. The core debugging logic is abstracted from these transport implementations.
Supported interfaces:
- JSON-RPC: Used by the built-in terminal client. This interface is updated in lockstep with new Delve features.
- DAP (Debug Adapter Protocol): A generic, industry-standard protocol used by many modern development tools.