> On 10 Nov 2025, at 12:34, Adrian Vogelsgesang <[email protected]> wrote:
>
> Thanks for looking into this!
>
> To test this locally, is there a pre-built version for Ubuntu 2024.4
> available anywhere?
not likely since it’s a development branch..
.. but the branch is on Compiler Explorer and it is up to date with that
temporary fix.
https://godbolt.org/z/8v3bMdKEa
> Or is the easiest way to build g++ from source?
… could be if you want to do more complex experimentation.
Iain
>
> 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
> >>>>
> >>>
> >>>
> >
>