Thanks for looking into this! To test this locally, is there a pre-built version for Ubuntu 2024.4 available anywhere? Or is the easiest way to build g++ from source?
On Mon, Nov 3, 2025 at 10:48 PM Iain Sandoe <[email protected]> wrote: > > > > On 3 Nov 2025, at 05:53, Iain Sandoe <[email protected]> wrote: > >> On 3 Nov 2025, at 04:18, Jason Merrill via Gcc <[email protected]> wrote: > >> On Mon, Nov 3, 2025 at 2:26 PM Adrian Vogelsgesang via Gcc < > [email protected]> > >> wrote: > >> > >>> And I just realized that I inadvertently dropped the ball by replying > >>> only to Sandoe, and forgot to keep the list in CC. > >>> > >>> Thanks for the quick response, Iain! > >>> Inline below my (original, by now pretty late) replies > >>> > >>>>> There are two reasons the script doesn't work for g++: > >>>>> 1. g++ does not emit a `__promise` variable inside the destroy > >>>>> function - that can be worked around by changing the script, though > >>>> > >>>> g++ accesses via the frame pointer - the following entries should be > >>>> available to the debugger. > >>>> > >>>> frame_pointer->_Coro_promise > >>>> frame_pointer->_Coro_resume_index > >>> > >>> Yes, that can be easily fixed in the script - I just didn't implement > it, > >>> yet. > >>> > >>>>> 2. g++ provides no way to map the compiler-assigned suspension point > >>>>> IDs back to line numbers - i.e., the topic of this email > >>>> > >>>> g++ retains the location information for user-authored code (so that > >>> setting > >>>> breakpoints on line nunber etc. should work.) > >>> > >>> yes, and that works great! However, that is besides my point / solving > a > >>> different problem. > >>> > >>> In my case, I have a suspended `std::coroutine_handle` which I want to > >>> pretty-print. From that coroutine_handle, I can get > >>> 1. a pointer to the destroy function's entry point (e.g., 0x7f1345) > >>> 2. the resume index (e.g., 15) > >>> > >>> I now need a way to map `0x7f1345` and `idx 15` back to its location. > >>> I.e., I need to answer "the suspension point with id 15 within the > >>> coroutine whose destroy function starts at address 0x7f1345 is located > >>> in foobar.cpp at line 65". > >>> > >>> Note that I cannot simply lookup 0x7f1345 in the line table, since that > >>> would give me the start of the coroutine function, not the position of > >>> suspension point 15 inside the function's body. > >>> > >>> In clang, I solved this by: > >>> 1. lookup the scope of the destroy function: > >>> destroy_func = gdb.block_for_pc(int(self.destroy_ptr)) > >>> 2. lookup the label for suspension point 15 within that function > >>> label_name = f"__coro_resume_{suspension_point_index}" > >>> resume_label = gdb.lookup_symbol(label_name, self.resume_func, > >>> gdb.SYMBOL_LABEL_DOMAIN)[0] > >>> 3. look at that label's line/column > >>> print(f"suspended at line {resume_label.line}") > >>> > >>> For gcc-compiled code > >>> 1. step 1 also works > >>> 2. step 2 works with a small adjustment to the label name > >>> 3. step 3 does not work, since the labels produced by gcc have > >>> neither location information nor a DW_AT_low_pc which I could look > >>> up in the line table. > >>> > >>>> However, for synthetic code (e.g. the ramp and the expansion of the > >>>> co_await expressions) so far, we have intentionally generated the code > >>>> with “unknown” locations. This (absent the kind of process you are > >>>> mentioning) tends to impove the debug experience - because it avoids > >>>> the apparent location jumping around. > >>> > >>> That's great! clang unfortunately does emit debug info for this > syntetic > >>> code, and hence single stepping into / out of a clang-compiled > coroutine > >>> is a bit clunky > >>> On Fri, Oct 10, 2025 at 8:19 AM Iain Sandoe <[email protected]> > >>> wrote: > >>>> > >>>> > >>>> > >>>>> On 10 Oct 2025, at 03:16, Adrian Vogelsgesang via Gcc < > [email protected]> > >>> wrote: > >>>>> > >>>>> Hi gcc-devs! > >>>>> > >>>>> TLDR: For debugging C++ coroutines, I patched clang to emit > artificial > >>>>> DW_TAG_labels, mapping suspension point ids to the corresponding > >>>>> source location. Looking for alignment re debugging info between > clang > >>>>> and gcc. > >>>>> > >>>>> (Finally following up on Iain Sandoe's request to send this email) > >>>> > >>>> Thanks, there are others here much better able to comment on debug > info > >>>> than I am. I’ve added a couple of notes below but hope that others > will > >>>> chime in with opinions on how to proceed. > >>>> > >>>>> ~~~~~~~~~ > >>>>> Background > >>>>> ~~~~~~~~~ > >>>>> > >>>>> When using coroutines for asynchronous programming, the physical > stack > >>>>> only tells part of the truth. One also wants to see the chain of > >>>>> "awaiting" coroutines, i.e. the coroutines which initiated the > current > >>>>> coroutine frame and are waiting for its completion. > >>>>> > >>>>> I published a gdb debugger script which provides a FrameFilter to > >>>>> inject those async stack frames. With this script, the `bt` command > >>>>> now returns > >>>>> > >>>>>> #0 write_output(...) at ... > >>>>>> [async] greet() at ... > >>>>>> [async] [noop_coroutine] at ... > >>>>>> #1 coroutine_handle<task::promise_type>::resume() const at ... > >>>>>> #2 task::syncStart() at ... > >>>>> > >>>>> However, this script currently doesn't work for gcc-compiled > binaries, > >>>>> yet, due to missing debug information. > >>>>> > >>>>> ~~~~~~~~~ > >>>>> Current state of gcc-generated debug info > >>>>> ~~~~~~~~~ > >>>>> > >>>>> There are two reasons the script doesn't work for g++: > >>>>> 1. g++ does not emit a `__promise` variable inside the destroy > >>>>> function - that can be worked around by changing the script, though > >>>> > >>>> g++ accesses via the frame pointer - the following entries should be > >>>> available to the debugger. > >>>> > >>>> frame_pointer->_Coro_promise > >>>> frame_pointer->_Coro_resume_index > >>>> > >>>> The resume index is updated as we pass the test for the awaiter being > >>>> ready - so that it should be correct whether the coroutine suspends or > >>>> continues. > >>>> > >>>>> 2. g++ provides no way to map the compiler-assigned suspension point > >>>>> IDs back to line numbers - i.e., the topic of this email > >>>> > >>>> g++ retains the location information for user-authored code (so that > >>> setting > >>>> breakpoints on line nunber etc. should work.) > >>>> > >>>> However, for synthetic code (e.g. the ramp and the expansion of the > >>>> co_await expressions) so far, we have intentionally generated the code > >>>> with “unknown” locations. This (absent the kind of process you are > >>>> mentioning) tends to impove the debug experience - because it avoids > >>>> the apparent location jumping around. > >>>> > >>>>> In clang, I solved this issue by emitting DW_TAG labels like > >>>>> > >>>>>> 0x00000f71: DW_TAG_label > >>>>>> DW_AT_name ("__coro_resume_17") > >>>>>> DW_AT_decl_file ("generator-example.cpp") > >>>>>> DW_AT_decl_line (5) > >>>>>> DW_AT_decl_column (3) > >>>>>> DW_AT_artificial (true) > >>>>>> DW_AT_LLVM_coro_suspend_idx (0x11) > >>>>>> DW_AT_low_pc (0x00000000000019be) > >>>>> > >>>>> The debugging script can lookup the DW_TAG_label for a given > >>>>> suspension point either by name or via DW_AT_LLVM_coro_suspend_idx > and > >>>>> retrieve the line, column and address (for setting breakpoints) from > >>>>> that label. > >>>>> > >>>>> gcc emits similar information: > >>>>> > >>>>>> 0x0000297c: DW_TAG_label > >>>>>> DW_AT_name ("resume.17") > >>>>>> > >>>>>> 0x00002981: DW_TAG_label > >>>>>> DW_AT_name ("destroy.17") > >>>>> > >>>>> Unfortunately, this information is not useful because it lacks file, > >>>>> line, column and address information. It would be great if g++ could > >>>>> also emit file, line, column and address information for those > labels. > >>> > >> > >> It looks like those labels internally already have the location > >> information, but the DWARF writer decides not to represent it because > the > >> labels are marked DECL_ARTIFICIAL. We might just remove that flag from > >> create_named_label_with_ctx (even though they are indeed artificial)? > > > > I can certainly experiment with doing that, I’m just not aware it there > are other > > consequences of removing the “artificial” state? > > I’ve made this change (temporarily) here: > > https://github.com/iains/gcc-cxx-coroutines/commits/c%2B%2B-coroutines/ > > This is also the branch that appears as “x86-64 gcc (coroutines)” on > compiler explorer. > (but I guess it will take 24h or so to percolate through) > > Right now, very pressed for time - but hopefully that might help your > exploration? > > Iain > > > > > > Iain > > > >>>>> ~~~~~~~~~ > >>>>> Can gcc also emit useful DW_TAG debug information for coroutines? > >>>>> ~~~~~~~~~ > >>>>> > >>>>> What do you think about the approach of using DW_TAG_label for > >>>>> debugging coroutines? Would you be willing to adopt the same approach > >>>>> also for g++? (I would also be happy to adjust clang, in case we come > >>>>> to a different alignment between both compilers). > >>>>> > >>>>> Adding the file, line, column and address would probably be pretty > >>>>> fundamental for a good debugging experience. Also it would be nice > >>>>> (although completely optional) if we could use the same naming > >>>>> convention (`__coro_resume_x`) and you might want to set the > >>>>> DW_AT_artificial tag. I chose `__coro_resume_x` for clang, because > >>>>> this is a reserved name which is still easily writeable in debugger > >>>>> commands. Using the DW_AT_artificial for those labels also seems to > >>>>> make semantically sense (although it is strictly speaking not blessed > >>>>> by the DWARF standard). > >>>>> > >>>>> ~~~~~~~~~ > >>>>> Further Reading > >>>>> ~~~~~~~~~ > >>>>> > >>>>> RFC for LLVM/clang: > >>>>> > >>> > https://discourse.llvm.org/t/rfc-debug-info-for-coroutine-suspension-locations-take-2/86606 > >>>>> > >>>>> Corresponding clang commit: > >>> https://github.com/llvm/llvm-project/pull/141937 > >>>>> > >>>>> Background on debugging of coroutines, both from the user's point of > >>>>> view and toolchain implementation details, such as the approach for > >>>>> devirtualizing the coroutine frame's state: > >>>>> https://clang.llvm.org/docs/DebuggingCoroutines.html > >>>>> > >>>>> Best, > >>>>> Adrian > >>>> > >>> > >>> > > > >
