summaryrefslogtreecommitdiff
path: root/cherrypy/lib/gctools.py
diff options
context:
space:
mode:
Diffstat (limited to 'cherrypy/lib/gctools.py')
-rw-r--r--cherrypy/lib/gctools.py217
1 files changed, 0 insertions, 217 deletions
diff --git a/cherrypy/lib/gctools.py b/cherrypy/lib/gctools.py
deleted file mode 100644
index 4b616c59..00000000
--- a/cherrypy/lib/gctools.py
+++ /dev/null
@@ -1,217 +0,0 @@
-import gc
-import inspect
-import os
-import sys
-import time
-
-try:
- import objgraph
-except ImportError:
- objgraph = None
-
-import cherrypy
-from cherrypy import _cprequest, _cpwsgi
-from cherrypy.process.plugins import SimplePlugin
-
-
-class ReferrerTree(object):
-
- """An object which gathers all referrers of an object to a given depth."""
-
- peek_length = 40
-
- def __init__(self, ignore=None, maxdepth=2, maxparents=10):
- self.ignore = ignore or []
- self.ignore.append(inspect.currentframe().f_back)
- self.maxdepth = maxdepth
- self.maxparents = maxparents
-
- def ascend(self, obj, depth=1):
- """Return a nested list containing referrers of the given object."""
- depth += 1
- parents = []
-
- # Gather all referrers in one step to minimize
- # cascading references due to repr() logic.
- refs = gc.get_referrers(obj)
- self.ignore.append(refs)
- if len(refs) > self.maxparents:
- return [("[%s referrers]" % len(refs), [])]
-
- try:
- ascendcode = self.ascend.__code__
- except AttributeError:
- ascendcode = self.ascend.im_func.func_code
- for parent in refs:
- if inspect.isframe(parent) and parent.f_code is ascendcode:
- continue
- if parent in self.ignore:
- continue
- if depth <= self.maxdepth:
- parents.append((parent, self.ascend(parent, depth)))
- else:
- parents.append((parent, []))
-
- return parents
-
- def peek(self, s):
- """Return s, restricted to a sane length."""
- if len(s) > (self.peek_length + 3):
- half = self.peek_length // 2
- return s[:half] + '...' + s[-half:]
- else:
- return s
-
- def _format(self, obj, descend=True):
- """Return a string representation of a single object."""
- if inspect.isframe(obj):
- filename, lineno, func, context, index = inspect.getframeinfo(obj)
- return "<frame of function '%s'>" % func
-
- if not descend:
- return self.peek(repr(obj))
-
- if isinstance(obj, dict):
- return "{" + ", ".join(["%s: %s" % (self._format(k, descend=False),
- self._format(v, descend=False))
- for k, v in obj.items()]) + "}"
- elif isinstance(obj, list):
- return "[" + ", ".join([self._format(item, descend=False)
- for item in obj]) + "]"
- elif isinstance(obj, tuple):
- return "(" + ", ".join([self._format(item, descend=False)
- for item in obj]) + ")"
-
- r = self.peek(repr(obj))
- if isinstance(obj, (str, int, float)):
- return r
- return "%s: %s" % (type(obj), r)
-
- def format(self, tree):
- """Return a list of string reprs from a nested list of referrers."""
- output = []
-
- def ascend(branch, depth=1):
- for parent, grandparents in branch:
- output.append((" " * depth) + self._format(parent))
- if grandparents:
- ascend(grandparents, depth + 1)
- ascend(tree)
- return output
-
-
-def get_instances(cls):
- return [x for x in gc.get_objects() if isinstance(x, cls)]
-
-
-class RequestCounter(SimplePlugin):
-
- def start(self):
- self.count = 0
-
- def before_request(self):
- self.count += 1
-
- def after_request(self):
- self.count -= 1
-request_counter = RequestCounter(cherrypy.engine)
-request_counter.subscribe()
-
-
-def get_context(obj):
- if isinstance(obj, _cprequest.Request):
- return "path=%s;stage=%s" % (obj.path_info, obj.stage)
- elif isinstance(obj, _cprequest.Response):
- return "status=%s" % obj.status
- elif isinstance(obj, _cpwsgi.AppResponse):
- return "PATH_INFO=%s" % obj.environ.get('PATH_INFO', '')
- elif hasattr(obj, "tb_lineno"):
- return "tb_lineno=%s" % obj.tb_lineno
- return ""
-
-
-class GCRoot(object):
-
- """A CherryPy page handler for testing reference leaks."""
-
- classes = [
- (_cprequest.Request, 2, 2,
- "Should be 1 in this request thread and 1 in the main thread."),
- (_cprequest.Response, 2, 2,
- "Should be 1 in this request thread and 1 in the main thread."),
- (_cpwsgi.AppResponse, 1, 1,
- "Should be 1 in this request thread only."),
- ]
-
- def index(self):
- return "Hello, world!"
- index.exposed = True
-
- def stats(self):
- output = ["Statistics:"]
-
- for trial in range(10):
- if request_counter.count > 0:
- break
- time.sleep(0.5)
- else:
- output.append("\nNot all requests closed properly.")
-
- # gc_collect isn't perfectly synchronous, because it may
- # break reference cycles that then take time to fully
- # finalize. Call it thrice and hope for the best.
- gc.collect()
- gc.collect()
- unreachable = gc.collect()
- if unreachable:
- if objgraph is not None:
- final = objgraph.by_type('Nondestructible')
- if final:
- objgraph.show_backrefs(final, filename='finalizers.png')
-
- trash = {}
- for x in gc.garbage:
- trash[type(x)] = trash.get(type(x), 0) + 1
- if trash:
- output.insert(0, "\n%s unreachable objects:" % unreachable)
- trash = [(v, k) for k, v in trash.items()]
- trash.sort()
- for pair in trash:
- output.append(" " + repr(pair))
-
- # Check declared classes to verify uncollected instances.
- # These don't have to be part of a cycle; they can be
- # any objects that have unanticipated referrers that keep
- # them from being collected.
- allobjs = {}
- for cls, minobj, maxobj, msg in self.classes:
- allobjs[cls] = get_instances(cls)
-
- for cls, minobj, maxobj, msg in self.classes:
- objs = allobjs[cls]
- lenobj = len(objs)
- if lenobj < minobj or lenobj > maxobj:
- if minobj == maxobj:
- output.append(
- "\nExpected %s %r references, got %s." %
- (minobj, cls, lenobj))
- else:
- output.append(
- "\nExpected %s to %s %r references, got %s." %
- (minobj, maxobj, cls, lenobj))
-
- for obj in objs:
- if objgraph is not None:
- ig = [id(objs), id(inspect.currentframe())]
- fname = "graph_%s_%s.png" % (cls.__name__, id(obj))
- objgraph.show_backrefs(
- obj, extra_ignore=ig, max_depth=4, too_many=20,
- filename=fname, extra_info=get_context)
- output.append("\nReferrers for %s (refcount=%s):" %
- (repr(obj), sys.getrefcount(obj)))
- t = ReferrerTree(ignore=[objs], maxdepth=3)
- tree = t.ascend(obj)
- output.extend(t.format(tree))
-
- return "\n".join(output)
- stats.exposed = True