Debugging with GoLand – Getting Started

Debugging is an essential part of any modern application lifecycle. It’s not only useful for finding bugs as programmers often use debuggers to see and understand what happens in a new codebase they have to work with or when learning a new language.

There are two styles of debugging which people prefer:

  • print statements: which is logging as your code executes various steps that might run
  • using a debugger such as Delve, either directly or via an IDE: this gives more control over the execution flow, more capabilities to see what the code does which may not have been included in the original print statement, or even changing values during the application runtime or going back and forward with the execution of the application.

In this series we’ll focus on the second option, using an IDE to debug an application.

As you noticed from the description above, doing so provides a lot more control and capabilities to find bugs and as such this article is broken down in several sections:

And after a look at all the above scenarios, we’ll see how GoLand handles them so that you can have the same set of features listed below regardless of where your application is running:

  • the basics of debugging
    • controlling the execution flow
    • evaluating expressions
    • watching custom values
    • changing variable values
  • working with breakpoints

The IDE also supports debugging core dumps produced on Linux and using Mozilla’s rr reversible debugger on Linux. We’ll see these features in upcoming, separate blog posts.

We’ll use a simple web server in all the above applications, but these can be applied to any kind of application, CLI tools, GUI applications, etc.

We’ll use Go Modules, but the default GOPATH using any other dependency management form can work just as well.

Create the application using the Go Modules (vgo) type, and make sure you have Go 1.11+ or newer.
If you don’t have Go 1.11 or want to use the GOPATH mode, then choose Go.

The application can be found below:

And we can also create a test file like this:

For Go Modules enabled applications, you can invoke the Alt+Enter shortcut then Sync packages of <my project>.
For non-Go Modules enabled applications, you can invoke the Alt+Enter shortcut then go get -t <missing dependency>.

One final information that we should note is that the debugging experience is also impacted by the Go version being used to compile the target program. With each Go version released, the Go Team works to add more debugging information and improve the quality of the existing one, and this can be seen when switching from an older version such as Go 1.8 to Go 1.9 or in a more dramatic way, from Go 1.8 to Go 1.11. So, the newer the Go version you can use, the better your experience will be.

Now that all our code is in place let’s start debugging it!

Debugging an application

To debug our application, we can click on the green triangle and then select Debug ‘go build main.go’.
Alternatively, we can right-click on a folder and choose Debug | go build <project name>.

Debugging with GoLand - 1

Debugging tests

This can be done similarly to debugging an application. GoLand recognizes tests from the standard testing package, gocheck, and testify frameworks, so these actions are available for all of them straight from the editor window.

For other frameworks you may need to configure a custom test runner by going to Run | Edit Configurations… and specifying additional arguments either in the Go tool arguments or in Program arguments, depending on where the custom library you are using needs those arguments.

Debugging with GoLand - 2

Debugging a running application on the local machine

There are cases where you would want to debug an application that’s launched outside of the IDE.
One of such cases is when the application runs on the local machine.
To run this using the debugger, then open the project in the IDE and select Attach to Process…

If it’s the first time you are using this feature, then the IDE will ask you to download a small utility program named gops, available at https://github.com/google/gops. This program helps the IDE find Go process running on your machine. Then invoke the Attach to Process… feature again.

You’ll see a list of all Go projects running on your computer, so who knows, maybe you’ll even discover new ones. Select the one that you want to debug from the list, and the debugger will attach to the process, and you can start your debugging session.

To ensure that the debugging session is successful and you can debug the application without issues, all you need to do is to compile your application with a special flag. The IDE will add these flags automatically for the other configuration types, so these are necessary only when compiling the application manually.

If you are running with Go 1.10 or newer, you need to add -gcflags="all=-N -l" to the go build command.
If you are running with Go 1.9 or older, then you need to add -gcflags="-N -l" to the go build command.

Important note! Some people also use the -ldflags="all=-w" or -ldflags="-w" , depending on the Go version being used.
This is incompatible with debugging the application as it strips out the necessary DWARF information needed for Delve to work.
As such the application will not be able to be debugged.
A similar problem will be encountered when using symbolic links, or symlinks, on operating systems and file systems which support this feature. Due to an incompatibility between the Go toolchain, Delve, and the IDE, using symlinks is currently not compatible with debugging an application.

Debugging with GoLand - 3

Debugging a running application on a remote machine

Lastly, this case is a more complex one, at least for now. This debugging session type allows you to connect the IDE to a remote target and debug a process running there. By remote target, we can consider containers running on the local machine remote targets or actual servers either on-premise or in the cloud.

Much like with running against applications running on your local machine, you have to be careful about the compiler flags that you use to compile the application.
After that, you need to compile Delve with the same Go version and host/target as your application as there might be slight differences between the various operating systems which could cause the debugging session not to work as expected.

You should also make sure that if you are using $GOPATH, the project is compiled using the same relative path to $GOPATH. For example: if your project is available under github.com/JetBrains/go-sample, then both on the machine with the IDE and in the machine that compiles the application, the application is under $GOPATH/src/github.com/JetBrains/go-sample, $GOPATH can be different between these two machines. Then the IDE can automatically map the sources between the local and remote machine.

Then, when you deploy your application, also deploy the copy of Delve that was compiled earlier, and you have two options to launch the debugger there:

  • let the debugger run the process for you: if you choose this option, you need to run dlv --listen=:2345 --headless=true --api-version=2 exec ./application . Also note that if you use any firewall or containers, then you’ll need to expose the port 2345 in those configurations. The port number can be any random one you need, not just 2345 as long as it’s free on the host machine.
  • attaching to the process: you need to run dlv --listen=:2345 --headless=true --api-version=2 attach <pid> where <pid> is the process id of your application.

After all of this is done, the final step is to connect your IDE to the remote debugger. You can do so by going to Run | Edit Configurations… | + | Go Remote and configuring the host and port your remote debugger is listening on.

Debugging with GoLand - 4

You can use the container definition from the Dockerfile below:

Please note that in this Dockerfile, the project is named goland-debugging, but you should change this folder name to match the name of the project you created.
When running the Docker container, you also need to specify --security-opt="apparmor=unconfined" --cap-add=SYS_PTRACE arguments to it. If you do it from the command line, these are arguments to the docker run command. If you do it from the IDE, these options must be placed in the Run options field.

Debugging with GoLand - 5

That’s it for today. In the next article in the series, we’ll learn how to use the various features that are available to us during one of the debugging scenarios described above. Please let us know your feedback in the comments section below, on Twitter, or open an issue on our issue tracker.

About Florin Pățan

Developer Advocate at JetBrains
This entry was posted in Features, Tutorial and tagged , , , . Bookmark the permalink.

7 Responses to Debugging with GoLand – Getting Started

  1. Brendan says:

    Would it be possible to add manual directory mapping for the local and remote machine? With the addition of go modules, it is no longer necessary to keep src files in $GOPATH. It would be nice if we could do a find and replace on the path so the source maps match and we can set a breakpoint.

  2. Jim says:

    I’m trying to debug an application with GoLand 2019.1. The app has a startup script and I can connect to it once I start it up by using “Attach to Process”, but I can’t debug application startup, since the code that I need to set a breakpoint on has already run once by time the I connect to it. What can I do?

    • Florin Pățan says:

      In your case, it would probably be better to launch the application via “Run | Debug” option and configure all the settings required in the Run Configuration as that will launch the application directly via the debugger and allow you to set breakpoints in the initialization part.

      If this is not an option for you, please describe your use-case and I’ll try and find a workaround it.

  3. Eric says:

    Text above references the “go build command”.. I’m having trouble finding such a field in my run configuration….

  4. Artem says:

    Hi, I need to run a custom command as I am using a Buffalo framework. So I need to substitute “go test” with “buffalo test” command. Is there any possible way to do it?
    Thanks

Leave a Reply to Jim Cancel reply

Your email address will not be published. Required fields are marked *