import cgi import gc import os localDir = os.path.join(os.getcwd(), os.path.dirname(__file__)) from StringIO import StringIO import sys import threading import time from types import FrameType, ModuleType import Image import ImageDraw import cherrypy import reftree def get_repr(obj, limit=250): return cgi.escape(reftree.get_repr(obj, limit)) class _(object): pass dictproxy = type(_.__dict__) method_types = [type(tuple.__le__), # 'wrapper_descriptor' type([1].__le__), # 'method-wrapper' type(sys.getcheckinterval), # 'builtin_function_or_method' type(cgi.FieldStorage.getfirst), # 'instancemethod' ] def url(path): try: return cherrypy.url(path) except AttributeError: return path def template(name, **params): p = {'maincss': url("/main.css"), 'home': url("/"), } p.update(params) return open(os.path.join(localDir, name)).read() % p class Root: period = 5 maxhistory = 300 def __init__(self): self.history = {} self.samples = 0 if cherrypy.__version__ >= '3.1': cherrypy.engine.subscribe('exit', self.stop) self.runthread = threading.Thread(target=self.start) self.runthread.start() def start(self): self.running = True while self.running: self.tick() time.sleep(self.period) def tick(self): gc.collect() typecounts = {} for obj in gc.get_objects(): objtype = type(obj) if objtype in typecounts: typecounts[objtype] += 1 else: typecounts[objtype] = 1 for objtype, count in typecounts.iteritems(): typename = objtype.__module__ + "." + objtype.__name__ if typename not in self.history: self.history[typename] = [0] * self.samples self.history[typename].append(count) samples = self.samples + 1 # Add dummy entries for any types which no longer exist for typename, hist in self.history.iteritems(): diff = samples - len(hist) if diff > 0: hist.extend([0] * diff) # Truncate history to self.maxhistory if samples > self.maxhistory: for typename, hist in self.history.iteritems(): hist.pop(0) else: self.samples = samples def stop(self): self.running = False def index(self, floor=0): rows = [] typenames = self.history.keys() typenames.sort() for typename in typenames: hist = self.history[typename] maxhist = max(hist) if maxhist > int(floor): row = ('
%s
' '
' 'Min: %s Cur: %s Max: %s TRACE
' % (cgi.escape(typename), url("chart/%s" % typename), min(hist), hist[-1], maxhist, url("trace/%s" % typename), ) ) rows.append(row) return template("graphs.html", output="\n".join(rows)) index.exposed = True def chart(self, typename): """Return a sparkline chart of the given type.""" data = self.history[typename] height = 20.0 scale = height / max(data) im = Image.new("RGB", (len(data), int(height)), 'white') draw = ImageDraw.Draw(im) draw.line([(i, int(height - (v * scale))) for i, v in enumerate(data)], fill="#009900") del draw f = StringIO() im.save(f, "PNG") result = f.getvalue() cherrypy.response.headers["Content-Type"] = "image/png" return result chart.exposed = True def trace(self, typename, objid=None): gc.collect() if objid is None: rows = self.trace_all(typename) else: rows = self.trace_one(typename, objid) return template("trace.html", output="\n".join(rows), typename=cgi.escape(typename), objid=str(objid or '')) trace.exposed = True def trace_all(self, typename): rows = [] for obj in gc.get_objects(): objtype = type(obj) if objtype.__module__ + "." + objtype.__name__ == typename: rows.append("

%s

" % ReferrerTree(obj).get_repr(obj)) if not rows: rows = ["

The type you requested was not found.

"] return rows def trace_one(self, typename, objid): rows = [] objid = int(objid) all_objs = gc.get_objects() for obj in all_objs: if id(obj) == objid: objtype = type(obj) if objtype.__module__ + "." + objtype.__name__ != typename: rows = ["

The object you requested is no longer " "of the correct type.

"] else: # Attributes rows.append('

Attributes

') for k in dir(obj): v = getattr(obj, k) if type(v) not in method_types: rows.append('

%s: %s

' % (k, get_repr(v))) del v rows.append('
') # Referrers rows.append('

Referrers (Parents)

') rows.append('

Show the ' 'entire tree of reachable objects

' % url("/tree/%s/%s" % (typename, objid))) tree = ReferrerTree(obj) tree.ignore(all_objs) for depth, parentid, parentrepr in tree.walk(maxdepth=1): if parentid: rows.append("

%s

" % parentrepr) rows.append('
') # Referents rows.append('

Referents (Children)

') for child in gc.get_referents(obj): rows.append("

%s

" % tree.get_repr(child)) rows.append('
') break if not rows: rows = ["

The object you requested was not found.

"] return rows def tree(self, typename, objid): gc.collect() rows = [] objid = int(objid) all_objs = gc.get_objects() for obj in all_objs: if id(obj) == objid: objtype = type(obj) if objtype.__module__ + "." + objtype.__name__ != typename: rows = ["

The object you requested is no longer " "of the correct type.

"] else: rows.append('
') tree = ReferrerTree(obj) tree.ignore(all_objs) for depth, parentid, parentrepr in tree.walk(maxresults=1000): rows.append(parentrepr) rows.append('
') break if not rows: rows = ["

The object you requested was not found.

"] params = {'output': "\n".join(rows), 'typename': cgi.escape(typename), 'objid': str(objid), } return template("tree.html", **params) tree.exposed = True try: # CherryPy 3 from cherrypy import tools Root.main_css = tools.staticfile.handler(root=localDir, filename="main.css") except ImportError: # CherryPy 2 cherrypy.config.update({ '/': {'log_debug_info_filter.on': False}, '/main.css': { 'static_filter.on': True, 'static_filter.file': 'main.css', 'static_filter.root': localDir, }, }) class ReferrerTree(reftree.Tree): ignore_modules = True def _gen(self, obj, depth=0): if self.maxdepth and depth >= self.maxdepth: yield depth, 0, "---- Max depth reached ----" raise StopIteration if isinstance(obj, ModuleType) and self.ignore_modules: raise StopIteration refs = gc.get_referrers(obj) refiter = iter(refs) self.ignore(refs, refiter) thisfile = sys._getframe().f_code.co_filename for ref in refiter: # Exclude all frames that are from this module or reftree. if (isinstance(ref, FrameType) and ref.f_code.co_filename in (thisfile, self.filename)): continue # Exclude all functions and classes from this module or reftree. mod = getattr(ref, "__module__", "") if "dowser" in mod or "reftree" in mod or mod == '__main__': continue # Exclude all parents in our ignore list. if id(ref) in self._ignore: continue # Yield the (depth, id, repr) of our object. yield depth, 0, '%s
' % (" " * depth) if id(ref) in self.seen: yield depth, id(ref), "see %s above" % id(ref) else: self.seen[id(ref)] = None yield depth, id(ref), self.get_repr(ref, obj) for parent in self._gen(ref, depth + 1): yield parent yield depth, 0, '%s
' % (" " * depth) def get_repr(self, obj, referent=None): """Return an HTML tree block describing the given object.""" objtype = type(obj) typename = objtype.__module__ + "." + objtype.__name__ prettytype = typename.replace("__builtin__.", "") name = getattr(obj, "__name__", "") if name: prettytype = "%s %r" % (prettytype, name) key = "" if referent: key = self.get_refkey(obj, referent) return ('%s ' '%s%s
' '%s' % (url("/trace/%s/%s" % (typename, id(obj))), id(obj), prettytype, key, get_repr(obj, 100)) ) def get_refkey(self, obj, referent): """Return the dict key or attribute name of obj which refers to referent.""" if isinstance(obj, dict): for k, v in obj.iteritems(): if v is referent: return " (via its %r key)" % k for k in dir(obj) + ['__dict__']: if getattr(obj, k, None) is referent: return " (via its %r attribute)" % k return "" if __name__ == '__main__': ## cherrypy.config.update({"environment": "production"}) try: cherrypy.quickstart(Root()) except AttributeError: cherrypy.root = Root() cherrypy.server.start()