Go:
Using a debugger

How to:

Go provides a built-in facility for debugging called delve. It is a full-featured debugging tool that allows you to execute Go programs step by step, inspect program variables, and evaluate expressions.

To begin, you must first install delve. You can do this by running:

go get -u github.com/go-delve/delve/cmd/dlv

Now, let’s debug a simple Go program. Consider a program main.go:

package main

import "fmt"

func main() {
    message := "Debugging in Go"
    fmt.Println(message)
}

To start debugging this program, open a terminal in the project’s directory and execute:

dlv debug

This command compiles the program with optimizations disabled (to improve the debugging experience), starts it, and attaches a debugger to it.

Once delve is running, you’re in the interactive debugger shell. Here are a few basic commands:

  • break main.main sets a breakpoint at the main function.
  • continue resumes program execution until a breakpoint is hit.
  • print message will print the value of the message variable.
  • next advances the program execution to the next line.
  • quit exits the debugger.

The output when hitting the breakpoint and printing the variable might look like this:

Breakpoint 1 at 0x49ecf3 for main.main() ./main.go:6
> main.main() ./main.go:6 (hits goroutine(1):1 total:1) (PC: 0x49ecf3)
     1: package main
     2:
     3: import "fmt"
     4:
     5: func main() {
     6: =>    message := "Debugging in Go"
     7:       fmt.Println(message)
     8: }
(dlv) print message
"Debugging in Go"

Using these commands, you can step through your program, inspecting the state as you go to understand how it behaves, and identify any issues.

Deep Dive

The choice of delve as Go’s debugging tool of choice over traditional tools like GDB (GNU Debugger) is primarily due to the nature of Go’s execution model and runtime. GDB was not initially designed with the Go runtime in mind, making delve a more suitable choice for Go developers. Delve is specifically designed for Go, offering a more intuitive debugging experience for Go routines, channels, and other Go-specific constructs.

Furthermore, delve supports a wide array of features beyond those offered by basic GDB when working with Go programs. These include but are not limited to: attaching to running processes for debugging; conditional breakpoints; and evaluating complex expressions that may involve Go’s concurrency primitives.

While delve is the go-to debugger for many Go developers, it is worth noting that the Go toolchain also includes lighter-weight forms of debugging support, such as the built-in pprof tool for profiling and the trace tool for concurrency visualization. These tools can sometimes provide a faster or more high-level avenue for diagnosing program performance issues or concurrency bugs, which might be complementary or even preferable depending on the debugging context.