chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.85k stars 1.2k forks source link

Decode a coroutine stack trace #1239

Open battlmonstr opened 1 year ago

battlmonstr commented 1 year ago

When debugging coroutine-based code I see stack traces like this:

frame #0: 0x00000001032c5258 sentry`silkworm::sentry::common::Channel<silkworm::sentry::common::Message>::receive() at channel.hpp:62:23
frame #1: 0x000000010341e578 sentry`std::experimental::coroutines_v1::coroutine_handle<void>::resume(this=0x0000600003788010) at coroutine:121:7
frame #2: 0x000000010341e46c sentry`boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::resume(this=0x0000600003788010) at awaitable.hpp:483:11
frame #3: 0x000000010341e2ac sentry`boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump(this=0x000000016d436428) at awaitable.hpp:751:47
frame #4: 0x00000001031d53dc sentry`boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>::operator(this=0x000000016d436428, ec=0x000000016d436430)(boost::system::error_code const&) at use_awaitable.hpp:93:11
frame #5: 0x00000001031d5354 sentry`boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>::operator(this=0x000000016d436428)() at bind_handler.hpp:171:5
frame #6: 0x00000001031d57cc sentry`void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> >(function=0x000000016d436428, context=0x000000016d436428) at handler_invoke_helpers.hpp:51:3
frame #7: 0x00000001031d56bc sentry`void boost::asio::detail::executor_function::complete<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, std::__1::allocator<void> >(base=0x0000600001e90b40, call=true) at executor_function.hpp:116:7
frame #8: 0x00000001033fc0c4 sentry`boost::asio::detail::executor_function::operator(this=0x000000016d436500)() at executor_function.hpp:64:7
frame #9: 0x00000001033fbc40 sentry`void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::executor_function, boost::asio::detail::executor_function>(function=0x000000016d436500, context=0x000000016d436500) at handler_invoke_helpers.hpp:51:3
frame #10: 0x00000001033fd020 sentry`boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::__1::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete(owner=0x0000600002883020, base=0x0000600003a88320, (null)=0x000000016d4365b8, (null)=0) at executor_op.hpp:70:7
frame #11: 0x0000000103408d44 sentry`boost::asio::detail::scheduler_operation::complete(this=0x0000600003a88320, owner=0x0000600002883020, ec=0x000000016d4365b8, bytes_transferred=0) at scheduler_operation.hpp:40:5
frame #12: 0x0000000103408c5c sentry`boost::asio::detail::strand_executor_service::run_ready_handlers(impl=std::__1::shared_ptr<boost::asio::detail::strand_executor_service::strand_impl>::element_type @ 0x0000600002883020 strong=74 weak=1) at strand_executor_service.ipp:150:8
frame #13: 0x000000010340bed4 sentry`boost::asio::detail::strand_executor_service::invoker<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4ul> const, void>::operator(this=0x000000016d436660)() at strand_executor_service.hpp:136:5
frame #14: 0x000000010340bd78 sentry`void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::strand_executor_service::invoker<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4ul> const, void>, boost::asio::detail::strand_executor_service::invoker<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4ul> const, void> >(function=0x000000016d436660, context=0x000000016d436660) at handler_invoke_helpers.hpp:51:3
frame #15: 0x000000010340c4d8 sentry`boost::asio::detail::executor_op<boost::asio::detail::strand_executor_service::invoker<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4ul> const, void>, boost::asio::detail::recycling_allocator<void, boost::asio::detail::thread_info_base::default_tag>, boost::asio::detail::scheduler_operation>::do_complete(owner=0x0000000151e044a0, base=0x0000600003a80280, (null)=0x000000016d436910, (null)=0) at executor_op.hpp:70:7
frame #16: 0x0000000103408d44 sentry`boost::asio::detail::scheduler_operation::complete(this=0x0000600003a80280, owner=0x0000000151e044a0, ec=0x000000016d436910, bytes_transferred=0) at scheduler_operation.hpp:40:5
frame #17: 0x00000001034426f0 sentry`boost::asio::detail::scheduler::do_run_one(this=0x0000000151e044a0, lock=0x000000016d4367d0, this_thread=0x000000016d436808, ec=0x000000016d436910) at scheduler.ipp:492:12
frame #18: 0x00000001034423b0 sentry`boost::asio::detail::scheduler::run(this=0x0000000151e044a0, ec=0x000000016d436910) at scheduler.ipp:210:10
frame #19: 0x000000010303b7ac sentry`boost::asio::io_context::run(this=0x0000600000580258) at io_context.ipp:63:24

Frame 0 is my function, but the rest is coroutine execution machinery.

It is not easy to figure out where this call originated from. In synchronous code it would be possible to step out of the function, but here a step out goes into the abyss.

It would be useful to have a helper function to decode the actual logical stack trace and print it, for example:

awaitable<void> a() {
    auto executor = co_await this_coro::executor;
    co_spawn(executor, b(), detached);
}

awaitable<void> b() {
    co_await c();
}

awaitable<void> c() {
    print_coro_stack();
}

Should print:

frame #0: c() at ...
frame #1: b() at ...
...

Ideally it should also recognize co_spawn points that await:

co_await co_spawn(executor, b(), use_awaitable);

and parallel_group fork points:

co_await (b1() && b2());
assafcohen commented 1 year ago

That would be awesome