[clang] [docs][coroutines] Update gdb debugger script (PR #162145)
Adrian Vogelsgesang via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 6 13:43:49 PDT 2025
https://github.com/vogelsgesang updated https://github.com/llvm/llvm-project/pull/162145
>From 23d09eb65ae07e995c0d3a95870da0b0f7c2cdf8 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Mon, 6 Oct 2025 18:56:09 +0000
Subject: [PATCH 1/2] [docs][coroutines] Update gdb script
In "Debugging C++ Coroutines", we provide a gdb script to aid with
debugging C++ coroutines in gdb.
This commit contains the following user-facing changes:
* `show-coro-frame` was replaced by a pretty-printer for
`std::coroutine_handle`. This is much easier to use than a custom
command since it works out-of-the-box with `p` and in my IDE's
variable view (tested using VS-Code)
* the new `get_coro_{frame,promise}` functions can be called from
expressions to access nested members.
Example: `p get_coro_promise(fib.coro_hdl)->current_state`
* `async-bt` was replaced by a frame filter. This way, the builtin
`bt` command directly shows all the async coroutine frames.
Under the covers, the script became more robust:
* For devirtualization, we now look up the `__coro_frame` variable
in the resume function instead of relying on the `.coro_frame_ty`
naming convention. Thereby, devirtualization works slightly better
also on gcc-compiled binaries (however, there is still more work to be
done).
* We use the LLVM-generated `__coro_resume_<N>` labels to get the exact
line at which a coroutine was suspended.
* The continuation handle is now looked up by name instead of via
dereferencing a calculated pointer. Thereby, the script should be
simpler to adjust for various coroutine libraries without requiring
pointer arithmetic hacks.
Other sections of the documentation were adjusted accordingly to reflect
the newly added features of the gdb script.
---
clang/docs/DebuggingCoroutines.rst | 432 ++++++++++++++++++-----------
1 file changed, 274 insertions(+), 158 deletions(-)
diff --git a/clang/docs/DebuggingCoroutines.rst b/clang/docs/DebuggingCoroutines.rst
index 9eaf8d4028adf..4304270458d09 100644
--- a/clang/docs/DebuggingCoroutines.rst
+++ b/clang/docs/DebuggingCoroutines.rst
@@ -179,8 +179,8 @@ generator and its internal state.
To do so, we can simply look into the ``gen.hdl`` variable. LLDB comes with a
pretty printer for ``std::coroutine_handle`` which will show us the internal
-state of the coroutine. For GDB, you will have to use the ``show-coro-frame``
-command provided by the :ref:`gdb-script`.
+state of the coroutine. For GDB, the pretty printer is provided by a script,
+see :ref:`gdb-script` for setup instructions.
.. image:: ./coro-generator-suspended.png
@@ -206,23 +206,16 @@ Tracking the exact suspension point
Among the compiler-generated members, the ``__coro_index`` is particularly
important. This member identifies the suspension point at which the coroutine
-is currently suspended.
+is currently suspended. However, it is non-trivial to map this number backto
+a source code location.
-However, it is non-trivial to map this number back to a source code location.
-The compiler emits debug info labels for the suspension points. This allows us
-to map the suspension point index back to a source code location. In gdb, we
-can use the ``info line`` command to get the source code location of the
-suspension point.
-
-::
-
- (gdb) info line -function coro_task -label __coro_resume_2
- Line 45 of "llvm-example.cpp" starts at address 0x1b1b <_ZL9coro_taski.resume+555> and ends at 0x1b46 <_ZL9coro_taski.resume+598>.
- Line 45 of "llvm-example.cpp" starts at address 0x201b <_ZL9coro_taski.destroy+555> and ends at 0x2046 <_ZL9coro_taski.destroy+598>.
- Line 45 of "llvm-example.cpp" starts at address 0x253b <_ZL9coro_taski.cleanup+555> and ends at 0x2566 <_ZL9coro_taski.cleanup+598>.
+For GDB, the provided :ref:`gdb-script` already takes care of this and provides
+the exact line number of the suspension point as part of the couroutine handle's
+summary string. Unfortunately, LLDB's pretty-printer does not support this, yet.
+Furthermore, those labels are only emitted starting with clang 21.0.
-LLDB does not support looking up labels. Furthermore, those labels are only emitted
-starting with clang 21.0.
+When debugging with LLDB or when using older clang versions, we will have to use
+a different approach.
For simple cases, you might still be able to guess the suspension point correctly.
Alternatively, you might also want to modify your coroutine library to store
@@ -655,33 +648,17 @@ There are two possible approaches to do so:
We can lookup their types and thereby get the types of promise
and coroutine frame.
-In gdb, one can use the following approach to devirtualize a coroutine type,
-assuming we have a ``std::coroutine_handle`` is at address 0x418eb0:
-
-::
+In general, the second approach is preferred, as it is more portable.
- (gdb) # Get the address of coroutine frame
- (gdb) print/x *0x418eb0
- $1 = 0x4019e0
- (gdb) # Get the linkage name for the coroutine
- (gdb) x 0x4019e0
- 0x4019e0 <_ZL9coro_taski>: 0xe5894855
- (gdb) # Turn off the demangler temporarily to avoid the debugger misunderstanding the name.
- (gdb) set demangle-style none
- (gdb) # The coroutine frame type is 'linkage_name.coro_frame_ty'
- (gdb) print ('_ZL9coro_taski.coro_frame_ty')*(0x418eb0)
- $2 = {__resume_fn = 0x4019e0 <coro_task(int)>, __destroy_fn = 0x402000 <coro_task(int)>, __promise = {...}, ...}
-
-In practice, one would use the ``show-coro-frame`` command provided by the
-:ref:`gdb-script`.
+To do so, we look up the types in the destroy function and not the resume function
+because the resume function pointer will be set to a ``nullptr`` as soon as a
+coroutine reaches its final suspension point. If we used the resume function,
+devirtualization would hence fail for all coroutines that have reached their final
+suspension point.
LLDB comes with devirtualization support out of the box, as part of the
-pretty-printer for ``std::coroutine_handle``. Internally, this pretty-printer
-uses the second approach. We look up the types in the destroy function and not
-the resume function because the resume function pointer will be set to a
-``nullptr`` as soon as a coroutine reaches its final suspension point. If we used
-the resume function, devirtualization would hence fail for all coroutines that
-have reached their final suspension point.
+pretty-printer for ``std::coroutine_handle``. For GDB, a similar pretty-printer
+is provided by the :ref:`gdb-script`.
Interpreting the coroutine frame in optimized builds
----------------------------------------------------
@@ -756,6 +733,26 @@ should not be thought of as directly representing the variables in the C++
source.
+Mapping suspension point indices to source code locations
+---------------------------------------------------------
+
+To aid in mapping a ``__coro_index`` back to a source code location, clang 21.0
+and newer emit special, compiler-generated labels for the suspension points.
+
+In gdb, we can use the ``info line`` command to get the source code location of
+the suspension point.
+
+::
+
+ (gdb) info line -function coro_task -label __coro_resume_2
+ Line 45 of "llvm-example.cpp" starts at address 0x1b1b <_ZL9coro_taski.resume+555> and ends at 0x1b46 <_ZL9coro_taski.resume+598>.
+ Line 45 of "llvm-example.cpp" starts at address 0x201b <_ZL9coro_taski.destroy+555> and ends at 0x2046 <_ZL9coro_taski.destroy+598>.
+ Line 45 of "llvm-example.cpp" starts at address 0x253b <_ZL9coro_taski.cleanup+555> and ends at 0x2566 <_ZL9coro_taski.cleanup+598>.
+
+LLDB does not support looking up labels, yet. That's also why LLDB's pretty-printer
+does not show the exact line number of the suspension point.
+
+
Resources
=========
@@ -1019,154 +1016,273 @@ GDB Debugger Script
For GDB, the following script provides a couple of useful commands:
-* ``async-bt`` to print the stack trace of a coroutine
-* ``show-coro-frame`` to print the coroutine frame, similar to
- LLDB's builtin pretty-printer for coroutine frames
+The following script provides
+
+* a pretty-printer for coroutine handles
+* a frame filter to add coroutine frames to the built-in ``bt`` command
+* the commands ``get_coro_frame`` and ``get_coro_promise`` to be used in
+ expressions, e.g. ``p get_coro_promise(fib.coro_hdl)->current_state``
+
+It can be loaded into GDB using ``source gdb_coro_debugging.py``.
+To load this by default, add this command to your ``~/.gdbinit`` file.
.. code-block:: python
- # debugging-helper.py
+ # gdb_coro_debugging.py
import gdb
from gdb.FrameDecorator import FrameDecorator
- class SymValueWrapper():
- def __init__(self, symbol, value):
- self.sym = symbol
- self.val = value
+ import typing
+ import re
+
+ def _load_pointer_at(addr: int):
+ return gdb.Value(addr).reinterpret_cast(gdb.lookup_type('void').pointer().pointer()).dereference()
+
+ """
+ Devirtualized coroutine frame.
+
+ Devirtualizes the promise and frame pointer types by inspecting
+ the destroy function.
+
+ Implements `to_string` and `children` to be used by `gdb.printing.PrettyPrinter`.
+ Base class for `CoroutineHandlePrinter`.
+ """
+ class DevirtualizedCoroFrame:
+ def __init__(self, frame_ptr_raw: int, val: gdb.Value | None = None):
+ self.val = val
+ self.frame_ptr_raw = frame_ptr_raw
+
+ # Get the resume and destroy pointers.
+ if frame_ptr_raw == 0:
+ self.resume_ptr = None
+ self.destroy_ptr = None
+ self.promise_ptr = None
+ self.frame_ptr = gdb.Value(frame_ptr_raw).reinterpret_cast(gdb.lookup_type("void").pointer())
+ return
+
+ # Get the resume and destroy pointers.
+ self.resume_ptr = _load_pointer_at(frame_ptr_raw)
+ self.destroy_ptr = _load_pointer_at(frame_ptr_raw + 8)
+
+ # Devirtualize the promise and frame pointer types.
+ frame_type = gdb.lookup_type("void")
+ promise_type = gdb.lookup_type("void")
+ self.destroy_func = gdb.block_for_pc(int(self.destroy_ptr))
+ if self.destroy_func is not None:
+ frame_var = gdb.lookup_symbol("__coro_frame", self.destroy_func, gdb.SYMBOL_VAR_DOMAIN)[0]
+ if frame_var is not None:
+ frame_type = frame_var.type
+ promise_var = gdb.lookup_symbol("__promise", self.destroy_func, gdb.SYMBOL_VAR_DOMAIN)[0]
+ if promise_var is not None:
+ promise_type = promise_var.type.strip_typedefs()
+
+ # If the type has a template argument, prefer it over the devirtualized type.
+ if self.val is not None:
+ promise_type_template_arg = self.val.type.template_argument(0)
+ if promise_type_template_arg is not None and promise_type_template_arg.code != gdb.TYPE_CODE_VOID:
+ promise_type = promise_type_template_arg
+
+ self.promise_ptr = gdb.Value(frame_ptr_raw + 16).reinterpret_cast(promise_type.pointer())
+ self.frame_ptr = gdb.Value(frame_ptr_raw).reinterpret_cast(frame_type.pointer())
+
+ # Try to get the suspension point index and look up the exact line entry.
+ self.suspension_point_index = int(self.frame_ptr.dereference()["__coro_index"]) if frame_type.code == gdb.TYPE_CODE_STRUCT else None
+ self.resume_func = gdb.block_for_pc(int(self.resume_ptr))
+ self.resume_label = None
+ if self.resume_func is not None and self.suspension_point_index is not None:
+ label_name = f"__coro_resume_{self.suspension_point_index}"
+ self.resume_label = gdb.lookup_symbol(label_name, self.resume_func, gdb.SYMBOL_LABEL_DOMAIN)[0]
+
+ def get_function_name(self):
+ if self.destroy_func is None:
+ return None
+ name = self.destroy_func.function.name
+ # Strip the "clone" suffix if it exists.
+ if " [clone " in name:
+ name = name[:name.index(" [clone ")]
+ return name
+
+ def to_string(self):
+ result = "coro(" + str(self.frame_ptr_raw) + ")"
+ if self.destroy_func is not None:
+ result += ": " + self.get_function_name()
+ if self.resume_label is not None:
+ result += ", line " + str(self.resume_label.line)
+ if self.suspension_point_index is not None:
+ result += ", suspension point " + str(self.suspension_point_index)
+ return result
+
+ def children(self):
+ if self.resume_ptr is None:
+ return [
+ ("coro_frame", self.frame_ptr),
+ ]
+ else:
+ return [
+ ("resume", self.resume_ptr),
+ ("destroy", self.destroy_ptr),
+ ("promise", self.promise_ptr),
+ ("coro_frame", self.frame_ptr)
+ ]
+
+
+ # Works for both libc++ and libstdc++.
+ libcxx_corohdl_regex = re.compile('^std::__[A-Za-z0-9]+::coroutine_handle<.+>$|^std::coroutine_handle<.+>(( )?&)?$')
+
+ def _extract_coro_frame_ptr_from_handle(val: gdb.Value):
+ if libcxx_corohdl_regex.match(val.type.strip_typedefs().name) is None:
+ raise ValueError("Expected a std::coroutine_handle, got %s" % val.type.strip_typedefs().name)
+
+ # We expect the coroutine handle to have a single field, which is the frame pointer.
+ # This heuristic works for both libc++ and libstdc++.
+ fields = val.type.fields()
+ if len(fields) != 1:
+ raise ValueError("Expected 1 field, got %d" % len(fields))
+ return int(val[fields[0]])
- def __str__(self):
- return str(self.sym) + " = " + str(self.val)
- def get_long_pointer_size():
- return gdb.lookup_type('long').pointer().sizeof
+ """
+ Pretty printer for `std::coroutine_handle<T>`
- def cast_addr2long_pointer(addr):
- return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
+ Works for both libc++ and libstdc++.
- def dereference(addr):
- return long(cast_addr2long_pointer(addr).dereference())
+ It prints the coroutine handle as a struct with the following fields:
+ - resume: the resume function pointer
+ - destroy: the destroy function pointer
+ - promise: the promise pointer
+ - coro_frame: the coroutine frame pointer
- class CoroutineFrame(object):
- def __init__(self, task_addr):
- self.frame_addr = task_addr
- self.resume_addr = task_addr
- self.destroy_addr = task_addr + get_long_pointer_size()
- self.promise_addr = task_addr + get_long_pointer_size() * 2
- # In the example, the continuation is the first field member of the promise_type.
- # So they have the same addresses.
- # If we want to generalize the scripts to other coroutine types, we need to be sure
- # the continuation field is the first member of promise_type.
- self.continuation_addr = self.promise_addr
+ Most of the functionality is implemented in `DevirtualizedCoroFrame`.
+ """
+ class CoroutineHandlePrinter(DevirtualizedCoroFrame):
+ def __init__(self, val : gdb.Value):
+ frame_ptr_raw = _extract_coro_frame_ptr_from_handle(val)
+ super(CoroutineHandlePrinter, self).__init__(frame_ptr_raw, val)
- def next_task_addr(self):
- return dereference(self.continuation_addr)
+ def build_pretty_printer():
+ pp = gdb.printing.RegexpCollectionPrettyPrinter("coroutine")
+ pp.add_printer('std::coroutine_handle', libcxx_corohdl_regex, CoroutineHandlePrinter)
+ return pp
+
+ gdb.printing.register_pretty_printer(
+ gdb.current_objfile(),
+ build_pretty_printer())
+
+
+ """
+ Get the coroutine frame pointer from a coroutine handle.
+
+ Usage:
+ ```
+ p *get_coro_frame(coroutine_hdl)
+ ```
+ """
+ class GetCoroFrame(gdb.Function):
+ def __init__(self):
+ super(GetCoroFrame, self).__init__("get_coro_frame")
+
+ def invoke(self, coroutine_hdl_raw):
+ return CoroutineHandlePrinter(coroutine_hdl_raw).frame_ptr
+
+ GetCoroFrame()
+
+
+ """
+ Get the coroutine frame pointer from a coroutine handle.
+
+ Usage:
+ ```
+ p *get_coro_promise(coroutine_hdl)
+ ```
+ """
+ class GetCoroFrame(gdb.Function):
+ def __init__(self):
+ super(GetCoroFrame, self).__init__("get_coro_promise")
+
+ def invoke(self, coroutine_hdl_raw):
+ return CoroutineHandlePrinter(coroutine_hdl_raw).promise_ptr
+
+ GetCoroFrame()
+
+
+ """
+ Decorator for coroutine frames.
+
+ Used by `CoroutineFrameFilter` to add the coroutine frames to the built-in `bt` command.
+ """
class CoroutineFrameDecorator(FrameDecorator):
- def __init__(self, coro_frame):
- super(CoroutineFrameDecorator, self).__init__(None)
+ def __init__(self, coro_frame: DevirtualizedCoroFrame, inferior_frame: gdb.Frame):
+ super(CoroutineFrameDecorator, self).__init__(inferior_frame)
self.coro_frame = coro_frame
- self.resume_func = dereference(self.coro_frame.resume_addr)
- self.resume_func_block = gdb.block_for_pc(self.resume_func)
- if self.resume_func_block is None:
- raise Exception('Not stackless coroutine.')
- self.line_info = gdb.find_pc_line(self.resume_func)
+
+ def function(self):
+ func_name = self.coro_frame.destroy_func.function.name
+ if func_name is not None:
+ return func_name
+ return "coroutine (coro_frame=" + str(self.coro_frame.frame_ptr_raw) + ")"
def address(self):
- return self.resume_func
+ return None
def filename(self):
- return self.line_info.symtab.filename
-
- def frame_args(self):
- return [SymValueWrapper("frame_addr", cast_addr2long_pointer(self.coro_frame.frame_addr)),
- SymValueWrapper("promise_addr", cast_addr2long_pointer(self.coro_frame.promise_addr)),
- SymValueWrapper("continuation_addr", cast_addr2long_pointer(self.coro_frame.continuation_addr))
- ]
-
- def function(self):
- return self.resume_func_block.function.print_name
+ if self.coro_frame.destroy_func is not None:
+ return self.coro_frame.destroy_func.function.symtab.filename
+ return None
def line(self):
- return self.line_info.line
-
- class StripDecorator(FrameDecorator):
- def __init__(self, frame):
- super(StripDecorator, self).__init__(frame)
- self.frame = frame
- f = frame.function()
- self.function_name = f
-
- def __str__(self, shift = 2):
- addr = "" if self.address() is None else '%#x' % self.address() + " in "
- location = "" if self.filename() is None else " at " + self.filename() + ":" + str(self.line())
- return addr + self.function() + " " + str([str(args) for args in self.frame_args()]) + location
-
- class CoroutineFilter:
- def create_coroutine_frames(self, task_addr):
- frames = []
- while task_addr != 0:
- coro_frame = CoroutineFrame(task_addr)
- frames.append(CoroutineFrameDecorator(coro_frame))
- task_addr = coro_frame.next_task_addr()
- return frames
-
- class AsyncStack(gdb.Command):
- def __init__(self):
- super(AsyncStack, self).__init__("async-bt", gdb.COMMAND_USER)
+ if self.coro_frame.resume_label is not None:
+ return self.coro_frame.resume_label.line
+ return None
- def invoke(self, arg, from_tty):
- coroutine_filter = CoroutineFilter()
- argv = gdb.string_to_argv(arg)
- if len(argv) == 0:
- try:
- task = gdb.parse_and_eval('__coro_frame')
- task = int(str(task.address), 16)
- except Exception:
- print ("Can't find __coro_frame in current context.\n" +
- "Please use `async-bt` in stackless coroutine context.")
- return
- elif len(argv) != 1:
- print("usage: async-bt <pointer to task>")
- return
- else:
- task = int(argv[0], 16)
+ def frame_args(self):
+ return []
- frames = coroutine_filter.create_coroutine_frames(task)
- i = 0
- for f in frames:
- print '#'+ str(i), str(StripDecorator(f))
- i += 1
- return
+ def frame_locals(self):
+ return []
- AsyncStack()
- class ShowCoroFrame(gdb.Command):
- def __init__(self):
- super(ShowCoroFrame, self).__init__("show-coro-frame", gdb.COMMAND_USER)
+ def _get_continuation(promise: gdb.Value) -> DevirtualizedCoroFrame | None:
+ try:
+ # TODO: adjust this according for your coroutine framework
+ return DevirtualizedCoroFrame(_extract_coro_frame_ptr_from_handle(promise["continuation"]))
+ except Exception as e:
+ return None
- def invoke(self, arg, from_tty):
- argv = gdb.string_to_argv(arg)
- if len(argv) != 1:
- print("usage: show-coro-frame <address of coroutine frame>")
- return
- addr = int(argv[0], 16)
- block = gdb.block_for_pc(long(cast_addr2long_pointer(addr).dereference()))
- if block is None:
- print "block " + str(addr) + " is None."
- return
+ def _create_coroutine_frames(coro_frame: DevirtualizedCoroFrame, inferior_frame: gdb.Frame):
+ while coro_frame is not None:
+ yield CoroutineFrameDecorator(coro_frame, inferior_frame)
+ coro_frame = _get_continuation(coro_frame.promise_ptr)
- # Disable demangling since gdb will treat names starting with `_Z`(The marker for Itanium ABI) specially.
- gdb.execute("set demangle-style none")
- coro_frame_type = gdb.lookup_type(block.function.linkage_name + ".coro_frame_ty")
- coro_frame_ptr_type = coro_frame_type.pointer()
- coro_frame = gdb.Value(addr).cast(coro_frame_ptr_type).dereference()
+ """
+ Frame filter to add coroutine frames to the built-in `bt` command.
+ """
+ class CppCoroutineFrameFilter():
+ def __init__(self):
+ self.name = "CppCoroutineFrameFilter"
+ self.priority = 50
+ self.enabled = True
+ # Register this frame filter with the global frame_filters dictionary.
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter: typing.Iterable[gdb.FrameDecorator]):
+ for frame in frame_iter:
+ yield frame
+ inferior_frame = frame.inferior_frame()
+ try:
+ promise_ptr = inferior_frame.read_var("__promise")
+ except Exception:
+ continue
+ parent_coro = _get_continuation(promise_ptr)
+ if parent_coro is not None:
+ yield from _create_coroutine_frames(parent_coro, inferior_frame)
- gdb.execute("set demangle-style auto")
- gdb.write(coro_frame.format_string(pretty_structs = True))
+ CppCoroutineFrameFilter()
- ShowCoroFrame()
+ if __name__ == '__main__':
+ print("This script should be loaded from GDB using `source <filename>`")
Further Reading
---------------
>From f1ab802bff413adb2bfc85ea8aac96a9a9b7cf10 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Mon, 6 Oct 2025 20:43:30 +0000
Subject: [PATCH 2/2] Fix typos
---
clang/docs/DebuggingCoroutines.rst | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/clang/docs/DebuggingCoroutines.rst b/clang/docs/DebuggingCoroutines.rst
index 4304270458d09..a3e640c0d94c3 100644
--- a/clang/docs/DebuggingCoroutines.rst
+++ b/clang/docs/DebuggingCoroutines.rst
@@ -206,11 +206,11 @@ Tracking the exact suspension point
Among the compiler-generated members, the ``__coro_index`` is particularly
important. This member identifies the suspension point at which the coroutine
-is currently suspended. However, it is non-trivial to map this number backto
+is currently suspended. However, it is non-trivial to map this number back to
a source code location.
For GDB, the provided :ref:`gdb-script` already takes care of this and provides
-the exact line number of the suspension point as part of the couroutine handle's
+the exact line number of the suspension point as part of the coroutine handle's
summary string. Unfortunately, LLDB's pretty-printer does not support this, yet.
Furthermore, those labels are only emitted starting with clang 21.0.
@@ -749,7 +749,7 @@ the suspension point.
Line 45 of "llvm-example.cpp" starts at address 0x201b <_ZL9coro_taski.destroy+555> and ends at 0x2046 <_ZL9coro_taski.destroy+598>.
Line 45 of "llvm-example.cpp" starts at address 0x253b <_ZL9coro_taski.cleanup+555> and ends at 0x2566 <_ZL9coro_taski.cleanup+598>.
-LLDB does not support looking up labels, yet. That's also why LLDB's pretty-printer
+LLDB does not support looking up labels, yet. For this reason, LLDB's pretty-printer
does not show the exact line number of the suspension point.
@@ -1014,13 +1014,11 @@ Note that this script requires LLDB 21.0 or newer.
GDB Debugger Script
-------------------
-For GDB, the following script provides a couple of useful commands:
-
-The following script provides
+The following script provides:
* a pretty-printer for coroutine handles
* a frame filter to add coroutine frames to the built-in ``bt`` command
-* the commands ``get_coro_frame`` and ``get_coro_promise`` to be used in
+* the ``get_coro_frame`` and ``get_coro_promise`` functions to be used in
expressions, e.g. ``p get_coro_promise(fib.coro_hdl)->current_state``
It can be loaded into GDB using ``source gdb_coro_debugging.py``.
More information about the cfe-commits
mailing list