cannot inspect "if let" variables in gdb - debugging

I'm having trouble inspecting variables set by if-let statements, for instance the following main.rs in a freshly created cargo project named "iflet-rust":
#[derive(Debug)]
struct Stuff;
fn maybe() -> Option<Stuff> {
Some(Stuff {})
}
fn main() {
if let Some(first) = maybe() {
println!("first {:?}", first); // line 8, cannot inspect 'first' here
}
let maybe_stuff = maybe();
if maybe_stuff.is_some() {
let second = maybe_stuff.unwrap();
println!("second {:?}", second); // no problem here
}
}
Then running cargo build and rust-gdb target/debug/iflet-rust
Reading symbols from target/debug/iflet-rust...
(gdb) b main.rs:8
Breakpoint 1 at 0x83f1: file src/main.rs, line 8.
(gdb) b main.rs:14
Breakpoint 2 at 0x848b: file src/main.rs, line 14.
(gdb) r
Starting program: iflet-rust/target/debug/iflet-rust
Breakpoint 1, iflet_rust::main () at src/main.rs:8
8 println!("first {:?}", first); // cannot inspect 'first' here
(gdb) p first
No symbol 'first' in current context
(gdb) info locals
No locals.
(gdb) c
Continuing.
first Stuff
Breakpoint 2, iflet_rust::main () at src/main.rs:14
14 println!("second {:?}", second); // no problem here
(gdb) info locals
second = iflet_rust::Stuff
maybe_stuff = core::option::Option::Some
Is this a know limitation or am I missing something?

It was an issue in the compiler, updating to Rust from 1.60 to 1.63 fixes the problem.
I shouldn't have limited my search to open issues, unfortunate timing.
See https://github.com/rust-lang/rust/issues/97799

Related

Why enum value binding in Rust is so slow?

I am currently learning Rust because I wanted to use it in project that requires a very high performance. I initially fallen in love with enums but then I started to evaluate their performance and I have found something that is really boggling me. Here is an example:
use std::time::{Instant};
pub enum MyEnum<'a> {
V1,
V2(&'a MyEnum<'a>),
V3,
}
impl MyEnum<'_> {
pub fn eval(&self) -> i64 {
match self {
MyEnum::V1 => 1,
MyEnum::V2(_) => 2,
MyEnum::V3 => 3,
}
}
pub fn eval2(&self) -> i64 {
match self {
MyEnum::V1 => 1,
MyEnum::V2(a) => a.eval2(),
MyEnum::V3 => 3,
}
}
}
fn main() {
const EXAMPLES: usize = 10000000;
let en = MyEnum::V1{};
let start = Instant::now();
let mut sum = 0;
for _ in 0..EXAMPLES {
sum += en.eval()
}
println!("enum without fields func call sum: {} at {:?}", sum, start.elapsed());
let start = Instant::now();
let mut sum = 0;
for _ in 0..EXAMPLES {
sum += en.eval2()
}
println!("enum with field func call sum: {} at {:?}", sum, start.elapsed());
}
Results I get:
enum without fields func call sum: 10000000 at 100ns
enum with field func call sum: 10000000 at 6.3425ms
eval function should execute exactly the same instructions as eval2 for V1 enum but it's working about 60x slower. Why is this happening?
Viewing the assembly, your first loop is optimized entirely into a single mov 10000000 instruction (that is, the compiler does something equivalent to sum += EXAMPLES) while the second is not. I do not know why the second loop is not constant-optimized as heavily.
I see no difference in performance, as one would expect.
$ ./test
enum without fields func call sum: 10000000 at 307.543596ms
enum with field func call sum: 10000000 at 312.196195ms
$ rustc --version
rustc 1.43.1 (8d69840ab 2020-05-04)
$ uname -a
Darwin Windhund.local 18.7.0 Darwin Kernel Version 18.7.0: Mon Feb 10 21:08:45 PST 2020; root:xnu-4903.278.28~1/RELEASE_X86_64 x86_64 i386 MacBookPro15,2 Darwin
One problem might be the use of simple "wall clock" time for benchmarking. This simple count of how much time passed is vulnerable to anything else running which might consume resources. Anti-virus, a web browser, any program. Instead, use benchmark tests.

Retrieving a Golang struct from a C void pointer

I have a C struct that looks something like this:
struct room {
void *reset_first;
}
I have a Golang struct that looks something like this:
type reset struct {
command string
}
Thanks to the joys of cgo, I can do the following from Go:
func someStuff(a *C.room, b *reset) {
a.reset_first = unsafe.Pointer(b)
}
and all is well
Then I attempt to retrieve it later:
func otherStuff(a *C.room) {
var r = (*reset)(a.reset_first)
fmt.Println(r.command)
}
and I end up with a segfault:
Thread 12 "test" received signal SIGSEGV, Segmentation fault.
316 fmt.Println(r.command)
but some of the values I get from inspection with gdb surprise me:
(gdb) info args
a = 0x7fffd5bc2d48
(gdb) info locals
r = 0x5
(gdb) print a
$1 = (struct main._Ctype_struct_room *) 0x7fffd5bc2d48
(gdb) print b
$2 = (struct main.reset *) 0x5
(gdb) print *a
3 = {_type = 2, _ = "\000\000\000", reset_first = 0xc422224a80}
How does r end up as 0x5 not 0xc422224a80?
I'm not surprised that trying to dereference 5 results in a segfault, but I'm baffled as to where the 5 came from!

Segmentation fault on macOS when returning from dylib

My Rust program loads Rust written dylibs to improve modularity. It runs smoothly on Linux, but on macOS it gets a segmentation fault when returning for the third time.
Yesterday the problem was way worse that this, on macOS it was going like this at first call:
rustegram(4467,0x7fffad81d340) malloc: *** error for object 0x106817040: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
During debugging, I found that the problem was with using objects as function call parameters. They were all String, so converting them to &str solved the problem.
Now I need to pass complex values like serde_json::value::Value and toml::Value.
pub struct Plugin {
name: String,
config: TomlValue,
plugins: Vec<Arc<Lib>>,
}
impl Plugin {
pub fn run(&self, secret: String, body: JsonValue) -> Result<JsonValue, String> {
if self.plugins.len() > 0 {
// In a real program you want to cache the symbol and not do it every time if your
// application is performance critical
match unsafe { self.plugins[0].lib.get(b"init_bot\0") } {
Ok(temp) => {
let f: Symbol<extern "C" fn(config: &TomlValue, secret: &str, body: &JsonValue) -> Result<JsonValue, String>> = temp;
//on mac it goes "segmentation fault" returning from the third call
println!("DEBUG: before");
let res = f(&self.config.clone(), &secret.clone(), &body.clone());
println!("DEBUG: after");
res
},
Err(e) => Err(format!("Error getting Symbol for {}: {}", self.name, e)),
}
}
else {
Err(format!("Lib {} not loaded", self.name))
}
}
}
Inside the called method, I've put three debugging println! "A" "B" and "C", one before every operation. The output is:
DEBUG: before
A
B
C
DEBUG: after
DEBUG: before
A
B
C
DEBUG: after
DEBUG: before
A
B
C
Segmentation fault: 11
Sometimes, instead of segmentation fault, it gives "Illegal instruction: 4".
For the full code, you can look at my GitHub project.
As pointed out by Matthieu M., I wasn't dereferencing correctly.
On lib side:
pub extern fn init_bot(ptr_config: *const TomlValue, secret: &str, ptr_body: *const JsonValue) -> Result<JsonValue, String>
On application side:
let f: Symbol<extern "C" fn(config: *const TomlValue, secret: &str, body: *const JsonValue) -> Result<JsonValue, String>> = temp;
f(Box::into_raw(Box::new(self.config.clone())), &secret, Box::into_raw(Box::new(body)))

How to get c++ enum value in Xcode?

I would like to know if this is possible to get the value of c++ enum item in Xcode.
In Visual Studio you just have to hover the item and you got a tooltip with its value but it does not do the same in Xcode.
I also tried to print the value in lldb console without success.
For instance with this simple enum:
enum Params{
eP1,
eP2,
eP3,
eP4,
eP5,
};
I tried different ways like p eP1 or p Param::eP1.
I also tried with an enum class with the same result.
At present, you have to use enumName:enumElement, but that is working for me:
> cat foo.cpp
#include <stdio.h>
enum Params
{
eP1,
eP2,
eP3,
eP4
};
int main()
{
enum Params elem = eP1;
printf ("%d\n", elem);
return 0;
}
> lldb a.out
(lldb) target create "a.out"
Current executable set to 'a.out' (x86_64).
(lldb) b s -p printf
Breakpoint 1: where = a.out`main + 29 at foo.cpp:14, address = 0x0000000100000f6d
(lldb) run
Process 26752 launched: '/private/tmp/a.out' (x86_64)
Process 26752 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000f6d a.out`main at foo.cpp:14
11 int main()
12 {
13 enum Params elem = eP1;
-> 14 printf ("%d\n", elem);
^
15 return 0;
16 }
Target 0: (a.out) stopped.
(lldb) expr Params::eP1
(int) $0 = 0
If you still can't get this to work, can you post a more complete example where it fails?
The problem for lldb, BTW, is that the debug information is organized into the full debug information and then a name->info accelerator table. lldb depends on the accelerator tables for lookup (otherwise it would have to go looking through all the debug info which can get pretty slow for big apps). The accelerator tables at present only have the name of the enum, not the element names.

How to use a debugger like GDB or LLDB to debug a crate in Rust?

I am writing a new crate. I wrote some tests for it and run the tests using cargo test. After that, some test_xxx executables are generated in the target folder. I have enabled the debug option in Cargo.toml. By running gdb targets/test_xxx, I can list and debug the code in the test_xxx executable. However, I could not step into the functions in the crate. There is no debug information. How can I build/link the crate to include its debug information?
Your question is a bit fuzzy, so I'll describe what I did:
Create a new crate:
cargo new --lib so
cd so/
Add a small bit of code:
src/lib.rs
fn thing1(a: i32) -> i32 {
a + 2
}
fn thing2(a: i32) -> i32 {
a * a
}
pub fn do_a_thing(a: i32, b: i32) -> i32 {
thing2(b) - thing1(a)
}
Created an external test; one that lives in tests. This matches your comment about test_XXX, as best I can guess:
tests/alpha.rs
#[test]
fn it_works() {
assert_eq!(1, so::do_a_thing(1, 2))
}
Run the tests (output trimmed):
% cargo test
Running target/debug/deps/alpha-b839f50a40d747a9
running 1 test
test it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Open it up in my debugger:
% lldb target/debug/alpha-b839f50a40d747a9
Set a regular expression breakpoint on a method in the crate and run it:
(lldb) br set -r 'do_a_thing'
Breakpoint 1: where = alpha-b839f50a40d747a9`so::do_a_thing::hd55d34fb5a87e372 + 14 at lib.rs:10:11, address = 0x0000000100001f9e
(lldb) r
Process 53895 launched: '/tmp/so/target/debug/alpha-b839f50a40d747a9' (x86_64)
running 1 test
Process 53895 stopped
* thread #2, name = 'it_works', stop reason = breakpoint 1.1
frame #0: 0x0000000100001f9e alpha-b839f50a40d747a9`so::do_a_thing::hd55d34fb5a87e372(a=1, b=2) at lib.rs:10:11
7 }
8
9 pub fn do_a_thing(a: i32, b: i32) -> i32 {
-> 10 thing2(b) - thing1(a)
11 }
Target 0: (alpha-b839f50a40d747a9) stopped.
(lldb) p b
(int) $0 = 2
(lldb) p a
(int) $1 = 1
(lldb) br set -r 'thing2'
Breakpoint 2: where = alpha-b839f50a40d747a9`so::thing2::hf3cb71248518a556 + 11 at lib.rs:6:4, address = 0x0000000100001f5b
(lldb) c
Process 53895 resuming
Process 53895 stopped
* thread #2, name = 'it_works', stop reason = breakpoint 2.1
frame #0: 0x0000000100001f5b alpha-b839f50a40d747a9`so::thing2::hf3cb71248518a556(a=2) at lib.rs:6:4
3 }
4
5 fn thing2(a: i32) -> i32 {
-> 6 a * a
7 }
8
9 pub fn do_a_thing(a: i32, b: i32) -> i32 {
Target 0: (alpha-b839f50a40d747a9) stopped.
(lldb) p a
(int) $2 = 2
This indicates that I was able to set breakpoints and debug in my crate.
For dependent crates
I added this to my Cargo.toml:
[dependencies]
time = "0.1.0"
Along with this code:
src/lib.rs
pub fn do_another_thing() -> bool {
time::precise_time_ns() % 2 == 0
}
And this test (still in the external test file):
tests/alpha.rs
#[test]
fn it_works2() {
assert_eq!(true, so::do_another_thing())
}
Built and ran the tests as before, then opened it in the debugger as before:
(lldb) br set -r 'precise_time'
Breakpoint 1: where = alpha-25aace4e290c57ee`time::precise_time_ns::h21114d10b3e2c8e8 + 8 at lib.rs:161:4, address = 0x0000000100002278
(lldb) r
Process 54043 launched: '/tmp/so/target/debug/alpha-25aace4e290c57ee' (x86_64)
running 2 tests
test it_works ... Process 54043 stopped
* thread #2, name = 'it_works2', stop reason = breakpoint 1.1
frame #0: 0x0000000100002278 alpha-25aace4e290c57ee`time::precise_time_ns::h21114d10b3e2c8e8 at lib.rs:161:4
158 */
159 #[inline]
160 pub fn precise_time_ns() -> u64 {
-> 161 sys::get_precise_ns()
162 }
163
164
Why a regex breakpoint?
Function names are mangled and namespaced. Our functions are actually called so::do_a_thing::hd55d34fb5a87e372 or time::precise_time_ns::h21114d10b3e2c8e8. Using a regex breakpoint selects those easily without thinking about the exact name.
You can also use LLDB's autocompletion
(lldb) b so::<TAB>
Available completions:
so::do_a_thing::hfb57d28ba1650245
so::do_another_thing::hace29914503d7a2f
so::thing1::ha6f7818d54de28d4
so::thing2::h2518577906df58fd
(lldb) b time::<TAB>
Available completions:
time::precise_time_ns::h21114d10b3e2c8e8
time::sys::inner::mac::get_precise_ns::h64c88ed88da4ac18
time::sys::inner::mac::info::_$u7b$$u7b$closure$u7d$$u7d$::ha03be28d018f231b
time::sys::inner::mac::info::h364c1a0ef2ef1f0c
Using GDB instead of LLDB
You can also use GDB, but it looks like it may be a bit harder. After setting a breakpoint in the primary crate's code, I stepped in and saw the function name seem to be the mangled version. You can use that as a breakpoint though.
Note my regex breakpoint doesn't work as intended, and also note the line number of the real breakpoint:
(gdb) rbreak precise_time_ns
u64 _ZN4time15precise_time_nsE()();
static u64 _ZN4time15precise_time_ns18os_precise_time_nsE()();
Breakpoint 1 ('_ZN4time15precise_time_ns18os_precise_time_ns13closure.24322E') pending.
<function, no debug info> time::precise_time_ns::os_precise_time_ns::closure.24322;
(gdb) b _ZN4time15precise_time_nsE
Breakpoint 2 at 0x1000052a0: file lib.rs, line 172.
(gdb) r
Starting program: /private/tmp/so/target/alpha-e0c6f11f426d14d2
running 2 tests
test it_works ... ok
[Switching to process 49131 thread 0xd03]
Breakpoint 1, _ZN4time15precise_time_nsE () at lib.rs:172
172 pub fn precise_time_ns() -> u64 {
(gdb) list
167
168 /**
169 * Returns the current value of a high-resolution performance counter
170 * in nanoseconds since an unspecified epoch.
171 */
172 pub fn precise_time_ns() -> u64 {
173 return os_precise_time_ns();
174
175 #[cfg(windows)]
176 fn os_precise_time_ns() -> u64 {
Debugger wrappers
There are two wrapper programs shipped with Rust: rust-lldb and rust-gdb. These are intended to increase the usefulness of each debugger. It's worth giving them a shot.
After receiving an answer, I found that this doesn't happen to all methods in an external crate, but only generic ones. I filed issue 19228 with the detailed steps.
This is what I am doing to debug the generic methods in the meantime:
Write the tests in the tests folder as usual. Run "cargo test" to check if it is successful.
If it fails, I move the test code into the crate lib.rs file and build a binary using "rustc -g src/lib.rs".
Use gdb to debug lib.

Resources