Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(277)

Unified Diff: third_party/cherrypy/lib/gctools.py

Issue 9368042: Add CherryPy to third_party. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/cherrypy/lib/encoding.py ('k') | third_party/cherrypy/lib/http.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/cherrypy/lib/gctools.py
===================================================================
--- third_party/cherrypy/lib/gctools.py (revision 0)
+++ third_party/cherrypy/lib/gctools.py (revision 0)
@@ -0,0 +1,214 @@
+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
+
Property changes on: third_party/cherrypy/lib/gctools.py
___________________________________________________________________
Added: svn:eol-style
+ LF
« no previous file with comments | « third_party/cherrypy/lib/encoding.py ('k') | third_party/cherrypy/lib/http.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698