Hello,
The goal of this short how-to article is to summarize the various configurations and efforts I had to make in order to reach a debugging setup I like using Emacs.
We are going to use this small demo repository for this purpose. As you can see
in its README, I was able to produce a decent debugging experience (similar to
the very good gdb-many-windows
setup that is built-in for C-like languages)
You can access the full picture there
Short version
- Install
lsp-mode
,dap-mode
,gdb
, andrust-analyzer
- Set Emacs up with the correct configuration. I used (roughly)
(setq dap-cpptools-extension-version "1.5.1") (with-eval-after-load 'lsp-rust (require 'dap-cpptools)) (with-eval-after-load 'dap-cpptools ;; Add a template specific for debugging Rust programs. ;; It is used for new projects, where I can M-x dap-edit-debug-template (dap-register-debug-template "Rust::CppTools Run Configuration" (list :type "cppdbg" :request "launch" :name "Rust::Run" :MIMode "gdb" :miDebuggerPath "rust-gdb" :environment [] :program "${workspaceFolder}/target/debug/hello / replace with binary" :cwd "${workspaceFolder}" :console "external" :dap-compilation "cargo build" :dap-compilation-dir "${workspaceFolder}"))) (with-eval-after-load 'dap-mode (setq dap-default-terminal-kind "integrated") ;; Make sure that terminal programs open a term for I/O in an Emacs buffer (dap-auto-configure-mode +1))
M-x dap-cpptools-setup
- Then you can open a project and
M-x dap-edit-debug-template
to set up the template
What is Debug Adapter Protocol ?
The Debug Adapter Protocol (DAP) is a cousin of LSP, that describes how a client (Emacs here) can communicate with a debugger (rust-gdb here) to be controlled and send diagnostics.
Emacs has a DAP client with dap-mode
, which is very closely coupled to
lsp-mode
. If you want to use dap-mode
, I think it’s better to use lsp-mode
over eglot
as LSP client, if only because lsp-mode
will be able to provide a
simple one click Debug Test
option when you use M-x lsp-lens-show
.
So basically, you can imagine that the goal of DAP and dap-mode is to provide
for all languages/all debuggers the same amazing experience as with gdb and
gdb-many-windows
.
How to test this dap-mode with Rust ?
Clone repo
You can find a small test repository on my github, that shows a minimal example
of what to provide in a launch.json
file to be able to start debugging.
Install dependencies
You will need 2 Emacs packages, at least 2 binary dependencies, and internet.
lsp-mode
is an Emacs package that providesRun Test|Debug
click buttons over testsdap-mode
is an Emacs package that provides a client implementation to DAP.rust-gdb
(or justgdb
) is the debugger that will provide the DAP-compliant information to Emacs. Therust-gdb
version has more visualization primitives for Rust data typesrust-analyzer
is a LSP server, that will provide Emacs a way to detect tests and start debugging sessions.
After doing all that, you should use M-x dap-cpptools-setup
in order to
download the correct dependencies. They are coming from Microsoft, and you can
choose which version you will use through the custom variable in Emacs.
Start a normal run debugging session
When you open the src/main.rs
file in Emacs, and start lsp
, you should see that rust-analyzer
analyzes the repo to provide extra data. You can then (require 'dap-cpptools)
, in order to get access to the M-x dap-debug
command.
dap-debug
prompts you with multiple options/templates it knows. As I already
included a launch.json
file at the root of the repository, you should be able
to see 2 preconfigured debugging setups to start:
- Run Part A Debug (input.txt)
- Run Part A Release (input.txt)
Feel free to look at the json file to see the various customization options, it’s the easiest way to learn how to configure a debug target. The documentation for the keys is available on Microsoft website.
After choosing the Debug run for example, Emacs will automatically:
- compile the binary
- set a breakpoint at entry (that doesn’t always work it seems)
- run the program and open all auxiliary windows on breakpoint
Start a test debugging session
In order to start the tests, you only need to:
- make sure the lenses are active with
M-x lsp-lens-show
, - it will show “Run Test | Debug” buttons near each test,
- click the “Debug” button to start a debugging run of the test automatically.
Navigate dap-mode user interface
First, if the program doesn’t stop, you can manually go in the src/main.rs
buffer and use M-x dap-breakpoint-add
, before running dap-debug
again.
Once you’re stopped, you’re free to set additional breakpoints, or add variable
watchers with M-x dap-ui-expressions-add
. There is also a hydra to help with
usual debugger commands : M-x dap-hydra
Conclusion
Hopefully all those steps worked without issue, and now you’re able to debug Rust programs in Emacs using a nicer interface and “classic” debugger features.
This blog post is part of a larger effort of mine to try to fully leverage LSP for work, and being able to move away from “eprintln!() debugging” and having more debugging/specific test running capabilities within Emacs was important for me. Also, hopefully this small test project will be able to be included somehow as an example in a WIP that will enable to spawn small test sessions from anywhere, so that demo-ing Emacs capabilities on “real” projects is easier to achieve.
I did not play a lot with this setup now (mostly because navigating RSeven
scheme slots is really not ergonomic with all the nested Rc::RefCell
), so
there might be other quirks to deal with before the setup is really good; but I
want this post to be a good starting point.
Have fun,
Gerry
Acknowledgements
It took me a while to figure things out, and it wouldn’t be possible without extra resources:
- Robert Krahn for all the pointers it gave to kickstart my configuration.
- Ivan Yonchovski for all his help whenever I had questions on Gitter, and allowing me to contribute a little to lsp/dap to fix a few pain points.
- https://code.visualstudio.com/docs/cpp/launch-json-reference as a reference for the keys I could set up in the template
Doom Emacs
If the snippets in the article don’t work as is, it’s probably because I adapted them from my Doom Emacs configuration. The good news is that there shouldn’t a lot of Doom specific magic in the snippets, so hopefully the errors are easy to fix.
If you are using Doom and want to see what it looks like, you’re free to search through my configuration on SourceHut