diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 9bee390e..64465c19 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -36,7 +36,7 @@ from mopidy.utils.deps import list_deps_optparse_callback from mopidy.utils.log import setup_logging from mopidy.utils.path import get_or_create_folder, get_or_create_file from mopidy.utils.process import (exit_handler, stop_remaining_actors, - stop_actors_by_class) + stop_actors_by_class, DebugThread) from mopidy.utils.settings import list_settings_optparse_callback @@ -44,7 +44,12 @@ logger = logging.getLogger('mopidy.main') def main(): + debug_thread = DebugThread() + debug_thread.start() + + signal.signal(signal.SIGUSR1, debug_thread.handler) signal.signal(signal.SIGTERM, exit_handler) + loop = gobject.MainLoop() try: options = parse_options() diff --git a/mopidy/utils/process.py b/mopidy/utils/process.py index 80d850fe..fdeb8e8c 100644 --- a/mopidy/utils/process.py +++ b/mopidy/utils/process.py @@ -1,7 +1,9 @@ import logging import signal +import sys import thread import threading +import traceback from pykka import ActorDeadError from pykka.registry import ActorRegistry @@ -10,6 +12,9 @@ from mopidy import SettingsError logger = logging.getLogger('mopidy.utils.process') +signals = dict((k, v) for v, k in signal.__dict__.iteritems() + if v.startswith('SIG') and not v.startswith('SIG_')) + def exit_process(): logger.debug(u'Interrupting main...') thread.interrupt_main() @@ -17,8 +22,6 @@ def exit_process(): def exit_handler(signum, frame): """A :mod:`signal` handler which will exit the program on signal.""" - signals = dict((k, v) for v, k in signal.__dict__.iteritems() - if v.startswith('SIG') and not v.startswith('SIG_')) logger.info(u'Got %s signal', signals[signum]) exit_process() @@ -65,3 +68,27 @@ class BaseThread(threading.Thread): def run_inside_try(self): raise NotImplementedError + +class DebugThread(threading.Thread): + daemon = True + name = 'DebugThread' + + event = threading.Event() + + def handler(self, signum, frame): + logger.info(u'Got %s signal', signals[signum]) + self.event.set() + + def run(self): + while True: + self.event.wait() + threads = dict((t.ident, t.name) for t in threading.enumerate()) + + for ident, frame in sys._current_frames().items(): + if self.ident == ident: + continue + + print "## Thread: %s (%s) ##" % (threads[ident], ident) + print ''.join(traceback.format_stack(frame)) + + self.event.clear()