Debug Go (golang) with emacs

ari mattila
4 min readJan 30, 2020

It is really possible to debug go code with emacs, but setup or using is not so straigth forward as you normally would expect.

If you want short cut take a look at my .emacs https://github.com/apmattil/.emacs

For debuging I use dap-mode (https://github.com/emacs-lsp/dap-mode)

dap-mode has two versions of golang support:

  • dap-go: older which uses microsoft extension that is no longer supported
  • dap-dlv-go: newer version that uses dap protocol directly to dlv.

Text at italic font apply only to older versio !

Things you need to install before you start and include to $PATH:

  • gopls
  • nodejs
  • dlv (for new dap-dlv-go, dlv must be version 1.7.3 or newer)
  • golangci-lint (I just like this linter)
  • (emacs version 27 and above has inbuilt json encoder/decoder)

Once use-package has installed needed packages dap-mode,lsp-mode, etc (see .emacs above) check that you get MS vscode extension installed .emacs.d/.extension/vscode/ms-vscode.go/extension/out/src/debugAdapter/goDebug.js

If it did not you can download it at vscode site (https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go), it is just zip file.

Now when you start debugging you need to tell gopls where your go.mod is, you can do this by M-x lsp-workspace-folder-add.

Next you propably need restart emacs.

Now finally you can start debug session:

  • open file containing your main package
  • M-x dap-debug
  • select ‘Go Dlv Launch File Configuration’
  • you should hit your breakpoint
  • once you are done with stepping the code and excutable has exited type q to quit dap-hydra (bottom window).

When you wanto quit the debug session use capital ‘Q’ at hydra, it will kill the dlv if it has not yet exited. You do not want to leave dlv’s hanging e.g your program is listening a port and is reserved when you start new debug session.

I did not like the hydra that dap-mode has (author recomeds to make your own) so I made my own, check my https://github.com/apmattil/.emacs , (hint type ‘q’ to close hydra window or ‘Q’ to kill dlv/session).

Soon you will need to pass cmd-line arguments.

Now templates come in, M-x dap-debug-edit-template

select e.g ‘Go Dlv Launch Executable Configuration’

;; Eval Buffer with `M-x eval-buffer' to register the newly created template.(dap-register-debug-template
"Launch Executable"
(list :type "go"
:request "launch"
:name "Launch Executable"
:mode "exec"
:program nil
:args "--help"
:env nil
:envFile nil))

here I edited the args to contain — help to pass to executable.

M-x eval-buffer (no need to save it) will add this to your selection list when you next time start debug session with dap-debug.

You can also put your own templates to you .emacs

Next remote debugging.

You need to start dlv at headless mode and with dap at your target host listening 8181.

dlv exec ari --headless --accept-multiclient -l :8181

To trouble shoot connectivity issues you can add ‘— log — log-output=dap,rcp‘ flags to dlv.

You need to start dlv at headless mode and with api 2 version at your target host (here localhost/127.0.0.1:8181)

dlv debug --headless --api-version=2--listen=127.0.0.1:8181

(some dlv versions have bug that when you pass — log it will not work)

Now if you start ‘ Go Dlv Remote dlv Debug’ template and pass host and port.

for docker you need to setup port forwarding:

done at around middle of this video : https://www.youtube.com/watch?v=2kjmLQY8RJk

sometimes you might need to use ssh port forwarding: TBD

Also take a look official instructions https://emacs-lsp.github.io/dap-mode/page/configuration/#go

Every thing is new and shiny so all does not work, e.g variable values are not always show.. instead some cryptic error values. When I look debug logs the problem seems to be somewhere else than in emacs or dap-mode.

What’s happening behind the scenes.

LSP (Language Server Protocol)is a protocol driven by MS and gopls acts as lsp-server.

lsp is used to run different tools, like godoc,etc so editor does not need to know about them.

Editor uses json protocol to talk with the lsp-server.

if you enable (setq lsp-log-io nil) at lsp-mode config: section you can see messages emacs (lsp-mode) is sending to gopls at different lsp-buffers.

DAP (Debug Adapter Protocol) is also driven by MS and it also uses json.

if you enable (setq dap-print-io t) at dap-mode config: section you see dap-mode at message buffer.

goDebug.js will talk to dlv.

if you set :trace “verbose” at dap-debug-template you can see logs (/tmp/vscode-go-debug.txt) from goDebug.js talking to dlv.

there is a new dap interface coming to dlv, where dlv at remote is started with ‘dlv dap’.

This mode will improve how e.g server knows how to start program.

--

--