--- /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):