1======================== 2Debugging C++ Coroutines 3======================== 4 5.. contents:: 6 :local: 7 8Introduction 9============ 10 11For performance and other architectural reasons, the C++ Coroutines feature in 12the Clang compiler is implemented in two parts of the compiler. Semantic 13analysis is performed in Clang, and Coroutine construction and optimization 14takes place in the LLVM middle-end. 15 16However, this design forces us to generate insufficient debugging information. 17Typically, the compiler generates debug information in the Clang frontend, as 18debug information is highly language specific. However, this is not possible 19for Coroutine frames because the frames are constructed in the LLVM middle-end. 20 21To mitigate this problem, the LLVM middle end attempts to generate some debug 22information, which is unfortunately incomplete, since much of the language 23specific information is missing in the middle end. 24 25This document describes how to use this debug information to better debug 26coroutines. 27 28Terminology 29=========== 30 31Due to the recent nature of C++20 Coroutines, the terminology used to describe 32the concepts of Coroutines is not settled. This section defines a common, 33understandable terminology to be used consistently throughout this document. 34 35coroutine type 36-------------- 37 38A `coroutine function` is any function that contains any of the Coroutine 39Keywords `co_await`, `co_yield`, or `co_return`. A `coroutine type` is a 40possible return type of one of these `coroutine functions`. `Task` and 41`Generator` are commonly referred to coroutine types. 42 43coroutine 44--------- 45 46By technical definition, a `coroutine` is a suspendable function. However, 47programmers typically use `coroutine` to refer to an individual instance. 48For example: 49 50.. code-block:: c++ 51 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'. 56 57In practice, we typically say "`Coros` contains 3 coroutines" in the above 58example, though this is not strictly correct. More technically, this should 59say "`Coros` contains 3 coroutine instances" or "Coros contains 3 coroutine 60objects." 61 62In this document, we follow the common practice of using `coroutine` to refer 63to an individual `coroutine instance`, since the terms `coroutine instance` and 64`coroutine object` aren't sufficiently defined in this case. 65 66coroutine frame 67--------------- 68 69The C++ Standard uses `coroutine state` to describe the allocated storage. In 70the compiler, we use `coroutine frame` to describe the generated data structure 71that contains the necessary information. 72 73The structure of coroutine frames 74================================= 75 76The structure of coroutine frames is defined as: 77 78.. code-block:: c++ 79 80 struct { 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 85 } 86 87In the debugger, the function's name is obtainable from the address of the 88function. And the name of `resume` function is equal to the name of the 89coroutine function. So the name of the coroutine is obtainable once the 90address of the coroutine is known. 91 92Print promise_type 93================== 94 95Every coroutine has a `promise_type`, which defines the behavior 96for the corresponding coroutine. In other words, if two coroutines have the 97same `promise_type`, they should behave in the same way. 98To print a `promise_type` in a debugger when stopped at a breakpoint inside a 99coroutine, printing the `promise_type` can be done by: 100 101.. parsed-literal:: 102 103 print __promise 104 105It is also possible to print the `promise_type` of a coroutine from the address 106of the coroutine frame. For example, if the address of a coroutine frame is 1070x416eb0, and the type of the `promise_type` is `task::promise_type`, printing 108the `promise_type` can be done by: 109 110.. parsed-literal:: 111 112 print (task::promise_type)*(0x416eb0+0x10) 113 114This is possible because the `promise_type` is guaranteed by the ABI to be at a 11516 bit offset from the coroutine frame. 116 117Note that there is also an ABI independent method: 118 119.. parsed-literal:: 120 121 print std::coroutine_handle<task::promise_type>::from_address((void*)0x416eb0).promise() 122 123The functions `from_address(void*)` and `promise()` are often small enough to 124be removed during optimization, so this method may not be possible. 125 126Print coroutine frames 127====================== 128 129LLVM generates the debug information for the coroutine frame in the LLVM middle 130end, which permits printing of the coroutine frame in the debugger. Much like 131the `promise_type`, when stopped at a breakpoint inside a coroutine we can 132print the coroutine frame by: 133 134.. parsed-literal:: 135 136 print __coro_frame 137 138 139Just as printing the `promise_type` is possible from the coroutine address, 140printing the details of the coroutine frame from an address is also possible: 141 142:: 143 144 (gdb) # Get the address of coroutine frame 145 (gdb) print/x *0x418eb0 146 $1 = 0x4019e0 147 (gdb) # Get the linkage name for the coroutine 148 (gdb) x 0x4019e0 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 = {...}, ...} 155 156The above is possible because: 157 158(1) The name of the debug type of the coroutine frame is the `linkage_name`, 159plus the `.coro_frame_ty` suffix because each coroutine function shares the 160same coroutine type. 161 162(2) The coroutine function name is accessible from the address of the coroutine 163frame. 164 165The above commands can be simplified by placing them in debug scripts. 166 167Examples to print coroutine frames 168---------------------------------- 169 170The print examples below use the following definition: 171 172.. code-block:: c++ 173 174 #include <coroutine> 175 #include <iostream> 176 177 struct task{ 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 {} 184 185 int count = 0; 186 }; 187 188 void resume() noexcept { 189 handle.resume(); 190 } 191 192 task(std::coroutine_handle<promise_type> hdl) : handle(hdl) {} 193 ~task() { 194 if (handle) 195 handle.destroy(); 196 } 197 198 std::coroutine_handle<> handle; 199 }; 200 201 class await_counter : public std::suspend_always { 202 public: 203 template<class PromiseType> 204 void await_suspend(std::coroutine_handle<PromiseType> handle) noexcept { 205 handle.promise().count++; 206 } 207 }; 208 209 static task coro_task(int v) { 210 int a = v; 211 co_await await_counter{}; 212 a++; 213 std::cout << a << "\n"; 214 a++; 215 std::cout << a << "\n"; 216 a++; 217 std::cout << a << "\n"; 218 co_await await_counter{}; 219 a++; 220 std::cout << a << "\n"; 221 a++; 222 std::cout << a << "\n"; 223 } 224 225 int main() { 226 task t = coro_task(43); 227 t.resume(); 228 t.resume(); 229 t.resume(); 230 return 0; 231 } 232 233In debug mode (`O0` + `g`), the printing result would be: 234 235.. parsed-literal:: 236 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'}} 239 240In the above, the values of `v` and `a` are clearly expressed, as are the 241temporary 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 244of the current suspension point of the coroutine is emitted as `__coro_index`. 245In the above example, the `__coro_index` value of `1` means the coroutine 246stopped at the second suspend point (Note that `__coro_index` is zero indexed) 247which is the first `co_await await_counter{};` in `coro_task`. Note that the 248first initial suspend point is the compiler generated 249`co_await promise_type::initial_suspend()`. 250 251However, when optimizations are enabled, the printed result changes drastically: 252 253.. parsed-literal:: 254 255 {__resume_fn = 0x401280 <coro_task(int)>, __destroy_fn = 0x401390 <coro_task(int)>, __promise = {count = 1}, __int_32_0 = 43, __coro_index = 1 '\001'} 256 257Unused values are optimized out, as well as the name of the local variable `a`. 258The only information remained is the value of a 32 bit integer. In this simple 259case, it seems to be pretty clear that `__int_32_0` represents `a`. However, it 260is not true. 261 262An important note with optimization is that the value of a variable may not 263properly express the intended value in the source code. For example: 264 265.. code-block:: c++ 266 267 static task coro_task(int v) { 268 int a = 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"; 281 } 282 283When debugging step-by-step, the value of `__int_32_0` seemingly does not 284change, despite being frequently incremented, and instead is always `43`. 285While this might be surprising, this is a result of the optimizer recognizing 286that it can eliminate most of the load/store operations. The above code gets 287optimized to the equivalent of: 288 289.. code-block:: c++ 290 291 static task coro_task(int v) { 292 store v to __int_32_0 in the frame 293 co_await await_counter{}; 294 a = load __int_32_0 295 std::cout << a+1 << "\n"; 296 std::cout << a+2 << "\n"; 297 std::cout << a+3 << "\n"; 298 co_await await_counter{}; 299 a = load __int_32_0 300 std::cout << a+4 << "\n"; 301 std::cout << a+5 << "\n"; 302 } 303 304It should now be obvious why the value of `__int_32_0` remains unchanged 305throughout the function. It is important to recognize that `__int_32_0` 306does not directly correspond to `a`, but is instead a variable generated 307to assist the compiler in code generation. The variables in an optimized 308coroutine frame should not be thought of as directly representing the 309variables in the C++ source. 310 311Get the suspended points 312======================== 313 314An important requirement for debugging coroutines is to understand suspended 315points, which are where the coroutine is currently suspended and awaiting. 316 317For simple cases like the above, inspecting the value of the `__coro_index` 318variable in the coroutine frame works well. 319 320However, it is not quite so simple in really complex situations. In these 321cases, it is necessary to use the coroutine libraries to insert the 322line-number. 323 324For example: 325 326.. code-block:: c++ 327 328 // For all the promise_type we want: 329 class promise_type { 330 ... 331 + unsigned line_number = 0xffffffff; 332 }; 333 334 #include <source_location> 335 336 // For all the awaiter types we need: 337 class awaiter { 338 ... 339 template <typename Promise> 340 void await_suspend(std::coroutine_handle<Promise> handle, 341 std::source_location sl = std::source_location::current()) { 342 ... 343 handle.promise().line_number = sl.line(); 344 } 345 }; 346 347In this case, we use `std::source_location` to store the line number of the 348await inside the `promise_type`. Since we can locate the coroutine function 349from the address of the coroutine, we can identify suspended points this way 350as well. 351 352The downside here is that this comes at the price of additional runtime cost. 353This is consistent with the C++ philosophy of "Pay for what you use". 354 355Get the asynchronous stack 356========================== 357 358Another important requirement to debug a coroutine is to print the asynchronous 359stack to identify the asynchronous caller of the coroutine. As many 360implementations of coroutine types store `std::coroutine_handle<> continuation` 361in the promise type, identifying the caller should be trivial. The 362`continuation` is typically the awaiting coroutine for the current coroutine. 363That is, the asynchronous parent. 364 365Since the `promise_type` is obtainable from the address of a coroutine and 366contains the corresponding continuation (which itself is a coroutine with a 367`promise_type`), it should be trivial to print the entire asynchronous stack. 368 369This logic should be quite easily captured in a debugger script. 370 371Examples to print asynchronous stack 372------------------------------------ 373 374Here is an example to print the asynchronous stack for the normal task implementation. 375 376.. code-block:: c++ 377 378 // debugging-example.cpp 379 #include <coroutine> 380 #include <iostream> 381 #include <utility> 382 383 struct task { 384 struct promise_type { 385 task get_return_object(); 386 std::suspend_always initial_suspend() { return {}; } 387 388 void unhandled_exception() noexcept {} 389 390 struct FinalSuspend { 391 std::coroutine_handle<> continuation; 392 auto await_ready() noexcept { return false; } 393 auto await_suspend(std::coroutine_handle<> handle) noexcept { 394 return continuation; 395 } 396 void await_resume() noexcept {} 397 }; 398 FinalSuspend final_suspend() noexcept { return {continuation}; } 399 400 void return_value(int res) { result = res; } 401 402 std::coroutine_handle<> continuation = std::noop_coroutine(); 403 int result = 0; 404 }; 405 406 task(std::coroutine_handle<promise_type> handle) : handle(handle) {} 407 ~task() { 408 if (handle) 409 handle.destroy(); 410 } 411 412 auto operator co_await() { 413 struct Awaiter { 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; 418 return handle; 419 } 420 int await_resume() { 421 int ret = handle.promise().result; 422 handle.destroy(); 423 return ret; 424 } 425 }; 426 return Awaiter{std::exchange(handle, nullptr)}; 427 } 428 429 int syncStart() { 430 handle.resume(); 431 return handle.promise().result; 432 } 433 434 private: 435 std::coroutine_handle<promise_type> handle; 436 }; 437 438 task task::promise_type::get_return_object() { 439 return std::coroutine_handle<promise_type>::from_promise(*this); 440 } 441 442 namespace detail { 443 template <int N> 444 task chain_fn() { 445 co_return N + co_await chain_fn<N - 1>(); 446 } 447 448 template <> 449 task chain_fn<0>() { 450 // This is the default breakpoint. 451 __builtin_debugtrap(); 452 co_return 0; 453 } 454 } // namespace detail 455 456 task chain() { 457 co_return co_await detail::chain_fn<30>(); 458 } 459 460 int main() { 461 std::cout << chain().syncStart() << "\n"; 462 return 0; 463 } 464 465In the example, the ``task`` coroutine holds a ``continuation`` field, 466which would be resumed once the ``task`` completes. 467In another word, the ``continuation`` is the asynchronous caller for the ``task``. 468Just like the normal function returns to its caller when the function completes. 469 470So we can use the ``continuation`` field to construct the asynchronous stack: 471 472.. code-block:: python 473 474 # debugging-helper.py 475 import gdb 476 from gdb.FrameDecorator import FrameDecorator 477 478 class SymValueWrapper(): 479 def __init__(self, symbol, value): 480 self.sym = symbol 481 self.val = value 482 483 def __str__(self): 484 return str(self.sym) + " = " + str(self.val) 485 486 def get_long_pointer_size(): 487 return gdb.lookup_type('long').pointer().sizeof 488 489 def cast_addr2long_pointer(addr): 490 return gdb.Value(addr).cast(gdb.lookup_type('long').pointer()) 491 492 def dereference(addr): 493 return long(cast_addr2long_pointer(addr).dereference()) 494 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 506 507 def next_task_addr(self): 508 return dereference(self.continuation_addr) 509 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) 519 520 def address(self): 521 return self.resume_func 522 523 def filename(self): 524 return self.line_info.symtab.filename 525 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)) 530 ] 531 532 def function(self): 533 return self.resume_func_block.function.print_name 534 535 def line(self): 536 return self.line_info.line 537 538 class StripDecorator(FrameDecorator): 539 def __init__(self, frame): 540 super(StripDecorator, self).__init__(frame) 541 self.frame = frame 542 f = frame.function() 543 self.function_name = f 544 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 549 550 class CoroutineFilter: 551 def create_coroutine_frames(self, task_addr): 552 frames = [] 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() 557 return frames 558 559 class AsyncStack(gdb.Command): 560 def __init__(self): 561 super(AsyncStack, self).__init__("async-bt", gdb.COMMAND_USER) 562 563 def invoke(self, arg, from_tty): 564 coroutine_filter = CoroutineFilter() 565 argv = gdb.string_to_argv(arg) 566 if len(argv) == 0: 567 try: 568 task = gdb.parse_and_eval('__coro_frame') 569 task = int(str(task.address), 16) 570 except Exception: 571 print ("Can't find __coro_frame in current context.\n" + 572 "Please use `async-bt` in stackless coroutine context.") 573 return 574 elif len(argv) != 1: 575 print("usage: async-bt <pointer to task>") 576 return 577 else: 578 task = int(argv[0], 16) 579 580 frames = coroutine_filter.create_coroutine_frames(task) 581 i = 0 582 for f in frames: 583 print '#'+ str(i), str(StripDecorator(f)) 584 i += 1 585 return 586 587 AsyncStack() 588 589 class ShowCoroFrame(gdb.Command): 590 def __init__(self): 591 super(ShowCoroFrame, self).__init__("show-coro-frame", gdb.COMMAND_USER) 592 593 def invoke(self, arg, from_tty): 594 argv = gdb.string_to_argv(arg) 595 if len(argv) != 1: 596 print("usage: show-coro-frame <address of coroutine frame>") 597 return 598 599 addr = int(argv[0], 16) 600 block = gdb.block_for_pc(long(cast_addr2long_pointer(addr).dereference())) 601 if block == None: 602 print "block " + str(addr) + " is none." 603 return 604 605 # Disable demangling since gdb will treat names starting with `_Z`(The marker for Itanium ABI) specially. 606 gdb.execute("set demangle-style none") 607 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() 611 612 gdb.execute("set demangle-style auto") 613 gdb.write(coro_frame.format_string(pretty_structs = True)) 614 615 ShowCoroFrame() 616 617Then let's run: 618 619.. code-block:: text 620 621 $ clang++ -std=c++20 -g debugging-example.cpp -o debugging-example 622 $ gdb ./debugging-example 623 (gdb) # We've alreay set the breakpoint. 624 (gdb) r 625 Program received signal SIGTRAP, Trace/breakpoint trap. 626 detail::chain_fn<0> () at debugging-example2.cpp:73 627 73 co_return 0; 628 (gdb) # Executes the debugging scripts 629 (gdb) source debugging-helper.py 630 (gdb) # Print the asynchronous stack 631 (gdb) async-bt 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 664 665Now we get the complete asynchronous stack! 666It is also possible to print other asynchronous stack which doesn't live in the top of the stack. 667We can make it by passing the address of the corresponding coroutine frame to ``async-bt`` command. 668 669By the debugging scripts, we can print any coroutine frame too as long as we know the address. 670For example, we can print the coroutine frame for ``detail::chain_fn<18>()`` in the above example. 671From the log record, we know the address of the coroutine frame is ``0x4412c0`` in the run. Then we can: 672 673.. code-block:: text 674 675 (gdb) show-coro-frame 0x4412c0 676 { 677 __resume_fn = 0x410960 <detail::chain_fn<18>()>, 678 __destroy_fn = 0x410d60 <detail::chain_fn<18>()>, 679 __promise = { 680 continuation = { 681 _M_fr_ptr = 0x441270 682 }, 683 result = 0 684 }, 685 struct_Awaiter_0 = { 686 struct_std____n4861__coroutine_handle_0 = { 687 struct_std____n4861__coroutine_handle = { 688 PointerType = 0x441310 689 } 690 } 691 }, 692 struct_task_1 = { 693 struct_std____n4861__coroutine_handle_0 = { 694 struct_std____n4861__coroutine_handle = { 695 PointerType = 0x0 696 } 697 } 698 }, 699 struct_task__promise_type__FinalSuspend_2 = { 700 struct_std____n4861__coroutine_handle = { 701 PointerType = 0x0 702 } 703 }, 704 __coro_index = 1 '\001', 705 struct_std____n4861__suspend_always_3 = { 706 __int_8 = 0 '\000' 707 } 708 709 710Get the living coroutines 711========================= 712 713Another useful task when debugging coroutines is to enumerate the list of 714living coroutines, which is often done with threads. While technically 715possible, this task is not recommended in production code as it is costly at 716runtime. One such solution is to store the list of currently running coroutines 717in a collection: 718 719.. code-block:: c++ 720 721 inline std::unordered_set<void*> lived_coroutines; 722 // For all promise_type we want to record 723 class promise_type { 724 public: 725 promise_type() { 726 // Note to avoid data races 727 lived_coroutines.insert(std::coroutine_handle<promise_type>::from_promise(*this).address()); 728 } 729 ~promise_type() { 730 // Note to avoid data races 731 lived_coroutines.erase(std::coroutine_handle<promise_type>::from_promise(*this).address()); 732 } 733 }; 734 735In 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 737coroutine we can derive the function, `promise_type`, and other members of the 738frame. Thus, we could print the list of lived coroutines from that collection. 739 740Please note that the above is expensive from a storage perspective, and requires 741some level of locking (not pictured) on the collection to prevent data races. 742