# HG changeset patch # User Sylvain Thénault # Date 1268322547 -3600 # Node ID 550e35a69b750b5ff8a23252ce5225fb6ea821a4 # Parent 90ad729d3540b4d875f5b02c4a01d717212e7da9 [debug] a new view to help debugging memory leaks diff -r 90ad729d3540 -r 550e35a69b75 _gcdebug.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_gcdebug.py Thu Mar 11 16:49:07 2010 +0100 @@ -0,0 +1,87 @@ + +import gc, types, weakref + +from cubicweb.schema import CubicWebRelationSchema, CubicWebEntitySchema + +listiterator = type(iter([])) + +IGNORE_CLASSES = ( + type, tuple, dict, list, set, frozenset, type(len), + weakref.ref, weakref.WeakKeyDictionary, + listiterator, + property, classmethod, + types.ModuleType, types.FunctionType, types.MethodType, + types.MemberDescriptorType, types.GetSetDescriptorType, + ) + +def _get_counted_class(obj, classes): + for cls in classes: + if isinstance(obj, cls): + return cls + raise AssertionError() + +def gc_info(countclasses, + ignoreclasses=IGNORE_CLASSES, + viewreferrersclasses=(), showobjs=False): + gc.collect() + gc.collect() + counters = {} + ocounters = {} + for obj in gc.get_objects(): + if isinstance(obj, countclasses): + cls = _get_counted_class(obj, countclasses) + try: + counters[cls.__name__] += 1 + except KeyError: + counters[cls.__name__] = 1 + elif not isinstance(obj, ignoreclasses): + try: + key = '%s.%s' % (obj.__class__.__module__, + obj.__class__.__name__) + except AttributeError: + key = str(obj) + try: + ocounters[key] += 1 + except KeyError: + ocounters[key] = 1 + if isinstance(obj, viewreferrersclasses): + print ' ', obj, referrers(obj, showobjs) + return counters, ocounters, gc.garbage + + +def referrers(obj, showobj=False, maxlevel=1): + objreferrers = _referrers(obj, maxlevel) + try: + return sorted(set((type(x), showobj and x or getattr(x, '__name__', '%#x' % id(x))) + for x in objreferrers)) + except TypeError: + s = set() + unhashable = [] + for x in objreferrers: + try: + s.add(x) + except TypeError: + unhashable.append(x) + return sorted(s) + unhashable + +def _referrers(obj, maxlevel, _seen=None, _level=0): + interesting = [] + if _seen is None: + _seen = set() + for x in gc.get_referrers(obj): + if id(x) in _seen: + continue + _seen.add(id(x)) + if isinstance(x, types.FrameType): + continue + if isinstance(x, (CubicWebRelationSchema, CubicWebEntitySchema)): + continue + if isinstance(x, (list, tuple, set, dict, listiterator)): + if _level >= maxlevel: + pass + #interesting.append(x) + else: + interesting += _referrers(x, maxlevel, _seen, _level+1) + else: + interesting.append(x) + return interesting diff -r 90ad729d3540 -r 550e35a69b75 web/views/debug.py --- a/web/views/debug.py Thu Mar 11 16:48:38 2010 +0100 +++ b/web/views/debug.py Thu Mar 11 16:49:07 2010 +0100 @@ -130,3 +130,40 @@ for key, val in values]) else: self.w(u'

Empty

\n') + + +class GCView(StartupView): + """display garbage collector information""" + __regid__ = 'gc' + __select__ = StartupView.__select__ & match_user_groups('managers') + title = _('garbage') + + def call(self, **kwargs): + from cubicweb._gcdebug import gc_info + from rql.stmts import Union + from cubicweb.appobject import AppObject + from cubicweb.rset import ResultSet + from cubicweb.dbapi import Connection, Cursor + from cubicweb.web.request import CubicWebRequestBase + lookupclasses = (AppObject, + Union, ResultSet, + Connection, Cursor, + CubicWebRequestBase) + try: + from cubicweb.server.session import Session, ChildSession, InternalSession + lookupclasses += (InternalSession, ChildSession, Session) + except ImportError: + pass # no server part installed + self.w(u'

%s

' % _('Garbage collection information')) + counters, ocounters, garbage = gc_info(lookupclasses, + viewreferrersclasses=()) + self.w(u'

%s

' % _('Looked up classes')) + values = sorted(counters.iteritems(), key=lambda x: x[1], reverse=True) + self.wview('pyvaltable', pyvalue=values) + self.w(u'

%s

' % _('Most referenced classes')) + values = sorted(ocounters.iteritems(), key=lambda x: x[1], reverse=True) + self.wview('pyvaltable', pyvalue=values[:self._cw.form.get('nb', 20)]) + if garbage: + self.w(u'

%s

' % _('Unreachable objects')) + values = sorted(xml_escape(repr(o) for o in garbage)) + self.wview('pyvallist', pyvalue=values)