diff --git a/mitogen/debug.py b/mitogen/debug.py index e1122192..f2746380 100644 --- a/mitogen/debug.py +++ b/mitogen/debug.py @@ -33,17 +33,73 @@ Basic signal handler for dumping thread stacks. import difflib import logging import os +import gc import signal import sys import threading import time import traceback +import mitogen.core +import mitogen.master +import mitogen.parent + LOG = logging.getLogger(__name__) _last = None +def _hex(n): + return '%08x' % n + + +def get_routers(): + return { + _hex(id(router)): router + for klass in ( + mitogen.core.Router, + mitogen.parent.Router, + mitogen.master.Router, + ) + for router in gc.get_referrers(klass) + if isinstance(router, mitogen.core.Router) + } + + +def get_router_info(): + return { + 'routers': { + id_: { + 'id': id_, + 'streams': len(set(router._stream_by_id.values())), + 'contexts': len(set(router._context_by_id.values())), + 'handles': len(router._handle_map), + } + for id_, router in get_routers().items() + } + } + + +def get_router_info(router): + pass + + +def get_stream_info(router_id): + router = get_routers().get(router_id) + return { + 'streams': dict( + (_hex(id(stream)), ({ + 'name': stream.name, + 'remote_id': stream.remote_id, + 'sent_module_count': len(getattr(stream, 'sent_modules', [])), + 'routes': sorted(getattr(stream, 'routes', [])), + 'type': type(stream).__module__, + })) + for via_id, stream in router._stream_by_id.items() + ) + } + + def format_stacks(): name_by_id = { t.ident: t.name @@ -118,3 +174,42 @@ def dump_to_logger(): th = threading.Thread(target=_logging_main) th.setDaemon(True) th.start() + + +class ContextDebugger(object): + @classmethod + @mitogen.core.takes_econtext + def _configure_context(cls, econtext): + mitogen.parent.upgrade_router(econtext) + econtext.debugger = cls(econtext.router) + + def __init__(self, router): + self.router = router + self.router.add_handler( + func=self._on_debug_msg, + handle=mitogen.core.DEBUG, + persist=True, + policy=mitogen.core.has_parent_authority, + ) + mitogen.core.listen(router, 'register', self._on_stream_register) + LOG.debug('Context debugging configured.') + + def _on_stream_register(self, context, stream): + LOG.debug('_on_stream_register: sending configure() to %r', stream) + context.call_async(ContextDebugger._configure_context) + + def _on_debug_msg(self, msg): + if msg != mitogen.core._DEAD: + threading.Thread( + target=self._handle_debug_msg, + name='ContextDebuggerHandler', + args=(msg,) + ).start() + + def _handle_debug_msg(self, msg): + try: + method, args, kwargs = msg.unpickle() + msg.reply(getattr(cls, method)(*args, **kwargs)) + except Exception: + e = sys.exc_info()[1] + msg.reply(mitogen.core.CallError(e))