> 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.
> 
> ~~~~~~~~~
> 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

Reply via email to