Tutorial

Debugging Go Code with Visual Studio Code

GoDevelopmentVS Code

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

This article will go over how to debug your Go code, so it goes without saying that a prerequisite for this article is that you understand the basics of writing Go.

If you do not have experience writing Go, and would like to learn, we recommend that you check out A Tour of Go to start your Go journey.

Setup

You need have the following requirements met:

  1. You know enough about Go to write a small program, or even a small http server.
  2. You have go installed in your machine and the environment variable $GOPATH is set. (It defaults to ~/go)
  3. The path $GOPATH/bin is exposed to your PATH.
  4. You have Visual Studio Code installed.
  5. You have the VSCode-Go plugin installed.

Once you have this installed, when open any .go files in VSCode, you will be prompted on the bottom right of the status bar to Install Analysis Tools. Click on that link to install the necessary Go packages for the plugin to work efficiently.

We finally need to install Delve, an open-source debugger for Go. To do this, there are detailed installation instructions for specific platforms.

Creating a Sample App

We’ll use two examples to debug our Go code.

  • A Go program that generates a JSON.
  • We’ll write a function, write the test and see how we debug tests in VS Code

Here’s the source code for the first example. Create a file main.go.

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

// Avenger represents a single hero
type Avenger struct {
    RealName string `json:"real_name"`
    HeroName string `json:"hero_name"`
    Planet   string `json:"planet"`
    Alive    bool   `json:"alive"`
}

func (a *Avenger) isAlive() {
    a.Alive = true
}

func main() {
    avengers := []Avenger{
        {
            RealName: "Dr. Bruce Banner",
            HeroName: "Hulk",
            Planet:   "Midgard",
        },
        {
            RealName: "Tony Stark",
            HeroName: "Iron Man",
            Planet:   "Midgard",
        },
        {
            RealName: "Thor Odinson",
            HeroName: "Thor",
            Planet:   "Midgard",
        },
    }

    avengers[1].isAlive()

    jsonBytes, err := json.Marshal(avengers)
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(string(jsonBytes))
}

Here we are just defining a struct Avenger, and then creating an array of avengers, changing the status of one of them to alive, then converting the results to JSON, and finally printing it to STDOUT.

You can run the app with

go run main.go

[{"real_name":"Dr. Bruce Banner","hero_name":"Hulk","planet":"Midgard","alive":false},{"real_name":"Tony Stark","hero_name":"Iron Man","planet":"Midgard","alive":true},{"real_name":"Thor Odinson","hero_name":"Thor","planet":"Midgard","alive":false}]

Debugging with Breakpoints

To get started with debugging, we need to create a configuration. Click on the Debug Icon on the left pane of Visual Studio Code. Next, click on the gear Icon to create a configuration.

Debug and Settings/Gear icons

A configuration file is created under .vscode/launch.json with the contents shown above. Change the configurations program to point to the main.go file. In this instance, since we only have a main.go file, we can change it to our workspace root:

"program": "${workspaceRoot}",

We’re set. Next we need to add a breakpoint, because that’s what debugging is all about.

Let’s add a breakpoint on line 21 func main() by clicking to the left of the line number. There, you will see a red dot.

Red dot to the left of line 22

Next, either press F5 or Click on the Launch button with a green play button on the Debug Section on the top left to open the Debug View.

Visual Studio Code Debug View

Click twice on the Step Over button on the Debug Toolbar.

Step Over icon in Debug Toolbar

The debugger will have moved to line 40.

Debugger moved to line 40

The Debug Section on the left will give us the state of the current breakpoint position.

Debug section showing breakpoint state

We can see the state/value of the variables, at that particular time in the Variables section.

We can also see the the call stack, and at the moment the running function is the main function, and line 40.

You can continue Stepping Over, you’ll see the value of avengers changes once we are past the line. Tony Stark is Alive

Debug view showing Tony Stark is still alive

Adding Conditional Breakpoints

VSCode breakpoints provide you with an option to edit breakpoints by giving them an expression, which most of the time is usually a boolean expression.

For instance, on line 40, avengers[1].isAlive(), we could add a condition here that the breakpoint is only raised when the expression evaluates to true, as in avenger[1].Planet == "Earth"

To do this, right click on the breakpoint and select edit breakpoint.

"Edit breakpoint…" menu option

If you did not have a breakpoint, you can still right click, and you will be told to add a Conditional Breakpoint

"Add Conditional Breakpoint…" menu option

Let’s add the condition once we’ve selected any of the above. avenger[1].Planet == "Earth"

Adding condition 'avengers[1].Planet == "Earth"'

Now if you launch the debugger with F5, it will not stop at the breakpoint. The app will run fine, and you will see the results in the debug console.

Debug console results

Next, edit the code to match what we expect. Change Tony Stark’s planet to Earth:

// ....
{
    RealName: "Tony Stark",
    HeroName: "Iron Man",
    Planet:   "Earth",
},
//...

When we launch the debugger again with F5, the Debug View opens and the execution stops at the breakpoint. We can see the JSON is not displayed in the Debug Console

Debug View, no JSON displayed

Performing Further Debugging Tests

Let’s add a new function to our file, enabling addition arithmetic:

func add(a, b int) int{
    return a+b
}

Create a test file main_test.go in the same directory, with the following contents:

package main

import "testing"

func Test_add(t *testing.T) {
    a, b, c := 1, 2, 3
    res := add(a, b)
    if res != c {
        t.Fail()
    }
}

The code just adds two numbers, and the test just calls the function.

If you have VSCode-Go plugin installed however, you’ll see additional options at the top of the test function. run test and debug test

"run test" and "debug test" options

You can click on run test to run the test and see the results in the Output window.

To debug the test however, maybe because we can’t figure out something, all we need to do is add a breakpoint, like we did before, and click on debug test.

Add a breakpoint on line 10: if res != c. Then, click on debug test.

Adding breakpoint on line 10

The Debug View is opened again and we can use the Debug Tool to step over and inspect the state on the variables section on the left.

Conclusion

Debugging is a critical part of developing software, and with tools such as Visual Studio Code, our lives can be made much easier.

We’ve added the debugger to an example project to explain the concepts, but feel free to add the debugger to any of your existing projects and play around with it. You’ll end up reducing your fmt.Println statements used to log to see the value/state of code at a give point during its execution.

0 Comments

Creative Commons License