merge stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 11 Mar 2010 16:49:59 +0100
branchstable
changeset 4868 e34cbd85d14d
parent 4867 b67838d18a4f (diff)
parent 4861 2a16dbf5002c (current diff)
child 4870 101858d845f7
merge
web/request.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_gcdebug.py	Thu Mar 11 16:49:59 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
--- a/server/repository.py	Wed Mar 10 12:52:33 2010 +0100
+++ b/server/repository.py	Thu Mar 11 16:49:59 2010 +0100
@@ -365,10 +365,15 @@
     def stats(self): # XXX restrict to managers session?
         import threading
         results = {}
-        for hits, misses, title in (
-            (self.querier.cache_hit, self.querier.cache_miss, 'rqlt_st'),
-            (self.system_source.cache_hit, self.system_source.cache_miss, 'sql'),
+        querier = self.querier
+        source = self.system_source
+        for size, maxsize, hits, misses, title in (
+            (len(querier._rql_cache), self.config['rql-cache-size'],
+            querier.cache_hit, querier.cache_miss, 'rqlt_st'),
+            (len(source._cache), self.config['rql-cache-size'],
+            source.cache_hit, source.cache_miss, 'sql'),
             ):
+            results['%s_cache_size' % title] =  '%s / %s' % (size, maxsize)
             results['%s_cache_hit' % title] =  hits
             results['%s_cache_miss' % title] = misses
             results['%s_cache_hit_percent' % title] = (hits * 100) / (hits + misses)
--- a/server/session.py	Wed Mar 10 12:52:33 2010 +0100
+++ b/server/session.py	Thu Mar 11 16:49:59 2010 +0100
@@ -569,6 +569,7 @@
                 self.error('thread %s still alive after 10 seconds, will close '
                            'session anyway', thread)
         self.rollback()
+        del self._threaddata
 
     # transaction data/operations management ##################################
 
@@ -767,7 +768,7 @@
 
     def close(self):
         """do not close pool on session close, since they are shared now"""
-        self.rollback()
+        self.parent_session.close()
 
     def user_data(self):
         """returns a dictionnary with this user's information"""
--- a/web/request.py	Wed Mar 10 12:52:33 2010 +0100
+++ b/web/request.py	Thu Mar 11 16:49:59 2010 +0100
@@ -287,7 +287,11 @@
             if breadcrumbs is None:
                 breadcrumbs = SizeConstrainedList(10)
                 self.set_session_data('breadcrumbs', breadcrumbs)
-            breadcrumbs.append(self.url())
+                breadcrumbs.append(self.url())
+            else:
+                url = self.url()
+                if breadcrumbs[-1] != url:
+                    breadcrumbs.append(url)
 
     def last_visited_page(self):
         breadcrumbs = self.get_session_data('breadcrumbs', None)
--- a/web/views/debug.py	Wed Mar 10 12:52:33 2010 +0100
+++ b/web/views/debug.py	Thu Mar 11 16:49:59 2010 +0100
@@ -27,13 +27,13 @@
 
 
 class ProcessInformationView(StartupView):
+    """display various web server /repository information"""
     __regid__ = 'info'
     __select__ = none_rset() & match_user_groups('managers')
 
     title = _('server information')
 
     def call(self, **kwargs):
-        """display server information"""
         req = self._cw
         dtformat = req.property_value('ui.datetime-format')
         _ = req._
@@ -111,24 +111,59 @@
 
 
 class RegistryView(StartupView):
+    """display vregistry content"""
     __regid__ = 'registry'
     __select__ = StartupView.__select__ & match_user_groups('managers')
     title = _('registry')
 
     def call(self, **kwargs):
-        """The default view representing the instance's management"""
         self.w(u'<h1>%s</h1>' % _("Registry's content"))
         keys = sorted(self._cw.vreg)
-        self.w(u'<p>%s</p>\n' % ' - '.join('<a href="/_registry#%s">%s</a>'
-                                           % (key, key) for key in keys))
+        url = self._cw.url()
+        self.w(u'<p>%s</p>\n' % ' - '.join('<a href="%s#%s">%s</a>'
+                                           % (url, key, key) for key in keys))
         for key in keys:
-            self.w(u'<h2><a name="%s">%s</a></h2>' % (key, key))
-            items = self._cw.vreg[key].items()
-            if items:
-                self.w(u'<table><tbody>')
-                for key, value in sorted(items):
-                    self.w(u'<tr><td>%s</td><td>%s</td></tr>'
-                           % (key, xml_escape(repr(value))))
-                self.w(u'</tbody></table>\n')
+            self.w(u'<h2 id="%s">%s</h2>' % (key, key))
+            if self._cw.vreg[key]:
+                values = sorted(self._cw.vreg[key].iteritems())
+                self.wview('pyvaltable', pyvalue=[(key, xml_escape(repr(val)))
+                                                  for key, val in values])
             else:
                 self.w(u'<p>Empty</p>\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'<h1>%s</h1>' % _('Garbage collection information'))
+        counters, ocounters, garbage = gc_info(lookupclasses,
+                                               viewreferrersclasses=())
+        self.w(u'<h3>%s</h3>' % _('Looked up classes'))
+        values = sorted(counters.iteritems(), key=lambda x: x[1], reverse=True)
+        self.wview('pyvaltable', pyvalue=values)
+        self.w(u'<h3>%s</h3>' % _('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'<h3>%s</h3>' % _('Unreachable objects'))
+            values = sorted(xml_escape(repr(o) for o in garbage))
+            self.wview('pyvallist', pyvalue=values)
--- a/web/views/pyviews.py	Wed Mar 10 12:52:33 2010 +0100
+++ b/web/views/pyviews.py	Thu Mar 11 16:49:59 2010 +0100
@@ -17,18 +17,23 @@
     def call(self, pyvalue, headers=None):
         if headers is None:
             headers = self._cw.form.get('headers')
-        self.w(u'<table class="listing">\n')
+        w = self.w
+        w(u'<table class="listing">\n')
         if headers:
-            self.w(u'<tr>')
+            w(u'<thead>')
+            w(u'<tr>')
             for header in headers:
-                self.w(u'<th>%s</th>' % header)
-            self.w(u'</tr>\n')
+                w(u'<th>%s</th>' % header)
+            w(u'</tr>\n')
+            w(u'</thead>')
+        w(u'<tbody>')
         for row in pyvalue:
-            self.w(u'<tr>')
+            w(u'<tr>')
             for cell in row:
-                self.w(u'<td>%s</td>' % cell)
-            self.w(u'</tr>\n')
-        self.w(u'</table>\n')
+                w(u'<td>%s</td>' % cell)
+            w(u'</tr>\n')
+        w(u'</tbody>')
+        w(u'</table>\n')
 
 
 class PyValListView(View):