Repository navigation
Open
Labels
A-debuginfoArea: Debugging information in compiled programs (DWARF, PDB, etc.)Area: Debugging information in compiled programs (DWARF, PDB, etc.)C-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.E-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.Call for participation: Hard difficulty. Experience needed to fix: A lot.E-needs-designThis issue needs exploration and design to see how and if we can fix/implement itThis issue needs exploration and design to see how and if we can fix/implement itE-needs-investigationCall for participation: This issues needs some investigation to determine current statusCall for participation: This issues needs some investigation to determine current statusP-mediumMedium priorityMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.WG-debuggingWorking group: Bad Rust debugging experiencesWorking group: Bad Rust debugging experiences
Description
In the case of Vec<T> and String, it would be possible to have e.g.:
impl<'a, T> DebuggerView<'a> for Vec<T> {
type View = &'a [T];
fn debugger_view(&'a self) -> Self::View { &self[..] }
}impl<'a> DebuggerView<'a> for String {
type View = &'a str;
fn debugger_view(&'a self) -> Self::View { &self[..] }
}We would then need:
- support for pretty-printing
&[T]and&str- already exists in our custom pretty-printing scripts
- long-term DWARF should allow us to express these directly (see debuginfo: How to (ideally) represent reference and pointer types in DWARF #37504)
- a way to encode
<X as DebuggerView>::debugger_viewinX's debuginfo- i.e. a symbol name, or maybe we can make symbol names based on DWARF type IDs?
- support for going through
<X as DebuggerView>::debugger_viewfor pretty-printing- the method signature above should be relatively simple to call
I suspect there are better APIs that could accommodate more complex data structures, but I'm not sure what the limitation of the pretty-printer scripts are, around being able to call arbitrary functions.
There's also the possibility that we could encode this sort of information as data, instead of compiling it into functions, but that's probably a lot more work.
Reacted by Mateusz Mikuła, Diana, Artem Mukhin, Aaron Hill, Vitaly Bogdanov and Jakub Beránek
Metadata
Metadata
Assignees
Labels
A-debuginfoArea: Debugging information in compiled programs (DWARF, PDB, etc.)Area: Debugging information in compiled programs (DWARF, PDB, etc.)C-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.E-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.Call for participation: Hard difficulty. Experience needed to fix: A lot.E-needs-designThis issue needs exploration and design to see how and if we can fix/implement itThis issue needs exploration and design to see how and if we can fix/implement itE-needs-investigationCall for participation: This issues needs some investigation to determine current statusCall for participation: This issues needs some investigation to determine current statusP-mediumMedium priorityMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.WG-debuggingWorking group: Bad Rust debugging experiencesWorking group: Bad Rust debugging experiences
Activity
jonas-schievink commentedon Oct 18, 2019
How does this work when cross-compiling? Is it possible to easily strip this code from the executable to reduce its size while still being able to debug the binary (important for embedded)?
eddyb commentedon Oct 18, 2019
@jonas-schievink You mean with remote debugging? As long as the
gdbserver (or however you're controlling the target) lets you call functions, this should work.Also, for embedded, would you have dynamic allocation? Pretty printers are mostly needed in that case. And even if you do, each of these impls would be a couple instructions (at least the above ones).
jonas-schievink commentedon Oct 18, 2019
Ah, I see. Though this sounds like it would disrupt the target state more than the current solution. Is anything similar to this implemented by other languages?
You can, yes. I suppose in that case (if you can afford a heap) the overhead of a few instructions isn't terrible but it's still a regression nonetheless.
How would this be controlled? With
-Cdebuginfo? Does that currently have any impact on generated code or is everything it adds removable withstrip?matklad commentedon Oct 23, 2019
👍 on the approach, this is essentially what KotlinNative is doing: calling into runtime to "reflect" values
https://github.com/JetBrains/kotlin-native/blob/838cccf275b121878003d9c8c2f7600d78b10989/llvmDebugInfoC/src/scripts/konan_lldb.py#L178
https://github.com/JetBrains/kotlin-native/blob/838cccf275b121878003d9c8c2f7600d78b10989/llvmDebugInfoC/src/scripts/konan_lldb.py#L249
Production-grade
DebugerViewshould be richer and express children.The MVP here can be much simpler though:
fn debug<T: std::fmt::Debug>(value: &T, buf: *const u8, buf_len: usize)debugfor all T'sdebug::<T>matklad commentedon Oct 23, 2019
Hm, maybe even crazier:
fn debug(value: *const T, buf: *const u8, buf_len: usize)value(this might need some additional info like a frame pointer passed it)value,debugcalls intofmt::Debugusing the appropriate mangled nameThat way, debugger can blindly call
debugon any pointer, without containing logic for figuring out symbol name from a type.Note that this actually can be prototyped as a crate! The only thing that the crate can't do is forcing eager instantiation of
fmt::Debug, but, for a POC, it would be good enough to per-instantiate some stdlib types and providegenerate_debug_printers!(HashMap<String, MyType>)macro.eddyb commentedon Oct 23, 2019
The problem with using
fmt::Debugis that I don't know the extent of the power of Python scripts.That seems... extremely complicated, compared to the debugger which already has it.
Also, if DWARF type IDs are exposed in the Python scripts, then it should be trivial to get a per-type symbol name that way.
bjorn3 commentedon Oct 23, 2019
While I like the idea, I see two problems:
Debuginstances hide sensitive information (eg encryption keys) to prevent accidentially leaking it. However while debugging you may want to be able to inspect it though.eddyb commentedon Oct 23, 2019
Oh, I would prefer if this wasn't based on
fmt::Debugbut rather provided access, through collections, to elements, for debug purposes. So more like&_: IntoIteratorfor debugging.That is, I want the debugger to do inside, what it was doing outside, e.g.
Vec.bjorn3 commentedon Oct 23, 2019
Just a random idea: Is is possible to store
&mut [MaybeUninit<T>]insideVec<T>instead of ptr + cap or&mut [T]instead of ptr + len? That would make special casingVec<T>inside the debugger pretty printer unnecessary.eddyb commentedon Oct 23, 2019
That wouldn't work for, say,
HashMap. And there's no nice way to fit both capacity and length into one slice.michaelwoerister commentedon Oct 25, 2019
I'm not sure if there is a reliable way for calling trait methods from inside debuggers yet.
eddyb commentedon Oct 25, 2019
@michaelwoerister We would be exposing it as a regular function with a predictable symbol name, so that we can find it from the DWARF type information (via type ID perhaps?).
bjorn3 commentedon Oct 25, 2019
I think we need to add the type ID as custom DWARF attribute for a type (maybe call it DW_AT_rust_type_id) to be able to access the type id from inside a debugger.
6 remaining items
made gdb pretty-printing script more robust when printing uninitializ…
Rollup merge of rust-lang#66576 - pnkfelix:more-robust-gdb-vec-printe…
Rollup merge of rust-lang#66576 - pnkfelix:more-robust-gdb-vec-printe…
eddyb commentedon Nov 27, 2019
Something I wasn't aware of, that looks very useful: Windows
natvis!Spotted new
natvisdefinitions being added in #66597 (cc @MaulingMonkey)This is, for example,
Vec'snatvisdefinition:rust/src/etc/natvis/liballoc.natvis
Lines 3 to 13 in b5f265e
This looks decently declarative to me, and the more advanced features are pretty interesting. I definitely prefer it over Python code, even if it's wrapped in XML.
Here's a more advanced example (
HashMapfrom #66597):rust/src/etc/natvis/libstd.natvis
Lines 28 to 49 in b5f265e
I could see this being integrated as either an unstable attribute or an associated
const- but we could go further and generate the C expressions necessary (and/or some sort of DWARF ops) from a subset of MIR, for example, so that it has to fully go through type-checking first.In terms of support, VS Code even supports (a subset of)
natvisforgdb/lldb, but there are also other projects, likegdb-natvisandnatvis4gdb.And if we come up with our own solution that can be lowered to
natvis, then we can have our own pretty-printers and also interface with all of the above as well.MaulingMonkey commentedon Nov 27, 2019
natvis is indeed pretty useful! They're used by VS/VCS/WinDbg/CDB as the main means of visualizing the C++ stdlib containers, smart pointers, etc. - and they can be embedded into pdbs (as rustc does via undocumented link.exe flags) where they're automatically picked up by debuggers. They work fine on minidumps, VS can hot-reload .natvis files if they're part of your project, you can use them on third party types you don't even have source code for... there's even a non-Microsoft game console out there with natvis support in their debug engine.
Cons: Using XML for a programming language is admittedly kinda horrific, and CustomListItems amounts to that. Fortunately it's typically only needed for hash/tree containers. It's also very C++ oriented - my initial PR did a bit of mangling to make Rust debug info look C++y enough for natvis to work with slices and strs (although not reliably... something to do with fat pointer types perhaps?)
My own wild fantasies include auto-generating natvis files for rust enums, and teaching rust to gather and embed natvis files found in dependency crates (or maybe using build scripts to emit link args from said crates would be sufficient? I should experiment with that...)
eddyb commentedon Jan 24, 2020
Interestingly, DWARF is more flexible than I thought, and it looks like at the very least
Vec<T>andStringcould be implemented natively in it - see #37504 (comment).I could maybe see this scaling to
HashMap(to get something akin to[(K, V)]being shown in the debugger), but forLinkedListandBTreeMapyou would have some nesting wherever there's recursion through indirection.tromey commentedon Feb 26, 2020
In addition to this, in gdb we normally recommend that pretty-printers not make inferior calls because it involves letting the inferior run; this can be very confusing when other threads run when you are trying to print something. Also, these calls involve stack manipulations, which is unpleasant when also trying to unwind -- gdb calls into these things when printing the stack trace.
eddyb commentedon Feb 26, 2020
Overall that makes me want to go for something more like "
Vec<T>is a dynamic array where ptr is in this field and the length is in this other field".Maybe offsets instead if field indices (using
offset_of!?)We can do this with a
traitand an associatedconst, and generate DWARF from it, maybe even NatVis (if we can rely on embedding it intopdbs).wesleywiser commentedon May 15, 2023
Visited during wg-debugging triage. We think building debugger visualizations in "regular" Rust code is definitely a good idea but we agree with many of the points raised about the problems of the debugger calling into the target to render values. Additionally, users compiling with debuginfo enabled in release mode for production workloads would probably not accept needing all of this extra code in the binary which is only used at debug time.
There have been various discussions about embedding (S)MIR in debuginfo which can be split from the binary and building debugger plugins that can host a proper evaluation environment. We think that is a better but more difficult route which we would like to see more fully explored.