xref: /llvm-project/clang/docs/DebuggingCoroutines.rst (revision e8182029516dae445f21db304953aa5f10880d2d)
11934b3aeSChuanqi Xu========================
21934b3aeSChuanqi XuDebugging C++ Coroutines
31934b3aeSChuanqi Xu========================
41934b3aeSChuanqi Xu
51934b3aeSChuanqi Xu.. contents::
61934b3aeSChuanqi Xu   :local:
71934b3aeSChuanqi Xu
81934b3aeSChuanqi XuIntroduction
91934b3aeSChuanqi Xu============
101934b3aeSChuanqi Xu
111934b3aeSChuanqi XuFor performance and other architectural reasons, the C++ Coroutines feature in
121934b3aeSChuanqi Xuthe Clang compiler is implemented in two parts of the compiler.  Semantic
131934b3aeSChuanqi Xuanalysis is performed in Clang, and Coroutine construction and optimization
141934b3aeSChuanqi Xutakes place in the LLVM middle-end.
151934b3aeSChuanqi Xu
161934b3aeSChuanqi XuHowever, this design forces us to generate insufficient debugging information.
171934b3aeSChuanqi XuTypically, the compiler generates debug information in the Clang frontend, as
181934b3aeSChuanqi Xudebug information is highly language specific. However, this is not possible
191934b3aeSChuanqi Xufor Coroutine frames because the frames are constructed in the LLVM middle-end.
201934b3aeSChuanqi Xu
211934b3aeSChuanqi XuTo mitigate this problem, the LLVM middle end attempts to generate some debug
221934b3aeSChuanqi Xuinformation, which is unfortunately incomplete, since much of the language
231934b3aeSChuanqi Xuspecific information is missing in the middle end.
241934b3aeSChuanqi Xu
251934b3aeSChuanqi XuThis document describes how to use this debug information to better debug
261934b3aeSChuanqi Xucoroutines.
271934b3aeSChuanqi Xu
281934b3aeSChuanqi XuTerminology
291934b3aeSChuanqi Xu===========
301934b3aeSChuanqi Xu
311934b3aeSChuanqi XuDue to the recent nature of C++20 Coroutines, the terminology used to describe
321934b3aeSChuanqi Xuthe concepts of Coroutines is not settled.  This section defines a common,
331934b3aeSChuanqi Xuunderstandable terminology to be used consistently throughout this document.
341934b3aeSChuanqi Xu
351934b3aeSChuanqi Xucoroutine type
361934b3aeSChuanqi Xu--------------
371934b3aeSChuanqi Xu
381934b3aeSChuanqi XuA `coroutine function` is any function that contains any of the Coroutine
391934b3aeSChuanqi XuKeywords `co_await`, `co_yield`, or `co_return`.  A `coroutine type` is a
401934b3aeSChuanqi Xupossible return type of one of these `coroutine functions`.  `Task` and
411934b3aeSChuanqi Xu`Generator` are commonly referred to coroutine types.
421934b3aeSChuanqi Xu
431934b3aeSChuanqi Xucoroutine
441934b3aeSChuanqi Xu---------
451934b3aeSChuanqi Xu
461934b3aeSChuanqi XuBy technical definition, a `coroutine` is a suspendable function. However,
471934b3aeSChuanqi Xuprogrammers typically use `coroutine` to refer to an individual instance.
481934b3aeSChuanqi XuFor example:
491934b3aeSChuanqi Xu
501934b3aeSChuanqi Xu.. code-block:: c++
511934b3aeSChuanqi Xu
521934b3aeSChuanqi Xu  std::vector<Task> Coros; // Task is a coroutine type.
531934b3aeSChuanqi Xu  for (int i = 0; i < 3; i++)
541934b3aeSChuanqi Xu    Coros.push_back(CoroTask()); // CoroTask is a coroutine function, which
551934b3aeSChuanqi Xu                                 // would return a coroutine type 'Task'.
561934b3aeSChuanqi Xu
571934b3aeSChuanqi XuIn practice, we typically say "`Coros` contains 3 coroutines" in the above
581934b3aeSChuanqi Xuexample, though this is not strictly correct.  More technically, this should
591934b3aeSChuanqi Xusay "`Coros` contains 3 coroutine instances" or "Coros contains 3 coroutine
601934b3aeSChuanqi Xuobjects."
611934b3aeSChuanqi Xu
621934b3aeSChuanqi XuIn this document, we follow the common practice of using `coroutine` to refer
631934b3aeSChuanqi Xuto an individual `coroutine instance`, since the terms `coroutine instance` and
641934b3aeSChuanqi Xu`coroutine object` aren't sufficiently defined in this case.
651934b3aeSChuanqi Xu
661934b3aeSChuanqi Xucoroutine frame
671934b3aeSChuanqi Xu---------------
681934b3aeSChuanqi Xu
691934b3aeSChuanqi XuThe C++ Standard uses `coroutine state` to describe the allocated storage. In
701934b3aeSChuanqi Xuthe compiler, we use `coroutine frame` to describe the generated data structure
711934b3aeSChuanqi Xuthat contains the necessary information.
721934b3aeSChuanqi Xu
731934b3aeSChuanqi XuThe structure of coroutine frames
741934b3aeSChuanqi Xu=================================
751934b3aeSChuanqi Xu
761934b3aeSChuanqi XuThe structure of coroutine frames is defined as:
771934b3aeSChuanqi Xu
781934b3aeSChuanqi Xu.. code-block:: c++
791934b3aeSChuanqi Xu
801934b3aeSChuanqi Xu  struct {
811934b3aeSChuanqi Xu    void (*__r)(); // function pointer to the `resume` function
821934b3aeSChuanqi Xu    void (*__d)(); // function pointer to the `destroy` function
831934b3aeSChuanqi Xu    promise_type; // the corresponding `promise_type`
841934b3aeSChuanqi Xu    ... // Any other needed information
851934b3aeSChuanqi Xu  }
861934b3aeSChuanqi Xu
871934b3aeSChuanqi XuIn the debugger, the function's name is obtainable from the address of the
881934b3aeSChuanqi Xufunction. And the name of `resume` function is equal to the name of the
891934b3aeSChuanqi Xucoroutine function. So the name of the coroutine is obtainable once the
901934b3aeSChuanqi Xuaddress of the coroutine is known.
911934b3aeSChuanqi Xu
921934b3aeSChuanqi XuPrint promise_type
931934b3aeSChuanqi Xu==================
941934b3aeSChuanqi Xu
951934b3aeSChuanqi XuEvery coroutine has a `promise_type`, which defines the behavior
961934b3aeSChuanqi Xufor the corresponding coroutine. In other words, if two coroutines have the
971934b3aeSChuanqi Xusame `promise_type`, they should behave in the same way.
981934b3aeSChuanqi XuTo print a `promise_type` in a debugger when stopped at a breakpoint inside a
991934b3aeSChuanqi Xucoroutine, printing the `promise_type` can be done by:
1001934b3aeSChuanqi Xu
1011934b3aeSChuanqi Xu.. parsed-literal::
1021934b3aeSChuanqi Xu
1031934b3aeSChuanqi Xu  print __promise
1041934b3aeSChuanqi Xu
1051934b3aeSChuanqi XuIt is also possible to print the `promise_type` of a coroutine from the address
1061934b3aeSChuanqi Xuof the coroutine frame. For example, if the address of a coroutine frame is
1071934b3aeSChuanqi Xu0x416eb0, and the type of the `promise_type` is `task::promise_type`, printing
1081934b3aeSChuanqi Xuthe `promise_type` can be done by:
1091934b3aeSChuanqi Xu
1101934b3aeSChuanqi Xu.. parsed-literal::
1111934b3aeSChuanqi Xu
1121934b3aeSChuanqi Xu  print (task::promise_type)*(0x416eb0+0x10)
1131934b3aeSChuanqi Xu
1141934b3aeSChuanqi XuThis is possible because the `promise_type` is guaranteed by the ABI to be at a
1151934b3aeSChuanqi Xu16 bit offset from the coroutine frame.
1161934b3aeSChuanqi Xu
1171934b3aeSChuanqi XuNote that there is also an ABI independent method:
1181934b3aeSChuanqi Xu
1191934b3aeSChuanqi Xu.. parsed-literal::
1201934b3aeSChuanqi Xu
1211934b3aeSChuanqi Xu  print std::coroutine_handle<task::promise_type>::from_address((void*)0x416eb0).promise()
1221934b3aeSChuanqi Xu
1231934b3aeSChuanqi XuThe functions `from_address(void*)` and `promise()` are often small enough to
1241934b3aeSChuanqi Xube removed during optimization, so this method may not be possible.
1251934b3aeSChuanqi Xu
1261934b3aeSChuanqi XuPrint coroutine frames
1271934b3aeSChuanqi Xu======================
1281934b3aeSChuanqi Xu
1291934b3aeSChuanqi XuLLVM generates the debug information for the coroutine frame in the LLVM middle
1301934b3aeSChuanqi Xuend, which permits printing of the coroutine frame in the debugger. Much like
1311934b3aeSChuanqi Xuthe `promise_type`, when stopped at a breakpoint inside a coroutine we can
1321934b3aeSChuanqi Xuprint the coroutine frame by:
1331934b3aeSChuanqi Xu
1341934b3aeSChuanqi Xu.. parsed-literal::
1351934b3aeSChuanqi Xu
1361934b3aeSChuanqi Xu  print __coro_frame
1371934b3aeSChuanqi Xu
1381934b3aeSChuanqi Xu
1391934b3aeSChuanqi XuJust as printing the `promise_type` is possible from the coroutine address,
1401934b3aeSChuanqi Xuprinting the details of the coroutine frame from an address is also possible:
1411934b3aeSChuanqi Xu
14235f48572SAaron Ballman::
1431934b3aeSChuanqi Xu
1441934b3aeSChuanqi Xu  (gdb) # Get the address of coroutine frame
1451934b3aeSChuanqi Xu  (gdb) print/x *0x418eb0
1461934b3aeSChuanqi Xu  $1 = 0x4019e0
1471934b3aeSChuanqi Xu  (gdb) # Get the linkage name for the coroutine
1481934b3aeSChuanqi Xu  (gdb) x 0x4019e0
1491934b3aeSChuanqi Xu  0x4019e0 <_ZL9coro_taski>:  0xe5894855
150210a4197SChuanqi Xu  (gdb) # Turn off the demangler temporarily to avoid the debugger misunderstanding the name.
151210a4197SChuanqi Xu  (gdb) set demangle-style none
1521934b3aeSChuanqi Xu  (gdb) # The coroutine frame type is 'linkage_name.coro_frame_ty'
153210a4197SChuanqi Xu  (gdb) print  ('_ZL9coro_taski.coro_frame_ty')*(0x418eb0)
1541934b3aeSChuanqi Xu  $2 = {__resume_fn = 0x4019e0 <coro_task(int)>, __destroy_fn = 0x402000 <coro_task(int)>, __promise = {...}, ...}
1551934b3aeSChuanqi Xu
1561934b3aeSChuanqi XuThe above is possible because:
1571934b3aeSChuanqi Xu
1581934b3aeSChuanqi Xu(1) The name of the debug type of the coroutine frame is the `linkage_name`,
1591934b3aeSChuanqi Xuplus the `.coro_frame_ty` suffix because each coroutine function shares the
1601934b3aeSChuanqi Xusame coroutine type.
1611934b3aeSChuanqi Xu
1621934b3aeSChuanqi Xu(2) The coroutine function name is accessible from the address of the coroutine
1631934b3aeSChuanqi Xuframe.
1641934b3aeSChuanqi Xu
1651934b3aeSChuanqi XuThe above commands can be simplified by placing them in debug scripts.
1661934b3aeSChuanqi Xu
1671934b3aeSChuanqi XuExamples to print coroutine frames
1681934b3aeSChuanqi Xu----------------------------------
1691934b3aeSChuanqi Xu
1701934b3aeSChuanqi XuThe print examples below use the following definition:
1711934b3aeSChuanqi Xu
1721934b3aeSChuanqi Xu.. code-block:: c++
1731934b3aeSChuanqi Xu
1741934b3aeSChuanqi Xu  #include <coroutine>
1751934b3aeSChuanqi Xu  #include <iostream>
1761934b3aeSChuanqi Xu
1771934b3aeSChuanqi Xu  struct task{
1781934b3aeSChuanqi Xu    struct promise_type {
1791934b3aeSChuanqi Xu      task get_return_object() { return std::coroutine_handle<promise_type>::from_promise(*this); }
1801934b3aeSChuanqi Xu      std::suspend_always initial_suspend() { return {}; }
1811934b3aeSChuanqi Xu      std::suspend_always final_suspend() noexcept { return {}; }
1821934b3aeSChuanqi Xu      void return_void() noexcept {}
1831934b3aeSChuanqi Xu      void unhandled_exception() noexcept {}
1841934b3aeSChuanqi Xu
1851934b3aeSChuanqi Xu      int count = 0;
1861934b3aeSChuanqi Xu    };
1871934b3aeSChuanqi Xu
1881934b3aeSChuanqi Xu    void resume() noexcept {
1891934b3aeSChuanqi Xu      handle.resume();
1901934b3aeSChuanqi Xu    }
1911934b3aeSChuanqi Xu
1921934b3aeSChuanqi Xu    task(std::coroutine_handle<promise_type> hdl) : handle(hdl) {}
1931934b3aeSChuanqi Xu    ~task() {
1941934b3aeSChuanqi Xu      if (handle)
1951934b3aeSChuanqi Xu        handle.destroy();
1961934b3aeSChuanqi Xu    }
1971934b3aeSChuanqi Xu
1981934b3aeSChuanqi Xu    std::coroutine_handle<> handle;
1991934b3aeSChuanqi Xu  };
2001934b3aeSChuanqi Xu
2011934b3aeSChuanqi Xu  class await_counter : public std::suspend_always {
2021934b3aeSChuanqi Xu    public:
2031934b3aeSChuanqi Xu      template<class PromiseType>
2041934b3aeSChuanqi Xu      void await_suspend(std::coroutine_handle<PromiseType> handle) noexcept {
2051934b3aeSChuanqi Xu          handle.promise().count++;
2061934b3aeSChuanqi Xu      }
2071934b3aeSChuanqi Xu  };
2081934b3aeSChuanqi Xu
2091934b3aeSChuanqi Xu  static task coro_task(int v) {
2101934b3aeSChuanqi Xu    int a = v;
2111934b3aeSChuanqi Xu    co_await await_counter{};
2121934b3aeSChuanqi Xu    a++;
2131934b3aeSChuanqi Xu    std::cout << a << "\n";
2141934b3aeSChuanqi Xu    a++;
2151934b3aeSChuanqi Xu    std::cout << a << "\n";
2161934b3aeSChuanqi Xu    a++;
2171934b3aeSChuanqi Xu    std::cout << a << "\n";
2181934b3aeSChuanqi Xu    co_await await_counter{};
2191934b3aeSChuanqi Xu    a++;
2201934b3aeSChuanqi Xu    std::cout << a << "\n";
2211934b3aeSChuanqi Xu    a++;
2221934b3aeSChuanqi Xu    std::cout << a << "\n";
2231934b3aeSChuanqi Xu  }
2241934b3aeSChuanqi Xu
2251934b3aeSChuanqi Xu  int main() {
2261934b3aeSChuanqi Xu    task t = coro_task(43);
2271934b3aeSChuanqi Xu    t.resume();
2281934b3aeSChuanqi Xu    t.resume();
2291934b3aeSChuanqi Xu    t.resume();
2301934b3aeSChuanqi Xu    return 0;
2311934b3aeSChuanqi Xu  }
2321934b3aeSChuanqi Xu
2331934b3aeSChuanqi XuIn debug mode (`O0` + `g`), the printing result would be:
2341934b3aeSChuanqi Xu
2351934b3aeSChuanqi Xu.. parsed-literal::
2361934b3aeSChuanqi Xu
2371934b3aeSChuanqi Xu  {__resume_fn = 0x4019e0 <coro_task(int)>, __destroy_fn = 0x402000 <coro_task(int)>, __promise = {count = 1}, v = 43, a = 45, __coro_index = 1 '\001', struct_std__suspend_always_0 = {__int_8 = 0 '\000'},
2381934b3aeSChuanqi Xu    class_await_counter_1 = {__int_8 = 0 '\000'}, class_await_counter_2 = {__int_8 = 0 '\000'}, struct_std__suspend_always_3 = {__int_8 = 0 '\000'}}
2391934b3aeSChuanqi Xu
2401934b3aeSChuanqi XuIn the above, the values of `v` and `a` are clearly expressed, as are the
2411934b3aeSChuanqi Xutemporary values for `await_counter` (`class_await_counter_1` and
2421934b3aeSChuanqi Xu`class_await_counter_2`) and `std::suspend_always` (
2431934b3aeSChuanqi Xu`struct_std__suspend_always_0` and `struct_std__suspend_always_3`). The index
2441934b3aeSChuanqi Xuof the current suspension point of the coroutine is emitted as `__coro_index`.
2451934b3aeSChuanqi XuIn the above example, the `__coro_index` value of `1` means the coroutine
2461934b3aeSChuanqi Xustopped at the second suspend point (Note that `__coro_index` is zero indexed)
2471934b3aeSChuanqi Xuwhich is the first `co_await await_counter{};` in `coro_task`. Note that the
2481934b3aeSChuanqi Xufirst initial suspend point is the compiler generated
2491934b3aeSChuanqi Xu`co_await promise_type::initial_suspend()`.
2501934b3aeSChuanqi Xu
2511934b3aeSChuanqi XuHowever, when optimizations are enabled, the printed result changes drastically:
2521934b3aeSChuanqi Xu
2531934b3aeSChuanqi Xu.. parsed-literal::
2541934b3aeSChuanqi Xu
2551934b3aeSChuanqi Xu  {__resume_fn = 0x401280 <coro_task(int)>, __destroy_fn = 0x401390 <coro_task(int)>, __promise = {count = 1}, __int_32_0 = 43, __coro_index = 1 '\001'}
2561934b3aeSChuanqi Xu
2571934b3aeSChuanqi XuUnused values are optimized out, as well as the name of the local variable `a`.
2581934b3aeSChuanqi XuThe only information remained is the value of a 32 bit integer. In this simple
2591934b3aeSChuanqi Xucase, it seems to be pretty clear that `__int_32_0` represents `a`. However, it
2601934b3aeSChuanqi Xuis not true.
2611934b3aeSChuanqi Xu
2621934b3aeSChuanqi XuAn important note with optimization is that the value of a variable may not
2631934b3aeSChuanqi Xuproperly express the intended value in the source code.  For example:
2641934b3aeSChuanqi Xu
2651934b3aeSChuanqi Xu.. code-block:: c++
2661934b3aeSChuanqi Xu
2671934b3aeSChuanqi Xu  static task coro_task(int v) {
2681934b3aeSChuanqi Xu    int a = v;
2691934b3aeSChuanqi Xu    co_await await_counter{};
2701934b3aeSChuanqi Xu    a++; // __int_32_0 is 43 here
2711934b3aeSChuanqi Xu    std::cout << a << "\n";
2721934b3aeSChuanqi Xu    a++; // __int_32_0 is still 43 here
2731934b3aeSChuanqi Xu    std::cout << a << "\n";
2741934b3aeSChuanqi Xu    a++; // __int_32_0 is still 43 here!
2751934b3aeSChuanqi Xu    std::cout << a << "\n";
2761934b3aeSChuanqi Xu    co_await await_counter{};
2771934b3aeSChuanqi Xu    a++; // __int_32_0 is still 43 here!!
2781934b3aeSChuanqi Xu    std::cout << a << "\n";
2791934b3aeSChuanqi Xu    a++; // Why is __int_32_0 still 43 here?
2801934b3aeSChuanqi Xu    std::cout << a << "\n";
2811934b3aeSChuanqi Xu  }
2821934b3aeSChuanqi Xu
2831934b3aeSChuanqi XuWhen debugging step-by-step, the value of `__int_32_0` seemingly does not
2841934b3aeSChuanqi Xuchange, despite being frequently incremented, and instead is always `43`.
2851934b3aeSChuanqi XuWhile this might be surprising, this is a result of the optimizer recognizing
2861934b3aeSChuanqi Xuthat it can eliminate most of the load/store operations. The above code gets
2871934b3aeSChuanqi Xuoptimized to the equivalent of:
2881934b3aeSChuanqi Xu
2891934b3aeSChuanqi Xu.. code-block:: c++
2901934b3aeSChuanqi Xu
2911934b3aeSChuanqi Xu  static task coro_task(int v) {
2921934b3aeSChuanqi Xu    store v to __int_32_0 in the frame
2931934b3aeSChuanqi Xu    co_await await_counter{};
2941934b3aeSChuanqi Xu    a = load __int_32_0
2951934b3aeSChuanqi Xu    std::cout << a+1 << "\n";
2961934b3aeSChuanqi Xu    std::cout << a+2 << "\n";
2971934b3aeSChuanqi Xu    std::cout << a+3 << "\n";
2981934b3aeSChuanqi Xu    co_await await_counter{};
2991934b3aeSChuanqi Xu    a = load __int_32_0
3001934b3aeSChuanqi Xu    std::cout << a+4 << "\n";
3011934b3aeSChuanqi Xu    std::cout << a+5 << "\n";
3021934b3aeSChuanqi Xu  }
3031934b3aeSChuanqi Xu
3041934b3aeSChuanqi XuIt should now be obvious why the value of `__int_32_0` remains unchanged
3051934b3aeSChuanqi Xuthroughout the function. It is important to recognize that `__int_32_0`
3061934b3aeSChuanqi Xudoes not directly correspond to `a`, but is instead a variable generated
3071934b3aeSChuanqi Xuto assist the compiler in code generation. The variables in an optimized
3081934b3aeSChuanqi Xucoroutine frame should not be thought of as directly representing the
3091934b3aeSChuanqi Xuvariables in the C++ source.
3101934b3aeSChuanqi Xu
3111934b3aeSChuanqi XuGet the suspended points
3121934b3aeSChuanqi Xu========================
3131934b3aeSChuanqi Xu
3141934b3aeSChuanqi XuAn important requirement for debugging coroutines is to understand suspended
3151934b3aeSChuanqi Xupoints, which are where the coroutine is currently suspended and awaiting.
3161934b3aeSChuanqi Xu
3171934b3aeSChuanqi XuFor simple cases like the above, inspecting the value of the `__coro_index`
3181934b3aeSChuanqi Xuvariable in the coroutine frame works well.
3191934b3aeSChuanqi Xu
3201934b3aeSChuanqi XuHowever, it is not quite so simple in really complex situations. In these
3211934b3aeSChuanqi Xucases, it is necessary to use the coroutine libraries to insert the
3221934b3aeSChuanqi Xuline-number.
3231934b3aeSChuanqi Xu
3241934b3aeSChuanqi XuFor example:
3251934b3aeSChuanqi Xu
3261934b3aeSChuanqi Xu.. code-block:: c++
3271934b3aeSChuanqi Xu
3281934b3aeSChuanqi Xu  // For all the promise_type we want:
3291934b3aeSChuanqi Xu  class promise_type {
3301934b3aeSChuanqi Xu    ...
3311934b3aeSChuanqi Xu  +  unsigned line_number = 0xffffffff;
3321934b3aeSChuanqi Xu  };
3331934b3aeSChuanqi Xu
3341934b3aeSChuanqi Xu  #include <source_location>
3351934b3aeSChuanqi Xu
3361934b3aeSChuanqi Xu  // For all the awaiter types we need:
3371934b3aeSChuanqi Xu  class awaiter {
3381934b3aeSChuanqi Xu    ...
3391934b3aeSChuanqi Xu    template <typename Promise>
3401934b3aeSChuanqi Xu    void await_suspend(std::coroutine_handle<Promise> handle,
3411934b3aeSChuanqi Xu                       std::source_location sl = std::source_location::current()) {
3421934b3aeSChuanqi Xu          ...
3431934b3aeSChuanqi Xu          handle.promise().line_number = sl.line();
3441934b3aeSChuanqi Xu    }
3451934b3aeSChuanqi Xu  };
3461934b3aeSChuanqi Xu
3471934b3aeSChuanqi XuIn this case, we use `std::source_location` to store the line number of the
3481934b3aeSChuanqi Xuawait inside the `promise_type`.  Since we can locate the coroutine function
3491934b3aeSChuanqi Xufrom the address of the coroutine, we can identify suspended points this way
3501934b3aeSChuanqi Xuas well.
3511934b3aeSChuanqi Xu
3521934b3aeSChuanqi XuThe downside here is that this comes at the price of additional runtime cost.
3531934b3aeSChuanqi XuThis is consistent with the C++ philosophy of "Pay for what you use".
3541934b3aeSChuanqi Xu
3551934b3aeSChuanqi XuGet the asynchronous stack
3561934b3aeSChuanqi Xu==========================
3571934b3aeSChuanqi Xu
3581934b3aeSChuanqi XuAnother important requirement to debug a coroutine is to print the asynchronous
3591934b3aeSChuanqi Xustack to identify the asynchronous caller of the coroutine.  As many
3601934b3aeSChuanqi Xuimplementations of coroutine types store `std::coroutine_handle<> continuation`
3611934b3aeSChuanqi Xuin the promise type, identifying the caller should be trivial.  The
3621934b3aeSChuanqi Xu`continuation` is typically the awaiting coroutine for the current coroutine.
3631934b3aeSChuanqi XuThat is, the asynchronous parent.
3641934b3aeSChuanqi Xu
3651934b3aeSChuanqi XuSince the `promise_type` is obtainable from the address of a coroutine and
3661934b3aeSChuanqi Xucontains the corresponding continuation (which itself is a coroutine with a
3671934b3aeSChuanqi Xu`promise_type`), it should be trivial to print the entire asynchronous stack.
3681934b3aeSChuanqi Xu
3691934b3aeSChuanqi XuThis logic should be quite easily captured in a debugger script.
3701934b3aeSChuanqi Xu
3714332b049SChuanqi XuExamples to print asynchronous stack
3724332b049SChuanqi Xu------------------------------------
3734332b049SChuanqi Xu
3744332b049SChuanqi XuHere is an example to print the asynchronous stack for the normal task implementation.
3754332b049SChuanqi Xu
3764332b049SChuanqi Xu.. code-block:: c++
3774332b049SChuanqi Xu
3784332b049SChuanqi Xu  // debugging-example.cpp
3794332b049SChuanqi Xu  #include <coroutine>
3804332b049SChuanqi Xu  #include <iostream>
3814332b049SChuanqi Xu  #include <utility>
3824332b049SChuanqi Xu
3834332b049SChuanqi Xu  struct task {
3844332b049SChuanqi Xu    struct promise_type {
3854332b049SChuanqi Xu      task get_return_object();
3864332b049SChuanqi Xu      std::suspend_always initial_suspend() { return {}; }
3874332b049SChuanqi Xu
3884332b049SChuanqi Xu      void unhandled_exception() noexcept {}
3894332b049SChuanqi Xu
3904332b049SChuanqi Xu      struct FinalSuspend {
3914332b049SChuanqi Xu        std::coroutine_handle<> continuation;
3924332b049SChuanqi Xu        auto await_ready() noexcept { return false; }
3934332b049SChuanqi Xu        auto await_suspend(std::coroutine_handle<> handle) noexcept {
3944332b049SChuanqi Xu          return continuation;
3954332b049SChuanqi Xu        }
3964332b049SChuanqi Xu        void await_resume() noexcept {}
3974332b049SChuanqi Xu      };
3984332b049SChuanqi Xu      FinalSuspend final_suspend() noexcept { return {continuation}; }
3994332b049SChuanqi Xu
4004332b049SChuanqi Xu      void return_value(int res) { result = res; }
4014332b049SChuanqi Xu
4024332b049SChuanqi Xu      std::coroutine_handle<> continuation = std::noop_coroutine();
4034332b049SChuanqi Xu      int result = 0;
4044332b049SChuanqi Xu    };
4054332b049SChuanqi Xu
4064332b049SChuanqi Xu    task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
4074332b049SChuanqi Xu    ~task() {
4084332b049SChuanqi Xu      if (handle)
4094332b049SChuanqi Xu        handle.destroy();
4104332b049SChuanqi Xu    }
4114332b049SChuanqi Xu
4124332b049SChuanqi Xu    auto operator co_await() {
4134332b049SChuanqi Xu      struct Awaiter {
4144332b049SChuanqi Xu        std::coroutine_handle<promise_type> handle;
4154332b049SChuanqi Xu        auto await_ready() { return false; }
4164332b049SChuanqi Xu        auto await_suspend(std::coroutine_handle<> continuation) {
4174332b049SChuanqi Xu          handle.promise().continuation = continuation;
4184332b049SChuanqi Xu          return handle;
4194332b049SChuanqi Xu        }
4204332b049SChuanqi Xu        int await_resume() {
4214332b049SChuanqi Xu          int ret = handle.promise().result;
4224332b049SChuanqi Xu          handle.destroy();
4234332b049SChuanqi Xu          return ret;
4244332b049SChuanqi Xu        }
4254332b049SChuanqi Xu      };
4264332b049SChuanqi Xu      return Awaiter{std::exchange(handle, nullptr)};
4274332b049SChuanqi Xu    }
4284332b049SChuanqi Xu
4294332b049SChuanqi Xu    int syncStart() {
4304332b049SChuanqi Xu      handle.resume();
4314332b049SChuanqi Xu      return handle.promise().result;
4324332b049SChuanqi Xu    }
4334332b049SChuanqi Xu
4344332b049SChuanqi Xu  private:
4354332b049SChuanqi Xu    std::coroutine_handle<promise_type> handle;
4364332b049SChuanqi Xu  };
4374332b049SChuanqi Xu
4384332b049SChuanqi Xu  task task::promise_type::get_return_object() {
4394332b049SChuanqi Xu    return std::coroutine_handle<promise_type>::from_promise(*this);
4404332b049SChuanqi Xu  }
4414332b049SChuanqi Xu
4424332b049SChuanqi Xu  namespace detail {
4434332b049SChuanqi Xu  template <int N>
4444332b049SChuanqi Xu  task chain_fn() {
4454332b049SChuanqi Xu    co_return N + co_await chain_fn<N - 1>();
4464332b049SChuanqi Xu  }
4474332b049SChuanqi Xu
4484332b049SChuanqi Xu  template <>
4494332b049SChuanqi Xu  task chain_fn<0>() {
4504332b049SChuanqi Xu    // This is the default breakpoint.
4514332b049SChuanqi Xu    __builtin_debugtrap();
4524332b049SChuanqi Xu    co_return 0;
4534332b049SChuanqi Xu  }
4544332b049SChuanqi Xu  }  // namespace detail
4554332b049SChuanqi Xu
4564332b049SChuanqi Xu  task chain() {
4574332b049SChuanqi Xu    co_return co_await detail::chain_fn<30>();
4584332b049SChuanqi Xu  }
4594332b049SChuanqi Xu
4604332b049SChuanqi Xu  int main() {
4614332b049SChuanqi Xu    std::cout << chain().syncStart() << "\n";
4624332b049SChuanqi Xu    return 0;
4634332b049SChuanqi Xu  }
4644332b049SChuanqi Xu
4654332b049SChuanqi XuIn the example, the ``task`` coroutine holds a ``continuation`` field,
4664332b049SChuanqi Xuwhich would be resumed once the ``task`` completes.
4674332b049SChuanqi XuIn another word, the ``continuation`` is the asynchronous caller for the ``task``.
4684332b049SChuanqi XuJust like the normal function returns to its caller when the function completes.
4694332b049SChuanqi Xu
4704332b049SChuanqi XuSo we can use the ``continuation`` field to construct the asynchronous stack:
4714332b049SChuanqi Xu
4724332b049SChuanqi Xu.. code-block:: python
4734332b049SChuanqi Xu
4744332b049SChuanqi Xu  # debugging-helper.py
4754332b049SChuanqi Xu  import gdb
4764332b049SChuanqi Xu  from gdb.FrameDecorator import FrameDecorator
4774332b049SChuanqi Xu
4784332b049SChuanqi Xu  class SymValueWrapper():
4794332b049SChuanqi Xu      def __init__(self, symbol, value):
4804332b049SChuanqi Xu          self.sym = symbol
4814332b049SChuanqi Xu          self.val = value
4824332b049SChuanqi Xu
4834332b049SChuanqi Xu      def __str__(self):
4844332b049SChuanqi Xu          return str(self.sym) + " = " + str(self.val)
4854332b049SChuanqi Xu
4864332b049SChuanqi Xu  def get_long_pointer_size():
4874332b049SChuanqi Xu      return gdb.lookup_type('long').pointer().sizeof
4884332b049SChuanqi Xu
4894332b049SChuanqi Xu  def cast_addr2long_pointer(addr):
4904332b049SChuanqi Xu      return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
4914332b049SChuanqi Xu
4924332b049SChuanqi Xu  def dereference(addr):
4934332b049SChuanqi Xu      return long(cast_addr2long_pointer(addr).dereference())
4944332b049SChuanqi Xu
4954332b049SChuanqi Xu  class CoroutineFrame(object):
4964332b049SChuanqi Xu      def __init__(self, task_addr):
4974332b049SChuanqi Xu          self.frame_addr = task_addr
4984332b049SChuanqi Xu          self.resume_addr = task_addr
4994332b049SChuanqi Xu          self.destroy_addr = task_addr + get_long_pointer_size()
5004332b049SChuanqi Xu          self.promise_addr = task_addr + get_long_pointer_size() * 2
5014332b049SChuanqi Xu          # In the example, the continuation is the first field member of the promise_type.
5024332b049SChuanqi Xu          # So they have the same addresses.
5034332b049SChuanqi Xu          # If we want to generalize the scripts to other coroutine types, we need to be sure
50411e29758SKazu Hirata          # the continuation field is the first member of promise_type.
5054332b049SChuanqi Xu          self.continuation_addr = self.promise_addr
5064332b049SChuanqi Xu
5074332b049SChuanqi Xu      def next_task_addr(self):
5084332b049SChuanqi Xu          return dereference(self.continuation_addr)
5094332b049SChuanqi Xu
5104332b049SChuanqi Xu  class CoroutineFrameDecorator(FrameDecorator):
5114332b049SChuanqi Xu      def __init__(self, coro_frame):
5124332b049SChuanqi Xu          super(CoroutineFrameDecorator, self).__init__(None)
5134332b049SChuanqi Xu          self.coro_frame = coro_frame
5144332b049SChuanqi Xu          self.resume_func = dereference(self.coro_frame.resume_addr)
5154332b049SChuanqi Xu          self.resume_func_block = gdb.block_for_pc(self.resume_func)
516*e8182029SEisuke Kawashima          if self.resume_func_block is None:
5174332b049SChuanqi Xu              raise Exception('Not stackless coroutine.')
5184332b049SChuanqi Xu          self.line_info = gdb.find_pc_line(self.resume_func)
5194332b049SChuanqi Xu
5204332b049SChuanqi Xu      def address(self):
5214332b049SChuanqi Xu          return self.resume_func
5224332b049SChuanqi Xu
5234332b049SChuanqi Xu      def filename(self):
5244332b049SChuanqi Xu          return self.line_info.symtab.filename
5254332b049SChuanqi Xu
5264332b049SChuanqi Xu      def frame_args(self):
5274332b049SChuanqi Xu          return [SymValueWrapper("frame_addr", cast_addr2long_pointer(self.coro_frame.frame_addr)),
5284332b049SChuanqi Xu                  SymValueWrapper("promise_addr", cast_addr2long_pointer(self.coro_frame.promise_addr)),
5294332b049SChuanqi Xu                  SymValueWrapper("continuation_addr", cast_addr2long_pointer(self.coro_frame.continuation_addr))
5304332b049SChuanqi Xu                  ]
5314332b049SChuanqi Xu
5324332b049SChuanqi Xu      def function(self):
5334332b049SChuanqi Xu          return self.resume_func_block.function.print_name
5344332b049SChuanqi Xu
5354332b049SChuanqi Xu      def line(self):
5364332b049SChuanqi Xu          return self.line_info.line
5374332b049SChuanqi Xu
5384332b049SChuanqi Xu  class StripDecorator(FrameDecorator):
5394332b049SChuanqi Xu      def __init__(self, frame):
5404332b049SChuanqi Xu          super(StripDecorator, self).__init__(frame)
5414332b049SChuanqi Xu          self.frame = frame
5424332b049SChuanqi Xu          f = frame.function()
5434332b049SChuanqi Xu          self.function_name = f
5444332b049SChuanqi Xu
5454332b049SChuanqi Xu      def __str__(self, shift = 2):
546*e8182029SEisuke Kawashima          addr = "" if self.address() is None else '%#x' % self.address() + " in "
547*e8182029SEisuke Kawashima          location = "" if self.filename() is None else " at " + self.filename() + ":" + str(self.line())
5484332b049SChuanqi Xu          return addr + self.function() + " " + str([str(args) for args in self.frame_args()]) + location
5494332b049SChuanqi Xu
5504332b049SChuanqi Xu  class CoroutineFilter:
5514332b049SChuanqi Xu      def create_coroutine_frames(self, task_addr):
5524332b049SChuanqi Xu          frames = []
5534332b049SChuanqi Xu          while task_addr != 0:
5544332b049SChuanqi Xu              coro_frame = CoroutineFrame(task_addr)
5554332b049SChuanqi Xu              frames.append(CoroutineFrameDecorator(coro_frame))
5564332b049SChuanqi Xu              task_addr = coro_frame.next_task_addr()
5574332b049SChuanqi Xu          return frames
5584332b049SChuanqi Xu
5594332b049SChuanqi Xu  class AsyncStack(gdb.Command):
5604332b049SChuanqi Xu      def __init__(self):
5614332b049SChuanqi Xu          super(AsyncStack, self).__init__("async-bt", gdb.COMMAND_USER)
5624332b049SChuanqi Xu
5634332b049SChuanqi Xu      def invoke(self, arg, from_tty):
5644332b049SChuanqi Xu          coroutine_filter = CoroutineFilter()
5654332b049SChuanqi Xu          argv = gdb.string_to_argv(arg)
5664332b049SChuanqi Xu          if len(argv) == 0:
5674332b049SChuanqi Xu              try:
5684332b049SChuanqi Xu                  task = gdb.parse_and_eval('__coro_frame')
5694332b049SChuanqi Xu                  task = int(str(task.address), 16)
5704332b049SChuanqi Xu              except Exception:
5714332b049SChuanqi Xu                  print ("Can't find __coro_frame in current context.\n" +
5724332b049SChuanqi Xu                        "Please use `async-bt` in stackless coroutine context.")
5734332b049SChuanqi Xu                  return
5744332b049SChuanqi Xu          elif len(argv) != 1:
5754332b049SChuanqi Xu              print("usage: async-bt <pointer to task>")
5764332b049SChuanqi Xu              return
5774332b049SChuanqi Xu          else:
5784332b049SChuanqi Xu              task = int(argv[0], 16)
5794332b049SChuanqi Xu
5804332b049SChuanqi Xu          frames = coroutine_filter.create_coroutine_frames(task)
5814332b049SChuanqi Xu          i = 0
5824332b049SChuanqi Xu          for f in frames:
5834332b049SChuanqi Xu              print '#'+ str(i), str(StripDecorator(f))
5844332b049SChuanqi Xu              i += 1
5854332b049SChuanqi Xu          return
5864332b049SChuanqi Xu
5874332b049SChuanqi Xu  AsyncStack()
5884332b049SChuanqi Xu
5894332b049SChuanqi Xu  class ShowCoroFrame(gdb.Command):
5904332b049SChuanqi Xu      def __init__(self):
5914332b049SChuanqi Xu          super(ShowCoroFrame, self).__init__("show-coro-frame", gdb.COMMAND_USER)
5924332b049SChuanqi Xu
5934332b049SChuanqi Xu      def invoke(self, arg, from_tty):
5944332b049SChuanqi Xu          argv = gdb.string_to_argv(arg)
5954332b049SChuanqi Xu          if len(argv) != 1:
5964332b049SChuanqi Xu              print("usage: show-coro-frame <address of coroutine frame>")
5974332b049SChuanqi Xu              return
5984332b049SChuanqi Xu
5994332b049SChuanqi Xu          addr = int(argv[0], 16)
6004332b049SChuanqi Xu          block = gdb.block_for_pc(long(cast_addr2long_pointer(addr).dereference()))
601*e8182029SEisuke Kawashima          if block is None:
6024332b049SChuanqi Xu              print "block " + str(addr) + "  is none."
6034332b049SChuanqi Xu              return
6044332b049SChuanqi Xu
6054332b049SChuanqi Xu          # Disable demangling since gdb will treat names starting with `_Z`(The marker for Itanium ABI) specially.
6064332b049SChuanqi Xu          gdb.execute("set demangle-style none")
6074332b049SChuanqi Xu
6084332b049SChuanqi Xu          coro_frame_type = gdb.lookup_type(block.function.linkage_name + ".coro_frame_ty")
6094332b049SChuanqi Xu          coro_frame_ptr_type = coro_frame_type.pointer()
6104332b049SChuanqi Xu          coro_frame = gdb.Value(addr).cast(coro_frame_ptr_type).dereference()
6114332b049SChuanqi Xu
6124332b049SChuanqi Xu          gdb.execute("set demangle-style auto")
6134332b049SChuanqi Xu          gdb.write(coro_frame.format_string(pretty_structs = True))
6144332b049SChuanqi Xu
6154332b049SChuanqi Xu  ShowCoroFrame()
6164332b049SChuanqi Xu
6174332b049SChuanqi XuThen let's run:
6184332b049SChuanqi Xu
6194332b049SChuanqi Xu.. code-block:: text
6204332b049SChuanqi Xu
6214332b049SChuanqi Xu  $ clang++ -std=c++20 -g debugging-example.cpp -o debugging-example
6224332b049SChuanqi Xu  $ gdb ./debugging-example
623cc8237d9SAyushi Shukla  (gdb) # We've already set the breakpoint.
6244332b049SChuanqi Xu  (gdb) r
6254332b049SChuanqi Xu  Program received signal SIGTRAP, Trace/breakpoint trap.
6264332b049SChuanqi Xu  detail::chain_fn<0> () at debugging-example2.cpp:73
6274332b049SChuanqi Xu  73	  co_return 0;
6284332b049SChuanqi Xu  (gdb) # Executes the debugging scripts
6294332b049SChuanqi Xu  (gdb) source debugging-helper.py
6304332b049SChuanqi Xu  (gdb) # Print the asynchronous stack
6314332b049SChuanqi Xu  (gdb) async-bt
6324332b049SChuanqi Xu  #0 0x401c40 in detail::chain_fn<0>() ['frame_addr = 0x441860', 'promise_addr = 0x441870', 'continuation_addr = 0x441870'] at debugging-example.cpp:71
6334332b049SChuanqi Xu  #1 0x4022d0 in detail::chain_fn<1>() ['frame_addr = 0x441810', 'promise_addr = 0x441820', 'continuation_addr = 0x441820'] at debugging-example.cpp:66
6344332b049SChuanqi Xu  #2 0x403060 in detail::chain_fn<2>() ['frame_addr = 0x4417c0', 'promise_addr = 0x4417d0', 'continuation_addr = 0x4417d0'] at debugging-example.cpp:66
6354332b049SChuanqi Xu  #3 0x403df0 in detail::chain_fn<3>() ['frame_addr = 0x441770', 'promise_addr = 0x441780', 'continuation_addr = 0x441780'] at debugging-example.cpp:66
6364332b049SChuanqi Xu  #4 0x404b80 in detail::chain_fn<4>() ['frame_addr = 0x441720', 'promise_addr = 0x441730', 'continuation_addr = 0x441730'] at debugging-example.cpp:66
6374332b049SChuanqi Xu  #5 0x405910 in detail::chain_fn<5>() ['frame_addr = 0x4416d0', 'promise_addr = 0x4416e0', 'continuation_addr = 0x4416e0'] at debugging-example.cpp:66
6384332b049SChuanqi Xu  #6 0x4066a0 in detail::chain_fn<6>() ['frame_addr = 0x441680', 'promise_addr = 0x441690', 'continuation_addr = 0x441690'] at debugging-example.cpp:66
6394332b049SChuanqi Xu  #7 0x407430 in detail::chain_fn<7>() ['frame_addr = 0x441630', 'promise_addr = 0x441640', 'continuation_addr = 0x441640'] at debugging-example.cpp:66
6404332b049SChuanqi Xu  #8 0x4081c0 in detail::chain_fn<8>() ['frame_addr = 0x4415e0', 'promise_addr = 0x4415f0', 'continuation_addr = 0x4415f0'] at debugging-example.cpp:66
6414332b049SChuanqi Xu  #9 0x408f50 in detail::chain_fn<9>() ['frame_addr = 0x441590', 'promise_addr = 0x4415a0', 'continuation_addr = 0x4415a0'] at debugging-example.cpp:66
6424332b049SChuanqi Xu  #10 0x409ce0 in detail::chain_fn<10>() ['frame_addr = 0x441540', 'promise_addr = 0x441550', 'continuation_addr = 0x441550'] at debugging-example.cpp:66
6434332b049SChuanqi Xu  #11 0x40aa70 in detail::chain_fn<11>() ['frame_addr = 0x4414f0', 'promise_addr = 0x441500', 'continuation_addr = 0x441500'] at debugging-example.cpp:66
6444332b049SChuanqi Xu  #12 0x40b800 in detail::chain_fn<12>() ['frame_addr = 0x4414a0', 'promise_addr = 0x4414b0', 'continuation_addr = 0x4414b0'] at debugging-example.cpp:66
6454332b049SChuanqi Xu  #13 0x40c590 in detail::chain_fn<13>() ['frame_addr = 0x441450', 'promise_addr = 0x441460', 'continuation_addr = 0x441460'] at debugging-example.cpp:66
6464332b049SChuanqi Xu  #14 0x40d320 in detail::chain_fn<14>() ['frame_addr = 0x441400', 'promise_addr = 0x441410', 'continuation_addr = 0x441410'] at debugging-example.cpp:66
6474332b049SChuanqi Xu  #15 0x40e0b0 in detail::chain_fn<15>() ['frame_addr = 0x4413b0', 'promise_addr = 0x4413c0', 'continuation_addr = 0x4413c0'] at debugging-example.cpp:66
6484332b049SChuanqi Xu  #16 0x40ee40 in detail::chain_fn<16>() ['frame_addr = 0x441360', 'promise_addr = 0x441370', 'continuation_addr = 0x441370'] at debugging-example.cpp:66
6494332b049SChuanqi Xu  #17 0x40fbd0 in detail::chain_fn<17>() ['frame_addr = 0x441310', 'promise_addr = 0x441320', 'continuation_addr = 0x441320'] at debugging-example.cpp:66
6504332b049SChuanqi Xu  #18 0x410960 in detail::chain_fn<18>() ['frame_addr = 0x4412c0', 'promise_addr = 0x4412d0', 'continuation_addr = 0x4412d0'] at debugging-example.cpp:66
6514332b049SChuanqi Xu  #19 0x4116f0 in detail::chain_fn<19>() ['frame_addr = 0x441270', 'promise_addr = 0x441280', 'continuation_addr = 0x441280'] at debugging-example.cpp:66
6524332b049SChuanqi Xu  #20 0x412480 in detail::chain_fn<20>() ['frame_addr = 0x441220', 'promise_addr = 0x441230', 'continuation_addr = 0x441230'] at debugging-example.cpp:66
6534332b049SChuanqi Xu  #21 0x413210 in detail::chain_fn<21>() ['frame_addr = 0x4411d0', 'promise_addr = 0x4411e0', 'continuation_addr = 0x4411e0'] at debugging-example.cpp:66
6544332b049SChuanqi Xu  #22 0x413fa0 in detail::chain_fn<22>() ['frame_addr = 0x441180', 'promise_addr = 0x441190', 'continuation_addr = 0x441190'] at debugging-example.cpp:66
6554332b049SChuanqi Xu  #23 0x414d30 in detail::chain_fn<23>() ['frame_addr = 0x441130', 'promise_addr = 0x441140', 'continuation_addr = 0x441140'] at debugging-example.cpp:66
6564332b049SChuanqi Xu  #24 0x415ac0 in detail::chain_fn<24>() ['frame_addr = 0x4410e0', 'promise_addr = 0x4410f0', 'continuation_addr = 0x4410f0'] at debugging-example.cpp:66
6574332b049SChuanqi Xu  #25 0x416850 in detail::chain_fn<25>() ['frame_addr = 0x441090', 'promise_addr = 0x4410a0', 'continuation_addr = 0x4410a0'] at debugging-example.cpp:66
6584332b049SChuanqi Xu  #26 0x4175e0 in detail::chain_fn<26>() ['frame_addr = 0x441040', 'promise_addr = 0x441050', 'continuation_addr = 0x441050'] at debugging-example.cpp:66
6594332b049SChuanqi Xu  #27 0x418370 in detail::chain_fn<27>() ['frame_addr = 0x440ff0', 'promise_addr = 0x441000', 'continuation_addr = 0x441000'] at debugging-example.cpp:66
6604332b049SChuanqi Xu  #28 0x419100 in detail::chain_fn<28>() ['frame_addr = 0x440fa0', 'promise_addr = 0x440fb0', 'continuation_addr = 0x440fb0'] at debugging-example.cpp:66
6614332b049SChuanqi Xu  #29 0x419e90 in detail::chain_fn<29>() ['frame_addr = 0x440f50', 'promise_addr = 0x440f60', 'continuation_addr = 0x440f60'] at debugging-example.cpp:66
6624332b049SChuanqi Xu  #30 0x41ac20 in detail::chain_fn<30>() ['frame_addr = 0x440f00', 'promise_addr = 0x440f10', 'continuation_addr = 0x440f10'] at debugging-example.cpp:66
6634332b049SChuanqi Xu  #31 0x41b9b0 in chain() ['frame_addr = 0x440eb0', 'promise_addr = 0x440ec0', 'continuation_addr = 0x440ec0'] at debugging-example.cpp:77
6644332b049SChuanqi Xu
6654332b049SChuanqi XuNow we get the complete asynchronous stack!
6664332b049SChuanqi XuIt is also possible to print other asynchronous stack which doesn't live in the top of the stack.
6674332b049SChuanqi XuWe can make it by passing the address of the corresponding coroutine frame to ``async-bt`` command.
6684332b049SChuanqi Xu
6694332b049SChuanqi XuBy the debugging scripts, we can print any coroutine frame too as long as we know the address.
6704332b049SChuanqi XuFor example, we can print the coroutine frame for ``detail::chain_fn<18>()`` in the above example.
6714332b049SChuanqi XuFrom the log record, we know the address of the coroutine frame is ``0x4412c0`` in the run. Then we can:
6724332b049SChuanqi Xu
6734332b049SChuanqi Xu.. code-block:: text
6744332b049SChuanqi Xu
6754332b049SChuanqi Xu  (gdb) show-coro-frame 0x4412c0
6764332b049SChuanqi Xu  {
6774332b049SChuanqi Xu    __resume_fn = 0x410960 <detail::chain_fn<18>()>,
6784332b049SChuanqi Xu    __destroy_fn = 0x410d60 <detail::chain_fn<18>()>,
6794332b049SChuanqi Xu    __promise = {
6804332b049SChuanqi Xu      continuation = {
6814332b049SChuanqi Xu        _M_fr_ptr = 0x441270
6824332b049SChuanqi Xu      },
6834332b049SChuanqi Xu      result = 0
6844332b049SChuanqi Xu    },
6854332b049SChuanqi Xu    struct_Awaiter_0 = {
6864332b049SChuanqi Xu      struct_std____n4861__coroutine_handle_0 = {
6874332b049SChuanqi Xu        struct_std____n4861__coroutine_handle = {
6884332b049SChuanqi Xu          PointerType = 0x441310
6894332b049SChuanqi Xu        }
6904332b049SChuanqi Xu      }
6914332b049SChuanqi Xu    },
6924332b049SChuanqi Xu    struct_task_1 = {
6934332b049SChuanqi Xu      struct_std____n4861__coroutine_handle_0 = {
6944332b049SChuanqi Xu        struct_std____n4861__coroutine_handle = {
6954332b049SChuanqi Xu          PointerType = 0x0
6964332b049SChuanqi Xu        }
6974332b049SChuanqi Xu      }
6984332b049SChuanqi Xu    },
6994332b049SChuanqi Xu    struct_task__promise_type__FinalSuspend_2 = {
7004332b049SChuanqi Xu      struct_std____n4861__coroutine_handle = {
7014332b049SChuanqi Xu        PointerType = 0x0
7024332b049SChuanqi Xu      }
7034332b049SChuanqi Xu    },
7044332b049SChuanqi Xu    __coro_index = 1 '\001',
7054332b049SChuanqi Xu    struct_std____n4861__suspend_always_3 = {
7064332b049SChuanqi Xu      __int_8 = 0 '\000'
7074332b049SChuanqi Xu    }
7084332b049SChuanqi Xu
7094332b049SChuanqi Xu
7101934b3aeSChuanqi XuGet the living coroutines
7111934b3aeSChuanqi Xu=========================
7121934b3aeSChuanqi Xu
7131934b3aeSChuanqi XuAnother useful task when debugging coroutines is to enumerate the list of
7141934b3aeSChuanqi Xuliving coroutines, which is often done with threads.  While technically
7151934b3aeSChuanqi Xupossible, this task is not recommended in production code as it is costly at
7161934b3aeSChuanqi Xuruntime. One such solution is to store the list of currently running coroutines
7171934b3aeSChuanqi Xuin a collection:
7181934b3aeSChuanqi Xu
7191934b3aeSChuanqi Xu.. code-block:: c++
7201934b3aeSChuanqi Xu
7211934b3aeSChuanqi Xu  inline std::unordered_set<void*> lived_coroutines;
7221934b3aeSChuanqi Xu  // For all promise_type we want to record
7231934b3aeSChuanqi Xu  class promise_type {
7241934b3aeSChuanqi Xu  public:
7251934b3aeSChuanqi Xu      promise_type() {
7261934b3aeSChuanqi Xu          // Note to avoid data races
7271934b3aeSChuanqi Xu          lived_coroutines.insert(std::coroutine_handle<promise_type>::from_promise(*this).address());
7281934b3aeSChuanqi Xu      }
7291934b3aeSChuanqi Xu      ~promise_type() {
7301934b3aeSChuanqi Xu          // Note to avoid data races
7311934b3aeSChuanqi Xu          lived_coroutines.erase(std::coroutine_handle<promise_type>::from_promise(*this).address());
7321934b3aeSChuanqi Xu      }
7331934b3aeSChuanqi Xu  };
7341934b3aeSChuanqi Xu
7351934b3aeSChuanqi XuIn the above code snippet, we save the address of every lived coroutine in the
7361934b3aeSChuanqi Xu`lived_coroutines` `unordered_set`. As before, once we know the address of the
7371934b3aeSChuanqi Xucoroutine we can derive the function, `promise_type`, and other members of the
7381934b3aeSChuanqi Xuframe. Thus, we could print the list of lived coroutines from that collection.
7391934b3aeSChuanqi Xu
7401934b3aeSChuanqi XuPlease note that the above is expensive from a storage perspective, and requires
7411934b3aeSChuanqi Xusome level of locking (not pictured) on the collection to prevent data races.
742