DEV Community

Si
Si

Posted on • Originally published at simondrake.dev on

Debugging with delve

Learn how to use Delve to debug your Go applications and tests.

What is delve?

Delve is an open source debugger for the Go programming language.

When should I use delve to debug my Go programmes?

If you use one of the popular Code Editors (e.g. VSCode, Golang) they will handle this for you. If you use VIM, and Print statements aren't cutting it, then this tutorial is for you.

Pre-requisites

  • Install Go
  • Have at least a basic working knowledge of the language
  • Install delve

Setting up

  • Create a new directory in a place of your choice
  • Run the go mod init command (e.g go mod init github.com/simondrake/biggest-num)
  • Create our pkg directory - mkdir -p pkg/biggestNum
  • Add a biggestNum.go file in the biggestNum directory with the following contents:
package biggestNum

func BiggestEvenNum(nums []int) int {
    var biggest int

    for _, n := range nums {
        if n > biggest && isEven(n) {
            biggest = n
        }
    }

    return biggest
}

func isEven(num int) bool {
    isEven := num%2 == 0

    return isEven
}
Enter fullscreen mode Exit fullscreen mode
  • Create another directory, separate from the one you created at the beginning of this section
  • Run go mod init example.com
  • Change your go.mod file to the following, which basically imports our biggestNum package without having to push it up to GitHub (change directory names as required):
module example.com

go 1.16

require (
    github.com/simondrake/biggestNum v1.0.0
)

replace github.com/simondrake/biggestNum => ../biggest-num
Enter fullscreen mode Exit fullscreen mode
  • Create a main.go file with the following contents (change import for biggestNum as required):
package main

import (
    "fmt"

    "github.com/simondrake/biggestNum/pkg/biggestNum"
)

func main() {
    b := biggestNum.BiggestEvenNum([]int{1, 4, 23, 12, 41, 51})

    fmt.Println(b)
}
Enter fullscreen mode Exit fullscreen mode
  • Run go run main.go and make sure you don't get any compilation errors.

Debugging your Application

We now have a Go application we can debug, so let's start the debugger with dlv debug and you should see the following:

$ dlv debug
Type 'help' for list of commands.
(dlv)
Enter fullscreen mode Exit fullscreen mode

Add a breakpoint at line 10 of our main.go file by running breakpoint main.go:10 (or b main.go:10 for short).

(dlv) b main.go:10
Breakpoint 1 set at 0x10cb9cf for main.main() ./main.go:10
Enter fullscreen mode Exit fullscreen mode

Run continue (or c) to run-to-breakpoint.

(dlv) c
> main.main() ./main.go:10 (hits goroutine(1):1 total:1)
     5:
     6:         "github.com/simondrake/biggestNum/pkg/biggestNum"
     7: )
     8:
     9: func main() {
=>  10:         b := biggestNum.BiggestEvenNum([]int{1, 4, 23, 12, 41, 51})
    11:
    12:         fmt.Println(b)
    13: }
Enter fullscreen mode Exit fullscreen mode

From here we could continue (c), which would run the programme to completion because there are no more breakpoints, but instead let's step-into the BiggestEvenNum method by running step (or s).

> github.com/simondrake/biggestNum/pkg/biggestNum.BiggestEvenNum() /path/to/biggest-num/pkg/biggestNum/biggestNum.go:3
     1: package biggestNum
     2:
=>   3: func BiggestEvenNum(nums []int) int {
     4:         var biggest int
     5:
     6:         for _, n := range nums {
     7:                 if n > biggest && isEven(n) {
     8:                         biggest = n
Enter fullscreen mode Exit fullscreen mode

Note how the first line gives us a handy reference to where we are (/path/to/biggest-num/pkg/biggestNum/biggestNum.go:3).

Now let's add another breakpoint in our biggestNum package, on line 7:

(dlv) b /path/to/biggest-num/pkg/biggestNum/biggestNum.go:7
Breakpoint 2 set at 0x10cb8ea for github.com/simondrake/biggestNum/pkg/biggestNum.BiggestEvenNum() /path/to/biggest-num/pkg/biggestNum/biggestNum.go:7
Enter fullscreen mode Exit fullscreen mode

Run continue to get to our next breakpoint.

(dlv) c
> github.com/simondrake/biggestNum/pkg/biggestNum.BiggestEvenNum() /path/to/biggest-num/pkg/biggestNum/biggestNum.go:7 (hits goroutine(1):1 total:1)
     2:
     3: func BiggestEvenNum(nums []int) int {
     4:         var biggest int
     5:
     6:         for _, n := range nums {
=>   7:                 if n > biggest && isEven(n) {
     8:                         biggest = n
     9:                 }
    10:         }
    11:
    12:         return biggest
Enter fullscreen mode Exit fullscreen mode

You can evaluate expressions by running print (or p)

(dlv) p n
1
Enter fullscreen mode Exit fullscreen mode

You can evaluate a function call by running call

(dlv) call isEven(n)
> github.com/simondrake/biggestNum/pkg/biggestNum.BiggestEvenNum() /path/to/biggest-num/pkg/biggestNum/biggestNum.go:7 (hits goroutine(1):2 total:2)
Values returned:
        ~r1: false
Enter fullscreen mode Exit fullscreen mode

You can continue to run the programme to the next iteration of the loop, or run next (or n) to go to the next line, but we're going to skip that by removing this breakpoint. First, get the name of the breakpoint with breakpoints (or bp) and then delete it

(dlv) clear 2
Breakpoint 2 cleared at 0x10cb8ea for github.com/simondrake/biggestNum/pkg/biggestNum.BiggestEvenNum() /path/to/biggest-num/pkg/biggestNum/biggestNum.go:7
Enter fullscreen mode Exit fullscreen mode

Then we'll stepout (or so) of this package

(dlv) so
> main.main() ./main.go:10
Values returned:
        ~r1: 12

     5:
     6:         "github.com/simondrake/biggestNum/pkg/biggestNum"
     7: )
     8:
     9: func main() {
=>  10:         b := biggestNum.BiggestEvenNum([]int{1, 4, 23, 12, 41, 51})
    11:
    12:         fmt.Println(b)
    13: }
Enter fullscreen mode Exit fullscreen mode

From here you can restart if you want to run the same debugger again, or continue until completion of the application.

Debugging tests

So far, we've seen how to debug a Go application. But what if we wanted to debug our biggestNum standalone? If you try running dlv debug you'll get an error.

$ dlv debug pkg/biggestNum/biggestNum.go
could not launch process: not an executable file
Enter fullscreen mode Exit fullscreen mode

But, delve does have a test subcommand we can use to run our tests in debug mode. So, first, let's add the following test under biggestNum_test.go:

package biggestNum

import (
    "testing"
)

func TestBiggestEvenNum(t *testing.T) {
    expected := 290

    out := BiggestEvenNum([]int{23, 51, 4, 32, 45, 290})

    if out != expected {
        t.Fatalf("'%d' did not match expected '%d'", out, expected)
    }
}
Enter fullscreen mode Exit fullscreen mode

To run the tests in debug mode, use dlv test

$ dlv test pkg/biggestNum/*.go -test.v
Type 'help' for list of commands.
Enter fullscreen mode Exit fullscreen mode

Then set a breakpoint as we did in the previous step

(dlv) b biggestNum.go:6
Breakpoint 1 set at 0x11607d3 for command-line-arguments.BiggestEvenNum() ./pkg/biggestNum/biggestNum.go:6``
Enter fullscreen mode Exit fullscreen mode

Run continue as we did before

(dlv) continue
> command-line-arguments.BiggestEvenNum() ./pkg/biggestNum/biggestNum.go:6 (hits goroutine(4):1 total:1)
     1: package biggestNum
     2:
     3: func BiggestEvenNum(nums []int) int {
     4:         var biggest int
     5:
=>   6:         for _, n := range nums {
     7:                 if n > biggest && isEven(n) {
     8:                         biggest = n
     9:                 }
    10:         }
    11:
Enter fullscreen mode Exit fullscreen mode

And then debug your package, as we did our application, in the previous section.

Closing thoughts

You've learnt the basics of debugging with delve, but you should refer to the help command output to learn about what else delve can do.

Discussion (0)