1 ========================
2 Debugging C++ Coroutines
3 ========================
11 For performance and other architectural reasons, the C++ Coroutines feature in
12 the Clang compiler is implemented in two parts of the compiler. Semantic
13 analysis is performed in Clang, and Coroutine construction and optimization
14 takes place in the LLVM middle-end.
16 However, this design forces us to generate insufficient debugging information.
17 Typically, the compiler generates debug information in the Clang frontend, as
18 debug information is highly language specific. However, this is not possible
19 for Coroutine frames because the frames are constructed in the LLVM middle-end.
21 To mitigate this problem, the LLVM middle end attempts to generate some debug
22 information, which is unfortunately incomplete, since much of the language
23 specific information is missing in the middle end.
25 This document describes how to use this debug information to better debug
31 Due to the recent nature of C++20 Coroutines, the terminology used to describe
32 the concepts of Coroutines is not settled. This section defines a common,
33 understandable terminology to be used consistently throughout this document.
38 A `coroutine function` is any function that contains any of the Coroutine
39 Keywords `co_await`, `co_yield`, or `co_return`. A `coroutine type` is a
40 possible return type of one of these `coroutine functions`. `Task` and
41 `Generator` are commonly referred to coroutine types.
46 By technical definition, a `coroutine` is a suspendable function. However,
47 programmers typically use `coroutine` to refer to an individual instance.
52 std::vector<Task> Coros; // Task is a coroutine type.
53 for (int i = 0; i < 3; i++)
54 Coros.push_back(CoroTask()); // CoroTask is a coroutine function, which
55 // would return a coroutine type 'Task'.
57 In practice, we typically say "`Coros` contains 3 coroutines" in the above
58 example, though this is not strictly correct. More technically, this should
59 say "`Coros` contains 3 coroutine instances" or "Coros contains 3 coroutine
62 In this document, we follow the common practice of using `coroutine` to refer
63 to an individual `coroutine instance`, since the terms `coroutine instance` and
64 `coroutine object` aren't sufficiently defined in this case.
69 The C++ Standard uses `coroutine state` to describe the allocated storage. In
70 the compiler, we use `coroutine frame` to describe the generated data structure
71 that contains the necessary information.
73 The structure of coroutine frames
74 =================================
76 The structure of coroutine frames is defined as:
81 void (*__r)(); // function pointer to the `resume` function
82 void (*__d)(); // function pointer to the `destroy` function
83 promise_type; // the corresponding `promise_type`
84 ... // Any other needed information
87 In the debugger, the function's name is obtainable from the address of the
88 function. And the name of `resume` function is equal to the name of the
89 coroutine function. So the name of the coroutine is obtainable once the
90 address of the coroutine is known.
95 Every coroutine has a `promise_type`, which defines the behavior
96 for the corresponding coroutine. In other words, if two coroutines have the
97 same `promise_type`, they should behave in the same way.
98 To print a `promise_type` in a debugger when stopped at a breakpoint inside a
99 coroutine, printing the `promise_type` can be done by:
105 It is also possible to print the `promise_type` of a coroutine from the address
106 of the coroutine frame. For example, if the address of a coroutine frame is
107 0x416eb0, and the type of the `promise_type` is `task::promise_type`, printing
108 the `promise_type` can be done by:
112 print (task::promise_type)*(0x416eb0+0x10)
114 This is possible because the `promise_type` is guaranteed by the ABI to be at a
115 16 bit offset from the coroutine frame.
117 Note that there is also an ABI independent method:
121 print std::coroutine_handle<task::promise_type>::from_address((void*)0x416eb0).promise()
123 The functions `from_address(void*)` and `promise()` are often small enough to
124 be removed during optimization, so this method may not be possible.
126 Print coroutine frames
127 ======================
129 LLVM generates the debug information for the coroutine frame in the LLVM middle
130 end, which permits printing of the coroutine frame in the debugger. Much like
131 the `promise_type`, when stopped at a breakpoint inside a coroutine we can
132 print the coroutine frame by:
139 Just as printing the `promise_type` is possible from the coroutine address,
140 printing the details of the coroutine frame from an address is also possible:
144 (gdb) # Get the address of coroutine frame
145 (gdb) print/x *0x418eb0
147 (gdb) # Get the linkage name for the coroutine
149 0x4019e0 <_ZL9coro_taski>: 0xe5894855
150 (gdb) # Turn off the demangler temporarily to avoid the debugger misunderstanding the name.
151 (gdb) set demangle-style none
152 (gdb) # The coroutine frame type is 'linkage_name.coro_frame_ty'
153 (gdb) print ('_ZL9coro_taski.coro_frame_ty')*(0x418eb0)
154 $2 = {__resume_fn = 0x4019e0 <coro_task(int)>, __destroy_fn = 0x402000 <coro_task(int)>, __promise = {...}, ...}
156 The above is possible because:
158 (1) The name of the debug type of the coroutine frame is the `linkage_name`,
159 plus the `.coro_frame_ty` suffix because each coroutine function shares the
162 (2) The coroutine function name is accessible from the address of the coroutine
165 The above commands can be simplified by placing them in debug scripts.
167 Examples to print coroutine frames
168 ----------------------------------
170 The print examples below use the following definition:
178 struct promise_type {
179 task get_return_object() { return std::coroutine_handle<promise_type>::from_promise(*this); }
180 std::suspend_always initial_suspend() { return {}; }
181 std::suspend_always final_suspend() noexcept { return {}; }
182 void return_void() noexcept {}
183 void unhandled_exception() noexcept {}
188 void resume() noexcept {
192 task(std::coroutine_handle<promise_type> hdl) : handle(hdl) {}
198 std::coroutine_handle<> handle;
201 class await_counter : public std::suspend_always {
203 template<class PromiseType>
204 void await_suspend(std::coroutine_handle<PromiseType> handle) noexcept {
205 handle.promise().count++;
209 static task coro_task(int v) {
211 co_await await_counter{};
213 std::cout << a << "\n";
215 std::cout << a << "\n";
217 std::cout << a << "\n";
218 co_await await_counter{};
220 std::cout << a << "\n";
222 std::cout << a << "\n";
226 task t = coro_task(43);
233 In debug mode (`O0` + `g`), the printing result would be:
237 {__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'},
238 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'}}
240 In the above, the values of `v` and `a` are clearly expressed, as are the
241 temporary values for `await_counter` (`class_await_counter_1` and
242 `class_await_counter_2`) and `std::suspend_always` (
243 `struct_std__suspend_always_0` and `struct_std__suspend_always_3`). The index
244 of the current suspension point of the coroutine is emitted as `__coro_index`.
245 In the above example, the `__coro_index` value of `1` means the coroutine
246 stopped at the second suspend point (Note that `__coro_index` is zero indexed)
247 which is the first `co_await await_counter{};` in `coro_task`. Note that the
248 first initial suspend point is the compiler generated
249 `co_await promise_type::initial_suspend()`.
251 However, when optimizations are enabled, the printed result changes drastically:
255 {__resume_fn = 0x401280 <coro_task(int)>, __destroy_fn = 0x401390 <coro_task(int)>, __promise = {count = 1}, __int_32_0 = 43, __coro_index = 1 '\001'}
257 Unused values are optimized out, as well as the name of the local variable `a`.
258 The only information remained is the value of a 32 bit integer. In this simple
259 case, it seems to be pretty clear that `__int_32_0` represents `a`. However, it
262 An important note with optimization is that the value of a variable may not
263 properly express the intended value in the source code. For example:
267 static task coro_task(int v) {
269 co_await await_counter{};
270 a++; // __int_32_0 is 43 here
271 std::cout << a << "\n";
272 a++; // __int_32_0 is still 43 here
273 std::cout << a << "\n";
274 a++; // __int_32_0 is still 43 here!
275 std::cout << a << "\n";
276 co_await await_counter{};
277 a++; // __int_32_0 is still 43 here!!
278 std::cout << a << "\n";
279 a++; // Why is __int_32_0 still 43 here?
280 std::cout << a << "\n";
283 When debugging step-by-step, the value of `__int_32_0` seemingly does not
284 change, despite being frequently incremented, and instead is always `43`.
285 While this might be surprising, this is a result of the optimizer recognizing
286 that it can eliminate most of the load/store operations. The above code gets
287 optimized to the equivalent of:
291 static task coro_task(int v) {
292 store v to __int_32_0 in the frame
293 co_await await_counter{};
295 std::cout << a+1 << "\n";
296 std::cout << a+2 << "\n";
297 std::cout << a+3 << "\n";
298 co_await await_counter{};
300 std::cout << a+4 << "\n";
301 std::cout << a+5 << "\n";
304 It should now be obvious why the value of `__int_32_0` remains unchanged
305 throughout the function. It is important to recognize that `__int_32_0`
306 does not directly correspond to `a`, but is instead a variable generated
307 to assist the compiler in code generation. The variables in an optimized
308 coroutine frame should not be thought of as directly representing the
309 variables in the C++ source.
311 Get the suspended points
312 ========================
314 An important requirement for debugging coroutines is to understand suspended
315 points, which are where the coroutine is currently suspended and awaiting.
317 For simple cases like the above, inspecting the value of the `__coro_index`
318 variable in the coroutine frame works well.
320 However, it is not quite so simple in really complex situations. In these
321 cases, it is necessary to use the coroutine libraries to insert the
328 // For all the promise_type we want:
331 + unsigned line_number = 0xffffffff;
334 #include <source_location>
336 // For all the awaiter types we need:
339 template <typename Promise>
340 void await_suspend(std::coroutine_handle<Promise> handle,
341 std::source_location sl = std::source_location::current()) {
343 handle.promise().line_number = sl.line();
347 In this case, we use `std::source_location` to store the line number of the
348 await inside the `promise_type`. Since we can locate the coroutine function
349 from the address of the coroutine, we can identify suspended points this way
352 The downside here is that this comes at the price of additional runtime cost.
353 This is consistent with the C++ philosophy of "Pay for what you use".
355 Get the asynchronous stack
356 ==========================
358 Another important requirement to debug a coroutine is to print the asynchronous
359 stack to identify the asynchronous caller of the coroutine. As many
360 implementations of coroutine types store `std::coroutine_handle<> continuation`
361 in the promise type, identifying the caller should be trivial. The
362 `continuation` is typically the awaiting coroutine for the current coroutine.
363 That is, the asynchronous parent.
365 Since the `promise_type` is obtainable from the address of a coroutine and
366 contains the corresponding continuation (which itself is a coroutine with a
367 `promise_type`), it should be trivial to print the entire asynchronous stack.
369 This logic should be quite easily captured in a debugger script.
371 Examples to print asynchronous stack
372 ------------------------------------
374 Here is an example to print the asynchronous stack for the normal task implementation.
378 // debugging-example.cpp
384 struct promise_type {
385 task get_return_object();
386 std::suspend_always initial_suspend() { return {}; }
388 void unhandled_exception() noexcept {}
390 struct FinalSuspend {
391 std::coroutine_handle<> continuation;
392 auto await_ready() noexcept { return false; }
393 auto await_suspend(std::coroutine_handle<> handle) noexcept {
396 void await_resume() noexcept {}
398 FinalSuspend final_suspend() noexcept { return {continuation}; }
400 void return_value(int res) { result = res; }
402 std::coroutine_handle<> continuation = std::noop_coroutine();
406 task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
412 auto operator co_await() {
414 std::coroutine_handle<promise_type> handle;
415 auto await_ready() { return false; }
416 auto await_suspend(std::coroutine_handle<> continuation) {
417 handle.promise().continuation = continuation;
421 int ret = handle.promise().result;
426 return Awaiter{std::exchange(handle, nullptr)};
431 return handle.promise().result;
435 std::coroutine_handle<promise_type> handle;
438 task task::promise_type::get_return_object() {
439 return std::coroutine_handle<promise_type>::from_promise(*this);
445 co_return N + co_await chain_fn<N - 1>();
450 // This is the default breakpoint.
451 __builtin_debugtrap();
454 } // namespace detail
457 co_return co_await detail::chain_fn<30>();
461 std::cout << chain().syncStart() << "\n";
465 In the example, the ``task`` coroutine holds a ``continuation`` field,
466 which would be resumed once the ``task`` completes.
467 In another word, the ``continuation`` is the asynchronous caller for the ``task``.
468 Just like the normal function returns to its caller when the function completes.
470 So we can use the ``continuation`` field to construct the asynchronous stack:
472 .. code-block:: python
474 # debugging-helper.py
476 from gdb.FrameDecorator import FrameDecorator
478 class SymValueWrapper():
479 def __init__(self, symbol, value):
484 return str(self.sym) + " = " + str(self.val)
486 def get_long_pointer_size():
487 return gdb.lookup_type('long').pointer().sizeof
489 def cast_addr2long_pointer(addr):
490 return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
492 def dereference(addr):
493 return long(cast_addr2long_pointer(addr).dereference())
495 class CoroutineFrame(object):
496 def __init__(self, task_addr):
497 self.frame_addr = task_addr
498 self.resume_addr = task_addr
499 self.destroy_addr = task_addr + get_long_pointer_size()
500 self.promise_addr = task_addr + get_long_pointer_size() * 2
501 # In the example, the continuation is the first field member of the promise_type.
502 # So they have the same addresses.
503 # If we want to generalize the scripts to other coroutine types, we need to be sure
504 # the continuation field is the first memeber of promise_type.
505 self.continuation_addr = self.promise_addr
507 def next_task_addr(self):
508 return dereference(self.continuation_addr)
510 class CoroutineFrameDecorator(FrameDecorator):
511 def __init__(self, coro_frame):
512 super(CoroutineFrameDecorator, self).__init__(None)
513 self.coro_frame = coro_frame
514 self.resume_func = dereference(self.coro_frame.resume_addr)
515 self.resume_func_block = gdb.block_for_pc(self.resume_func)
516 if self.resume_func_block == None:
517 raise Exception('Not stackless coroutine.')
518 self.line_info = gdb.find_pc_line(self.resume_func)
521 return self.resume_func
524 return self.line_info.symtab.filename
526 def frame_args(self):
527 return [SymValueWrapper("frame_addr", cast_addr2long_pointer(self.coro_frame.frame_addr)),
528 SymValueWrapper("promise_addr", cast_addr2long_pointer(self.coro_frame.promise_addr)),
529 SymValueWrapper("continuation_addr", cast_addr2long_pointer(self.coro_frame.continuation_addr))
533 return self.resume_func_block.function.print_name
536 return self.line_info.line
538 class StripDecorator(FrameDecorator):
539 def __init__(self, frame):
540 super(StripDecorator, self).__init__(frame)
543 self.function_name = f
545 def __str__(self, shift = 2):
546 addr = "" if self.address() == None else '%#x' % self.address() + " in "
547 location = "" if self.filename() == None else " at " + self.filename() + ":" + str(self.line())
548 return addr + self.function() + " " + str([str(args) for args in self.frame_args()]) + location
550 class CoroutineFilter:
551 def create_coroutine_frames(self, task_addr):
553 while task_addr != 0:
554 coro_frame = CoroutineFrame(task_addr)
555 frames.append(CoroutineFrameDecorator(coro_frame))
556 task_addr = coro_frame.next_task_addr()
559 class AsyncStack(gdb.Command):
561 super(AsyncStack, self).__init__("async-bt", gdb.COMMAND_USER)
563 def invoke(self, arg, from_tty):
564 coroutine_filter = CoroutineFilter()
565 argv = gdb.string_to_argv(arg)
568 task = gdb.parse_and_eval('__coro_frame')
569 task = int(str(task.address), 16)
571 print ("Can't find __coro_frame in current context.\n" +
572 "Please use `async-bt` in stackless coroutine context.")
575 print("usage: async-bt <pointer to task>")
578 task = int(argv[0], 16)
580 frames = coroutine_filter.create_coroutine_frames(task)
583 print '#'+ str(i), str(StripDecorator(f))
589 class ShowCoroFrame(gdb.Command):
591 super(ShowCoroFrame, self).__init__("show-coro-frame", gdb.COMMAND_USER)
593 def invoke(self, arg, from_tty):
594 argv = gdb.string_to_argv(arg)
596 print("usage: show-coro-frame <address of coroutine frame>")
599 addr = int(argv[0], 16)
600 block = gdb.block_for_pc(long(cast_addr2long_pointer(addr).dereference()))
602 print "block " + str(addr) + " is none."
605 # Disable demangling since gdb will treat names starting with `_Z`(The marker for Itanium ABI) specially.
606 gdb.execute("set demangle-style none")
608 coro_frame_type = gdb.lookup_type(block.function.linkage_name + ".coro_frame_ty")
609 coro_frame_ptr_type = coro_frame_type.pointer()
610 coro_frame = gdb.Value(addr).cast(coro_frame_ptr_type).dereference()
612 gdb.execute("set demangle-style auto")
613 gdb.write(coro_frame.format_string(pretty_structs = True))
621 $ clang++ -std=c++20 -g debugging-example.cpp -o debugging-example
622 $ gdb ./debugging-example
623 (gdb) # We've already set the breakpoint.
625 Program received signal SIGTRAP, Trace/breakpoint trap.
626 detail::chain_fn<0> () at debugging-example2.cpp:73
628 (gdb) # Executes the debugging scripts
629 (gdb) source debugging-helper.py
630 (gdb) # Print the asynchronous stack
632 #0 0x401c40 in detail::chain_fn<0>() ['frame_addr = 0x441860', 'promise_addr = 0x441870', 'continuation_addr = 0x441870'] at debugging-example.cpp:71
633 #1 0x4022d0 in detail::chain_fn<1>() ['frame_addr = 0x441810', 'promise_addr = 0x441820', 'continuation_addr = 0x441820'] at debugging-example.cpp:66
634 #2 0x403060 in detail::chain_fn<2>() ['frame_addr = 0x4417c0', 'promise_addr = 0x4417d0', 'continuation_addr = 0x4417d0'] at debugging-example.cpp:66
635 #3 0x403df0 in detail::chain_fn<3>() ['frame_addr = 0x441770', 'promise_addr = 0x441780', 'continuation_addr = 0x441780'] at debugging-example.cpp:66
636 #4 0x404b80 in detail::chain_fn<4>() ['frame_addr = 0x441720', 'promise_addr = 0x441730', 'continuation_addr = 0x441730'] at debugging-example.cpp:66
637 #5 0x405910 in detail::chain_fn<5>() ['frame_addr = 0x4416d0', 'promise_addr = 0x4416e0', 'continuation_addr = 0x4416e0'] at debugging-example.cpp:66
638 #6 0x4066a0 in detail::chain_fn<6>() ['frame_addr = 0x441680', 'promise_addr = 0x441690', 'continuation_addr = 0x441690'] at debugging-example.cpp:66
639 #7 0x407430 in detail::chain_fn<7>() ['frame_addr = 0x441630', 'promise_addr = 0x441640', 'continuation_addr = 0x441640'] at debugging-example.cpp:66
640 #8 0x4081c0 in detail::chain_fn<8>() ['frame_addr = 0x4415e0', 'promise_addr = 0x4415f0', 'continuation_addr = 0x4415f0'] at debugging-example.cpp:66
641 #9 0x408f50 in detail::chain_fn<9>() ['frame_addr = 0x441590', 'promise_addr = 0x4415a0', 'continuation_addr = 0x4415a0'] at debugging-example.cpp:66
642 #10 0x409ce0 in detail::chain_fn<10>() ['frame_addr = 0x441540', 'promise_addr = 0x441550', 'continuation_addr = 0x441550'] at debugging-example.cpp:66
643 #11 0x40aa70 in detail::chain_fn<11>() ['frame_addr = 0x4414f0', 'promise_addr = 0x441500', 'continuation_addr = 0x441500'] at debugging-example.cpp:66
644 #12 0x40b800 in detail::chain_fn<12>() ['frame_addr = 0x4414a0', 'promise_addr = 0x4414b0', 'continuation_addr = 0x4414b0'] at debugging-example.cpp:66
645 #13 0x40c590 in detail::chain_fn<13>() ['frame_addr = 0x441450', 'promise_addr = 0x441460', 'continuation_addr = 0x441460'] at debugging-example.cpp:66
646 #14 0x40d320 in detail::chain_fn<14>() ['frame_addr = 0x441400', 'promise_addr = 0x441410', 'continuation_addr = 0x441410'] at debugging-example.cpp:66
647 #15 0x40e0b0 in detail::chain_fn<15>() ['frame_addr = 0x4413b0', 'promise_addr = 0x4413c0', 'continuation_addr = 0x4413c0'] at debugging-example.cpp:66
648 #16 0x40ee40 in detail::chain_fn<16>() ['frame_addr = 0x441360', 'promise_addr = 0x441370', 'continuation_addr = 0x441370'] at debugging-example.cpp:66
649 #17 0x40fbd0 in detail::chain_fn<17>() ['frame_addr = 0x441310', 'promise_addr = 0x441320', 'continuation_addr = 0x441320'] at debugging-example.cpp:66
650 #18 0x410960 in detail::chain_fn<18>() ['frame_addr = 0x4412c0', 'promise_addr = 0x4412d0', 'continuation_addr = 0x4412d0'] at debugging-example.cpp:66
651 #19 0x4116f0 in detail::chain_fn<19>() ['frame_addr = 0x441270', 'promise_addr = 0x441280', 'continuation_addr = 0x441280'] at debugging-example.cpp:66
652 #20 0x412480 in detail::chain_fn<20>() ['frame_addr = 0x441220', 'promise_addr = 0x441230', 'continuation_addr = 0x441230'] at debugging-example.cpp:66
653 #21 0x413210 in detail::chain_fn<21>() ['frame_addr = 0x4411d0', 'promise_addr = 0x4411e0', 'continuation_addr = 0x4411e0'] at debugging-example.cpp:66
654 #22 0x413fa0 in detail::chain_fn<22>() ['frame_addr = 0x441180', 'promise_addr = 0x441190', 'continuation_addr = 0x441190'] at debugging-example.cpp:66
655 #23 0x414d30 in detail::chain_fn<23>() ['frame_addr = 0x441130', 'promise_addr = 0x441140', 'continuation_addr = 0x441140'] at debugging-example.cpp:66
656 #24 0x415ac0 in detail::chain_fn<24>() ['frame_addr = 0x4410e0', 'promise_addr = 0x4410f0', 'continuation_addr = 0x4410f0'] at debugging-example.cpp:66
657 #25 0x416850 in detail::chain_fn<25>() ['frame_addr = 0x441090', 'promise_addr = 0x4410a0', 'continuation_addr = 0x4410a0'] at debugging-example.cpp:66
658 #26 0x4175e0 in detail::chain_fn<26>() ['frame_addr = 0x441040', 'promise_addr = 0x441050', 'continuation_addr = 0x441050'] at debugging-example.cpp:66
659 #27 0x418370 in detail::chain_fn<27>() ['frame_addr = 0x440ff0', 'promise_addr = 0x441000', 'continuation_addr = 0x441000'] at debugging-example.cpp:66
660 #28 0x419100 in detail::chain_fn<28>() ['frame_addr = 0x440fa0', 'promise_addr = 0x440fb0', 'continuation_addr = 0x440fb0'] at debugging-example.cpp:66
661 #29 0x419e90 in detail::chain_fn<29>() ['frame_addr = 0x440f50', 'promise_addr = 0x440f60', 'continuation_addr = 0x440f60'] at debugging-example.cpp:66
662 #30 0x41ac20 in detail::chain_fn<30>() ['frame_addr = 0x440f00', 'promise_addr = 0x440f10', 'continuation_addr = 0x440f10'] at debugging-example.cpp:66
663 #31 0x41b9b0 in chain() ['frame_addr = 0x440eb0', 'promise_addr = 0x440ec0', 'continuation_addr = 0x440ec0'] at debugging-example.cpp:77
665 Now we get the complete asynchronous stack!
666 It is also possible to print other asynchronous stack which doesn't live in the top of the stack.
667 We can make it by passing the address of the corresponding coroutine frame to ``async-bt`` command.
669 By the debugging scripts, we can print any coroutine frame too as long as we know the address.
670 For example, we can print the coroutine frame for ``detail::chain_fn<18>()`` in the above example.
671 From the log record, we know the address of the coroutine frame is ``0x4412c0`` in the run. Then we can:
675 (gdb) show-coro-frame 0x4412c0
677 __resume_fn = 0x410960 <detail::chain_fn<18>()>,
678 __destroy_fn = 0x410d60 <detail::chain_fn<18>()>,
686 struct_std____n4861__coroutine_handle_0 = {
687 struct_std____n4861__coroutine_handle = {
688 PointerType = 0x441310
693 struct_std____n4861__coroutine_handle_0 = {
694 struct_std____n4861__coroutine_handle = {
699 struct_task__promise_type__FinalSuspend_2 = {
700 struct_std____n4861__coroutine_handle = {
704 __coro_index = 1 '\001',
705 struct_std____n4861__suspend_always_3 = {
710 Get the living coroutines
711 =========================
713 Another useful task when debugging coroutines is to enumerate the list of
714 living coroutines, which is often done with threads. While technically
715 possible, this task is not recommended in production code as it is costly at
716 runtime. One such solution is to store the list of currently running coroutines
721 inline std::unordered_set<void*> lived_coroutines;
722 // For all promise_type we want to record
726 // Note to avoid data races
727 lived_coroutines.insert(std::coroutine_handle<promise_type>::from_promise(*this).address());
730 // Note to avoid data races
731 lived_coroutines.erase(std::coroutine_handle<promise_type>::from_promise(*this).address());
735 In the above code snippet, we save the address of every lived coroutine in the
736 `lived_coroutines` `unordered_set`. As before, once we know the address of the
737 coroutine we can derive the function, `promise_type`, and other members of the
738 frame. Thus, we could print the list of lived coroutines from that collection.
740 Please note that the above is expensive from a storage perspective, and requires
741 some level of locking (not pictured) on the collection to prevent data races.