How to debug asyncio coroutines with GDB?

Viewed 1252

There is some extension that allows to use GDB to see detailed info of a Python process, I installed and tried to use it with my hanging web app based on aiohttp. But, whatever request is being processed, I always see only the primary stack trace, with no useful information:

(gdb) py-bt
Traceback (most recent call first):
  File "/usr/lib/python3.7/selectors.py", line 468, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
  File "/usr/lib/python3.7/asyncio/base_events.py", line 1739, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.7/asyncio/base_events.py", line 539, in run_forever
    self._run_once()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 571, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.7/dist-packages/aiohttp/web.py", line 433, in run_app
    reuse_port=reuse_port))
  File "./my-server/main.py", line 98, in <module>
    web.run_app(app_main, host=host, port=port)

This is probably a consequence of separate stack trace created for each coroutine by asyncio.

The goal is to properly debug asyncio apps. So, how can I see execution stack and interrupt/continue coroutines?

1 Answers

After long digging, I found an alternative solution: simple & amazing py-spy! In contrast to gdb, it doesn't require target process to be run with special build of Python, also it has no problem dealing with async functions. Here is a simple use scenario:

pip install py-spy

# Get stack traces of active threads:
py-spy dump --pid 1

# Watch executing functions in realtime:
py-spy top --pid 1

A full description is available here: https://github.com/benfred/py-spy

A notice, important in some environments: just like with gdb, to be used in a Docker container, py-spy requires flag SYS_PTRACE to be set.

Related