diff --git a/changes/7a68cdaf97188251836d1f71709d33ce.yaml b/changes/7a68cdaf97188251836d1f71709d33ce.yaml new file mode 100644 index 0000000000..f4b8ccee6d --- /dev/null +++ b/changes/7a68cdaf97188251836d1f71709d33ce.yaml @@ -0,0 +1,7 @@ +--- +desc: Added information about ``SynTask`` data when printing asyncio tasks in response + to receving a ``SIGUSR2`` signal. +desc:literal: false +prs: [] +type: note +... diff --git a/synapse/glob.py b/synapse/glob.py index faab5cafe0..d6801dadae 100644 --- a/synapse/glob.py +++ b/synapse/glob.py @@ -1,27 +1,38 @@ import os +import pprint import signal import asyncio import logging import threading import faulthandler +import synapse.common as s_common +import synapse.lib.time as s_time + logger = logging.getLogger(__name__) _glob_loop = None _glob_thrd = None - def _asynciostacks(*args, **kwargs): # pragma: no cover ''' A signal handler used to print asyncio task stacks and thread stacks. ''' + ts = s_time.repr(s_common.now()) print(80 * '*') - print('Asyncio tasks stacks:') + print(f'Asyncio task stacks @ {ts}:') tasks = asyncio.all_tasks(_glob_loop) for task in tasks: task.print_stack() + if hasattr(task, '_syn_task'): + st = task._syn_task + root = None + if st.root is not None: + root = st.root.iden + print(f'syntask metadata: iden={st.iden} name={st.name} root={root} user={st.user.iden} username={st.user.name}') + print(pprint.pformat(st.info)) print(80 * '*') - print('Faulthandler stack frames per thread:') + print(f'Faulthandler stack frames per thread @ {ts}:') faulthandler.dump_traceback() print(80 * '*') @@ -29,8 +40,9 @@ def _threadstacks(*args, **kwargs): # pragma: no cover ''' A signal handler used to print thread stacks. ''' + ts = s_time.repr(s_common.now()) print(80 * '*') - print('Faulthandler stack frames per thread:') + print(f'Faulthandler stack frames per thread @ {ts}:') faulthandler.dump_traceback() print(80 * '*') diff --git a/synapse/tests/test_glob.py b/synapse/tests/test_glob.py index 536cf26bd5..5d181213b1 100644 --- a/synapse/tests/test_glob.py +++ b/synapse/tests/test_glob.py @@ -1,3 +1,6 @@ +import asyncio +import unittest.mock as mock + import synapse.glob as s_glob import synapse.tests.utils as s_t_utils @@ -11,3 +14,47 @@ async def afoo(): retn = s_glob.sync(afoo()) self.eq(retn, 42) + + async def test_glob_stacks(self): + + lines = [] + def mock_print(*args, **kwargs): + self.isinstance(args[0], str) + lines.append(args[0]) + + with mock.patch('builtins.print', mock_print): + self.none(s_glob._threadstacks()) + + text = '\n'.join(lines) + self.isin('Faulthandler stack frames per thread', text) + + lines.clear() + + async with self.getTestCore() as core: + + q = 'while (true) { $lib.time.sleep(60) }' + event = asyncio.Event() + + async def coro(info): + async for mesg in core.storm(q): + if mesg[0] == 'init': + info |= mesg[1] + event.set() + + init_mesg = {} + fut = core.schedCoro(coro(init_mesg)) + + self.true(await asyncio.wait_for(event.wait(), timeout=12)) + + with mock.patch('builtins.print', mock_print): + self.none(s_glob._asynciostacks()) + + fut.cancel() + + text = '\n'.join(lines) + self.isin('Asyncio task stacks', text) + self.isin('syntask metadata', text) + self.isin(q, text) + self.isin('root=None', text) + self.isin(f'root={init_mesg.get("task", "newp")}', text) + self.isin('Faulthandler stack frames per thread', text)