How to debug Rust using GDB in windows?

Hi All,

I see that most of the Rust community prefer GDB for debugging. I was using MSVC for debugging in windows but I felt it was lacking pretty-printer support. So, I thought I will switch to gdb.

Below is the command I use to execute the GDB with pretty printer in windows. I got this based on the rust-gdb.cmd link

gdb  --directory="C:\Users\selva\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\etc" -iex "add-auto-load-safe-path C:\Users\selva\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\etc" .\target\debug\test.exe

below is the test function

fn main() {
    let a = 10;
    let b = &"test".to_string();
    let c = "test";
    let d = "test".to_string();
    let e = Some(10);
    let f = Some("test".to_string());

below is the output
I was expecting String to be displayed properly (similar to &str). But it shows internals. Also, the same behavior is seen in Vec and HashMap. I feel pretty-printer is not triggered properly.

Can somebody help here?

I don't really know anything about it on windows, but assuming that rust-gdb isn't being distributed by rust, you're getting gdb from somewhere, it certainly can't hurt to check that your gdb is python enabled.

with something like gdb -ex 'python print("foo")' -ex 'quit'

Thanks for reply. I am new to GDB.

I executed your command and below is the output,

Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name 'foo' is not defined
Error while executing Python code.

Ahh sorry, I'm assuming that this doesn't do what I'm used to on the windows command line for some reason involving quoting rules.

it looks like python is compiled in, i.e. if it wasn't you would have seen: "Python scripting is not supported in this copy of GDB.", in this case just python print("foo") from the (gdb) prompt is what I should have asked.

I guess the next thing to ask would if auto-loading scripts is actually enabled:

(gdb) show auto-load python-scripts
Auto-loading of Python scripts is on.

It says "Auto-loading of Python scripts is on."

Hmm, I'll have to look and see if there is any debugging we can turn on for autoloading.
Also I realize I didn't confirm that it looks like your scripts are indeed not being autoloaded correctly.

Here is a transcript on another platform...

(gdb) p a
$1 = 10
(gdb) p b
$2 = (*mut alloc::string::String) 0x7fffffffdee8
(gdb) p *b
$3 = "test"
(gdb) p c
$4 = "test"
(gdb) p d
$5 = "test"
(gdb) p e
$6 = core::option::Option<i32>::Some(10)
(gdb) p f
$7 = core::option::Option<alloc::string::String>::Some("test")

Thanks for quick response.

No, mine doesn't look like yours (especially for string) Below is my output,

(gdb) p a
$1 = 10
(gdb) p b
$2 = (*mut alloc::string::String) 0x5a629f71d8
(gdb) p *b
$3 = alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: 0x1c70b94e960, _marker: core::marker::PhantomData<u8>}, cap: 4, alloc: alloc::alloc::Global}, len: 4}}
(gdb) p c
$4 = "test"
(gdb) p d
$5 = alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: 0x1c70b94ea70, _marker: core::marker::PhantomData<u8>}, cap: 4, alloc: alloc::alloc::Global}, len: 4}}
(gdb) p e
$6 = core::option::Option<i32>::Some(10)
(gdb) p f
$7 = core::option::Option<alloc::string::String>::Some(alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: 0x1c70b94eb20, _marker: core::marker::PhantomData<u8>}, cap: 4, alloc: alloc::alloc::Global}, len: 4}})

Well looking at debugging the auto-load process, there are a few things you can look at, I found these via the command (gdb) apropos pretty and (gdb) apropos auto-load, there are a bunch more things in that output to look at but some useful ones are:

(gdb) info auto-load python-scripts
Loaded  Script
(gdb) show print pretty
Pretty formatting of structures is on.
(gdb) set print pretty on

and this one will produce a lot of output, and you'll want to set it on the command line like the gdb -iex "set debug auto-load on"

curiously info pretty-printer doesn't produce anything which I would relate to the rust output, shrug

This was turned off and I set it on. However, still same. I also get below output,

(gdb) source
Traceback (most recent call last):
  File "c:\msys64\mingw64\share\gdb/python\gdb\", line 141, in _execute_file
    with open(filepath, "rb") as file:
FileNotFoundError: [Errno 2] No such file or directory: ''

Looks like GDB is not able to recongnize scripts in C:\Users\selva\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\etc

I have also set PYTHONHOME before executing gdb,

$env:PYTHONPATH = 'C:\Users\selva\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\etc'

Well, that is a bona fide error at least, alas I don't know enough about windows or msys paths to really be of any help in knowing what the right thing to fix it is.

I guess my suggestion would be though to figure out how to open up an MSys shell, try and see if you can find the paths to the files it fails to load, and then feed it paths that look like that?

When I wrote that I was having those issues with the Visual Studio debugger someone from Microsoft replied and said they are always looking for improve the Rust support, and suggested I file issues with them.

While you should obviously find immediate solutions to your needs, I would suggest you also report any specific issues with Microsoft as well. We definitely want the Rust debugging with Visual Studio to improve.

I've had quite a bit of success remote debugging Rust binaries using msvsmon and the Visual Studio debugger. I tried to set up such a thing with lldb, but it was a no-go, but I can't remember the details.

Thanks @blonk. I think Visual studio debugger is working as expected for visualizing variables (although recently I saw a bug in visualizing HashMap and filed an issue in Rust github to correct Natvis). Moreover, one can write a custom Natvis.
However, what I am not able to work correctly is - Conditional breakpoint.
For example consider a struct like below,

struct test {
    my_str: String,
    my_f32: f32,
    my_vec: Vec<String>,

I would like to set conditional breakpoint with to below conditions
my_test.my_str == "Test"
my_test.my_f32 == 3.1
my_test.my_vec[0] == "Test"

Thanks @ratmice. With your help, I was able to understand some more about python scripts.

I was able to modify the gdbinit and I could see it is reflecting in the gdb console. For example, I added print("selva") in gdbinit and gdb printed it.
Then, I also added import gdb_lookup, it worked which clearly shows that gdb is able to find the scripts in the C:\Users\selva\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\etc.

However, when I add gdb_lookup.register_printers(gdb.current_objfile()), it throws an error as below,

Traceback (most recent call last):
  File "<string>", line 8, in <module>
  File "C:\Users\selva\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\etc\", line 12, in register_printers
AttributeError: 'NoneType' object has no attribute 'pretty_printers'

below is my gdbinit

import sys
sys.path.insert(0, sys.path[0] + '/../../gcc-11.2.0/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
# Below are the code I added
import gdb
import gdb_lookup

I think that the problem is that you need to load an object file/executable before it calls register_printers,
I think the following is probably reimplementing something which already does it,
but you could try:

def register_rust_pretty_on_new_objfile(objfile):
                print("new objfile", objfile.new_objfile)

I could clarifify this, gdb loads .gdbinit very early, before executables, so even if you passed an executable on the command line gdb.current_objfile() would still return None.

your script might work if you source .gdbinit after an executable.

Hi @ratmice. It worked. Thanks. Now I made your code into a .py file and calling gdb -iex "source" .\target\debug\test.exe

1 Like

Cool, in theory you should be able to throw the above function in .gdbinit and it should work --
without passing source Because it registers that function to be called when new_objfiles are loaded by gdb.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.