Back to index

moin  1.9.0~rc2
thread_monitor.py
Go to the documentation of this file.
00001 # -*- coding: ascii -*-
00002 """
00003     Thread monitor - Check the state of all threads.
00004 
00005     Just call activate_hook() as early as possible in program execution.
00006     Then you can trigger the output of tracebacks of all threads
00007     by calling trigger_dump().
00008 
00009     Usage of Python 2.5 is recommended because it allows for a much safer
00010     and faster frame extraction.
00011 
00012     @copyright: 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
00013     @license: GNU GPL Version 2
00014 """
00015 
00016 
00017 __all__ = "activate_hook trigger_dump dump_regularly".split()
00018 
00019 
00020 import sys
00021 import threading
00022 import traceback
00023 from time import sleep
00024 from StringIO import StringIO
00025 
00026 from MoinMoin.support.python_compatibility import set
00027 
00028 
00029 class AbstractMonitor(object):
00030     def activate_hook(self):
00031         """ Activates the thread monitor hook. Note that this might interfere
00032         with any kind of profiler and some debugging extensions. """
00033         raise NotImplementedError
00034 
00035     def trigger_dump(self, dumpfile=None):
00036         """ Triggers the dump of the tracebacks of all threads.
00037             If dumpfile is specified, it is used as the output file. """
00038         raise NotImplementedError
00039 
00040     def hook_enabled(self):
00041         """ Returns true if the thread_monitor hook is enabled. """
00042         raise NotImplementedError
00043 
00044 class LegacyMonitor(AbstractMonitor):
00045     # global state
00046     dumping = False
00047     dump_file = None
00048     dumped = set()
00049     to_dump = set()
00050     hook_enabled = False
00051 
00052     def dump(cls, label):
00053         df = cls.dump_file or sys.stderr
00054         s = StringIO()
00055         print >>s, "\nDumping thread %s:" % (label, )
00056         try:
00057             raise ZeroDivisionError
00058         except ZeroDivisionError:
00059             f = sys.exc_info()[2].tb_frame.f_back.f_back
00060         traceback.print_list(traceback.extract_stack(f, None), s)
00061         df.write(s.getvalue())
00062     dump = classmethod(dump)
00063 
00064     def dump_hook(cls, a, b, c): # arguments are ignored
00065         if cls.dumping and sys.exc_info()[0] is None:
00066             thread = threading.currentThread()
00067             if thread in cls.to_dump:
00068                 cls.dump(repr(thread))
00069                 cls.to_dump.discard(thread)
00070                 cls.dumped.add(thread)
00071                 if not cls.to_dump:
00072                     cls.dumping = False
00073     dump_hook = classmethod(dump_hook)
00074 
00075     def trigger_dump(cls, dumpfile=None):
00076         cls.to_dump = set(threading.enumerate())
00077         if dumpfile is not None:
00078             cls.dump_file = dumpfile
00079         cls.dumping = True
00080     trigger_dump = classmethod(trigger_dump)
00081 
00082     def activate_hook(cls):
00083         sys.setprofile(cls.dump_hook)
00084         threading.setprofile(cls.dump_hook)
00085         cls.hook_enabled = True
00086     activate_hook = classmethod(activate_hook)
00087 
00088     def hook_enabled(cls):
00089         return cls.hook_enabled
00090     hook_enabled = classmethod(hook_enabled)
00091 
00092 
00093 class DirectMonitor(AbstractMonitor):
00094     def __init__(self):
00095         self.enabled = False
00096         assert hasattr(sys, "_current_frames")
00097 
00098     def activate_hook(self):
00099         self.enabled = True
00100 
00101     def trigger_dump(self, dumpfile=None):
00102         if not self.enabled:
00103             return
00104         dumpfile = dumpfile or sys.stderr
00105         cur_frames = sys._current_frames()
00106         for i in cur_frames:
00107             s = StringIO()
00108             print >>s, "\nDumping thread (id %s):" % (i, )
00109             traceback.print_stack(cur_frames[i], file=s)
00110             dumpfile.write(s.getvalue())
00111 
00112     def hook_enabled(self):
00113         return self.enabled
00114 
00115 
00116 def dump_regularly(seconds):
00117     """ Dumps the tracebacks every 'seconds' seconds. """
00118     activate_hook()
00119 
00120     def background_dumper(seconds):
00121         while 1:
00122             sleep(seconds)
00123             trigger_dump()
00124 
00125     threading.Thread(target=background_dumper, args=(seconds, )).start()
00126 
00127 
00128 # Python 2.5 provides an optimised version
00129 if hasattr(sys, "_current_frames"):
00130     mon = DirectMonitor()
00131 else:
00132     mon = LegacyMonitor()
00133 
00134 activate_hook = mon.activate_hook
00135 trigger_dump = mon.trigger_dump
00136 hook_enabled = mon.hook_enabled