Change:
Some idea as
Here's the broken object file.
is this something that's easy for ddbug to check for and give more information about?
Maybe, but I don't understand what the problem is and what needs to be checked. The type of 0x001b2d2f is 0x001b2d3d, and I don't know why it claims 0x001b2d3d is a forward declaration since its members are defined too.
So the crash doesn't happen with rust-lldb, so this probably pretty low to fix. Otherwise it seems like the next steps would be to figure out what's going on in lldb's DWARFFastParserClang and why it's sad about our info.
I have this occurring with rust-lldb. Sample code: And I'm stepping into the call. I have rexpect installed locally via: and am running: I'm on macOS 10.14.2 with just installed cargo and rust ().
It seems like has resurfaced. From what I understand this is causing some issues for WebRender devs.
I'm going to look into this asap. Thanks for the test case, that should be very helpful!
I can reproduce this on macOS and Linux. On Linux I can reproduce with a recent debug build of LLDB.
maybe we should make this . At least it's what I'll be working on until it's resolved.
I've been able to make some progress here. I was not able to pinpoint why exactly LLDB fails to load the type info for fields in question but for the test cases I had, the bug occurred only when LLDB tries to get the type information of fields in a closure environment. I tried debugging further but with my limited understanding of LLDB's codebase makes this very time-consuming. So, for the time being I propose to implement a workaround: If we make mark closure environment structs and their fields with then LLDB does not hit the problematic code paths. Marking those fields as artificial is the right thing to do here anyway (we already do it for things like fields of slices). It's unfortunate that we don't know the exact problem in LLDB but the workaround would not be a hack. In my local testing this did not break any debuginfo tests for either LLDB or GDB.
I may be able to look into the LLDB side further. Do you have a simple testcase I can use? I wasn't able to reproduce with the testcase given earlier. At a guess, the problem may be because we use the same name () for multiple types.
That would be awesome, Are you familiar with compiling Firefox? Then getting a test case is straightforward. It's also somewhat lengthy initially so bear with me Get a recent clone of Firefox, e.g. from Make sure you've get an up-to-date build environment by running in the root dir of the FF checkout. Place the following file in the root dir of the checkout: Run in the root directory of your FF checkout. Depending your machine this will take between 5 and 50 minutes Run in the root directory of your FF checkout. This starts a debug instance of the browser. Type into the address bar, look for the option and make sure it is set to . If you needed to change it, close the browser and run again. The test browser should have remembered the setting. If you are on Linux, find out the process ID of the process named , if you are on macOS, find out the PID of the main FF process. Start your version of LLDB and run Once LLDB has successfully stopped FF, set a breakpoint via Run in LLDB and focus the FF window. The breakpoint should be hit immediately. Now type into LLDB. This should trigger the problematic code path and make LLDB crash. For me, the above repro worked on Fedora 30 as well as an up-to-date macOS. It also worked with the stock macOS LLDB version, LLDB 8 on Fedora and a self-compiled LLDB from a recent LLVM master. Maybe yeah. Unfortunately, LLDB crashed on me while debugging itself and seemed to get stuck indefinitely after executing in LLDB.
Thanks for the instructions, Here's a reduced testcase: (in lldb: , , ).
Hex editing the DWARF to change the names of the closure types stops the error from happening.
Very interesting! Generating some kind of unique name for the closure types shouldn't be too hard.
Alright, so I would suggest fixing this issue by doing both: closure environment structs and their fields with , and a unique type name for closure environment structs. The same should probably done for generators (I have not really looked at those yet). Implementing item (1) should be straightforward: Go into and make closure types and their fields get applied. The debuginfo for closure types is created here: ... and implementing this will mean that needs to grow a parameter. The rest is just threading the right flags value through the various function calls. Closure fields are created here (because the compiler treats them as tuple fields): Similar to the case above, should probably be parameterized with a field which can then be used to make closure fields . For examples, one can also look for other occurrences of in the same file. Implementing item (2), i.e. generating a unique type name for closures, is even simpler: Debuginfo type names are generated in , and for closures specifically the name is generated here: The first field of the we have available here is the of the closure, which gives us access to pretty much anything we can know about it. A very simple way of making the type name unique would be to append the of the closure to it, e.g. . The for a given can very cheaply be obtained via . One could also find a more human-readable unique name for closure types (e.g. by using the instead of the ) but that would be more costly and take up more space, so I think for now the is a good option. I'm gonna tag this issue as and in case somebody wants to take a stab at implementing the suggested fix. I'm happy to help out with any questions.
Further reduced: In lldb: , , . The requirements are to have the struct for one closure included as a member of the struct for another closure, and then get lldb to print that struct. I'll take a look at implementing the fix.
Thanks, This should also make for a good regression test.
Should I be using for the names? That's 32 bytes, which seems a bit larger than needed. Probably not something to do right now, but maybe we should move these types inside the functions where they are defined, and then use a simple disambiguation index (are we doing something like this for mangling closure symbols?). This would be similar to using , except that we don't need to repeat the path because the location of the type means it simply inherits the path of the function.
Yes, I also think that we don't need that many characters for disambiguation. I'd probably do something like: This should result in ~11 disambiguation chars which seems fine. Do DWARF & LLVM allow for type DIEs inside of function DIEs? What would be the implications? I can't remember if we already do something like this (we definitely use namespaces though).
I haven't checked clang, but here's some output from gcc which does that:
This whole thread had me confused because I just assumed we were using def-paths (like symbol names do, and we know those are unambiguous), but I thought maybe it was some subtlety everyone else was aware of. Thanks for asking this "out loud", everything's clear now! I wonder if there are other bugs/complications arising from debuginfo not (fully) relying on the disambiguating information that everything else is based on.
You should be able to create similar situations to closures via this: This should create two structs with the same def-path except for the disambiguator on the last component (since blocks don't introduce their own def-path components).
Well, I don't think DWARF actually demands that types have different names. Each DIE has some kind of ID and the attribute is actually optional.
I remember now: We are building a parallel tree of s and put the types into those, because at the time (5 years ago?) GDB wasn't able to handle types inside of functions. Here's what this looks like: DWARF It would be great to get rid of that hack
Ok, but the closure types are all being put under the . So we should at least move them into that namespace with the rest of the types, but yes getting rid of the parallel tree would be good too. Not sure if this will cause problems, but one possible difference from other languages is that these types are visible outside the function by returning .
Pretty sure you can get the same in C++ with .
I'm pretty sure would give you the expected namespace. You can find many examples of how this is used in . Right now we seem to re-use the tuple metadata infrastructure for closures, which is why they all end up in the top-level scope.

Discussion:
} ty::Closure(def_id, substs) => { let upvar_tys : Vec<_> = substs.upvar_tys(def_id, cx.tcx).collect(); <ins> let containing_scope = get_namespace_for_item(cx, def_id); </ins> prepare_tuple_metadata(cx, t, &upvar_tys, unique_type_id, <del> usage_site_span).finalize(cx) </del> <ins> usage_site_span, Some(containing_scope)).finalize(cx) </ins> } ty::Generator(def_id, substs,  _) => { let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| {

t, &tys, unique_type_id, <del> usage_site_span).finalize(cx) </del> <ins> usage_site_span, NO_SCOPE_METADATA).finalize(cx) </ins> } _ => { bug!("debuginfo: unexpected type in type_metadata: {:?}", t)

component_types: &[Ty<'tcx>], unique_type_id: UniqueTypeId, span: Span, <ins> containing_scope: Option<&'ll DIScope>, </ins> ) -> RecursiveTypeDescription<'ll, 'tcx> { let tuple_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false);

tuple_type, &tuple_name[..], unique_type_id, <del> NO_SCOPE_METADATA); </del> <ins> containing_scope); </ins> create_and_register_recursive_type_forward_declaration( cx,

// processing visited.remove(t); }, <del> ty::Closure(..) => { output.push_str("closure"); </del> <ins> ty::Closure(def_id, ..) => { output.push_str(&format!( "closure-{}", tcx.def_key(def_id).disambiguated_data.disambiguator )); </ins> } <del> ty::Generator(..) => { output.push_str("generator"); </del> <ins> ty::Generator(def_id, ..) => { output.push_str(&format!( "generator-{}", tcx.def_key(def_id).disambiguated_data.disambiguator )); </ins> } ty::Error | ty::Infer(_) |

// gdb-command:run // gdb-command:print b <del> // gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 0, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {[...]}}} </del> <ins> // gdb-check:$1 = generator_objects::main::generator-0 {__0: 0x[...], <<variant>>: {__state: 0, 0: generator_objects::main::generator-0::Unresumed, 1: generator_objects::main::generator-0::Returned, 2: generator_objects::main::generator-0::Panicked, 3: generator_objects::main::generator-0::Suspend0 {[...]}, 4: generator_objects::main::generator-0::Suspend1 {[...]}}} </ins> // gdb-command:continue // gdb-command:print b <del> // gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 3, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {c: 6, d: 7}, 4: generator_objects::main::generator::Suspend1 {[...]}}} </del> <ins> // gdb-check:$2 = generator_objects::main::generator-0 {__0: 0x[...], <<variant>>: {__state: 3, 0: generator_objects::main::generator-0::Unresumed, 1: generator_objects::main::generator-0::Returned, 2: generator_objects::main::generator-0::Panicked, 3: generator_objects::main::generator-0::Suspend0 {c: 6, d: 7}, 4: generator_objects::main::generator-0::Suspend1 {[...]}}} </ins> // gdb-command:continue // gdb-command:print b <del> // gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 4, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {c: 7, d: 8}}} </del> <ins> // gdb-check:$3 = generator_objects::main::generator-0 {__0: 0x[...], <<variant>>: {__state: 4, 0: generator_objects::main::generator-0::Unresumed, 1: generator_objects::main::generator-0::Returned, 2: generator_objects::main::generator-0::Panicked, 3: generator_objects::main::generator-0::Suspend0 {[...]}, 4: generator_objects::main::generator-0::Suspend1 {c: 7, d: 8}}} </ins> // gdb-command:continue // gdb-command:print b <del> // gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 1, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {[...]}}} </del> <ins> // gdb-check:$4 = generator_objects::main::generator-0 {__0: 0x[...], <<variant>>: {__state: 1, 0: generator_objects::main::generator-0::Unresumed, 1: generator_objects::main::generator-0::Returned, 2: generator_objects::main::generator-0::Panicked, 3: generator_objects::main::generator-0::Suspend0 {[...]}, 4: generator_objects::main::generator-0::Suspend1 {[...]}}} </ins> // === LLDB TESTS ================================================================================== // lldb-command:run // lldb-command:print b <del> // lldbg-check:(generator_objects::main::generator) $0 = generator(&0x[...]) </del> <ins> // lldbg-check:(generator_objects::main::generator-0) $0 = generator-0(&0x[...]) </ins> // lldb-command:continue // lldb-command:print b <del> // lldbg-check:(generator_objects::main::generator) $1 = generator(&0x[...]) </del> <ins> // lldbg-check:(generator_objects::main::generator-0) $1 = generator-0(&0x[...]) </ins> // lldb-command:continue // lldb-command:print b <del> // lldbg-check:(generator_objects::main::generator) $2 = generator(&0x[...]) </del> <ins> // lldbg-check:(generator_objects::main::generator-0) $2 = generator-0(&0x[...]) </ins> // lldb-command:continue // lldb-command:print b <del> // lldbg-check:(generator_objects::main::generator) $3 = generator(&0x[...]) </del> <ins> // lldbg-check:(generator_objects::main::generator-0) $3 = generator-0(&0x[...]) </ins> #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section]

<ins> // This test makes sure that the LLDB pretty printer does not throw an exception // for nested closures and generators. // Require LLVM with DW_TAG_variant_part and a gdb that can read it. // min-system-llvm-version: 8.0 // min-gdb-version: 8.2 // ignore-tidy-linelength // compile-flags:-g // === GDB TESTS =================================================================================== // gdb-command:run // gdb-command:print g // gdb-check:$1 = issue_57822::main::closure-1 (issue_57822::main::closure-0 (1)) // gdb-command:print b // gdb-check:$2 = issue_57822::main::generator-3 {__0: issue_57822::main::generator-2 {__0: 2, <<variant>>: {[...]}}, <<variant>>: {[...]}} // === LLDB TESTS ================================================================================== // lldb-command:run // lldb-command:print g // lldbg-check:(issue_57822::main::closure-1) $0 = closure-1(closure-0(1)) // lldb-command:print b // lldbg-check:(issue_57822::main::generator-3) $1 = generator-3(generator-2(2)) #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section] use std::ops::Generator; use std::pin::Pin; fn main() { let mut x = 1; let f = move || x; let g = move || f(); let mut y = 2; let mut a = move || { y += 1; yield; }; let mut b = move || { Pin::new(&mut a).resume(); yield; }; zzz(); // #break } fn zzz() { () } </ins>