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)
~~~~~~~~~
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
2. g++ provides no way to map the compiler-assigned suspension point
IDs back to line numbers - i.e., the topic of this email
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