You know that feeling when you're staring at ptr
, cap
, and len
fields instead of your actual data? That's the exact pain rust-lldb was built to eliminate. Let me tell you about the worst debugging session of my fucking life - one that could've been avoided if I'd just understood why this tool exists.
Picture this: Saturday morning, production service is down, customers are pissed, and I'm staring at a core dump trying to figure out why our Rust service is segfaulting. I fire up plain LLDB because "how hard could it be?"
Big fucking mistake.
The Rust Debugging Nightmare Without Proper Tools
Here's what a simple Option<String>
looks like in plain LLDB:
(lldb) p my_option
(Option<String>) my_option = {
discriminant = 1
value = {
vec = {
buf = {
ptr = 0x7f8b8c000abc
cap = 10
_phantom = {}
}
len = 5
}
}
}
Cool, so my string is... where exactly? Oh right, I need to manually dereference that pointer and count bytes while my production system burns money and my phone keeps buzzing with customer complaints. Took me like 2 hours to figure out it was just "hello" - five fucking characters.
When rust-lldb Actually Saves Your Ass
Same scenario with rust-lldb:
(lldb) p my_option
(Option<String>) my_option = Some(\"hello\")
That's it. That's the difference between debugging and suffering.
Real Talk: When This Tool Actually Matters
The HashMap From Hell: Ever tried debugging a HashMap<String, Vec<Option<Result<User, DatabaseError>>>>
in plain LLDB? It's 47 levels of pointer indirection. Spent forever trying to figure out why user lookups were failing, manually following pointers through memory dumps like some kind of detective. Turns out the HashMap had the right key but the Result
was Err(ConnectionTimeout)
- only found that when I finally got rust-lldb working.
The async/await Disaster: Async state machines are where plain debuggers go to die. Debugging a hanging HTTP service over the weekend - rust-lldb showed me the exact Future stuck waiting on recv()
with a half-parsed JSON payload. Plain LLDB? Showed me a struct called __async_gen_0_state_machine
with field names like __field_37_variant_0_async_suspend
- basically compiler vomit.
Production Core Dumps: Service crashes sometime after midnight, phone starts buzzing. SSH into the server, core dump is sitting there, manager's already asking for an ETA. rust-lldb shows you panic!(\"index out of bounds: len 5, index 7\")
with the actual Vec that caused it. Plain LLDB shows you 0x7fff5fbfe008
and some assembly instructions that might as well be hieroglyphics.
The Platform-Specific Gotchas Nobody Warns You About
macOS Monterey: Broke rust-lldb for months straight. Xcode 13.1 wouldn't load Rust symbols, 13.2 crashed on launch, 13.2.1 finally worked but only if you codesigned your binaries first. Nothing like downgrading your entire development environment while production burns. Check the rust-lang GitHub issues for the current Apple-induced breakage.
Linux DWARF Version Hell: Rust 1.70 changed the DWARF format. If you're using LLDB < 15, rust-lldb just silently fails to load symbols. No error message, no warning, just broken debugging. Learned this one the hard way during a Sunday night incident.
Windows WSL2: rust-lldb through WSL2 fails in creative ways. Debugging works fine for a while, then LLDB just hangs when you inspect a Vec with 100 strings. Restart WSL, works for a bit longer, then crashes with SIGSEGV
in the Python pretty printer. Just use rustup-init.exe
on Windows proper and save yourself the pain.
When To Give Up and Use println! Instead
Look, rust-lldb is great, but sometimes it's not worth the hassle:
- Release builds with full optimization: Everything shows up as "optimized out" anyway
- Embedded targets: Just use RTT and save yourself the pain
- When the pretty printer uses more memory than your actual program: Yes, this happens with large HashMaps
- When you need to debug the debugger: Meta-debugging is where madness lies
rust-lldb ships with every Rust installation via rustup because even the compiler team got tired of debugging by reading raw memory dumps. It's not perfect - the Python pretty-printers crash on proc-macro generated types and take LLDB down with them - but it beats manually calculating pointer offsets when you're already hours deep into a production incident.
The Rust debugging documentation covers the basics, but real-world debugging scenarios require understanding the LLDB API and how Rust's memory layout works under the hood. When debugging async code, you're dealing with generated state machines that LLDB struggles to represent. The Rust async book explains why async debugging is fundamentally difficult.
Platform-specific debugging issues are documented in various GitHub issues, particularly around macOS compatibility and Windows WSL2 problems. The LLVM project maintains LLDB, but Rust-specific enhancements come from the Rust compiler team.