# # Utility methods to help inspect a ZODB # # Laurence Rowe 21/04/2005 # # See also $SOFTWARE_HOME/bin/analyze.py # and http://mail.zope.org/pipermail/zodb-dev/2001-August/001309.html # # First open the storage (read-only!) and iterate to the transaction you are # interested in. recs = list(txn). find the size of each rec by len(rec.data) # target is rec.oid of the rec you are interested in. # # In a zope debug console you can get the object with app._p_jar[rec.oid] # For some objects (like BTrees.IOBTree.IOBucket) this is pretty useless. # They represent themselves as their C data structure. Better find their path. # # Build a refmap - graph of object references # (not too slow if the data.fs fits in memory). # use doSearch to get a reference path (beginnings of other paths are returned # as additionals). With the list of oids you can reconstruct the path by using # app._p_jar[oid]. When you reach a python object something useful is shown! # from ZODB.referencesf import referencesf def buildRefmap(fs): '''build a refmap from a filestorage. look in every record of every transaction. build a dict of oid -> list(referenced oids) ''' refmap = {} fsi = fs.iterator() for txn in fsi: for rec in txn: pickle, revid = fs.load(rec.oid, rec.version) refs = referencesf(pickle) refmap[rec.oid] = refs return refmap def backrefs(target, refmap): '''Return a list of oids in the refmap who reference target ''' oidlist = [] for oid, refs in refmap.iteritems(): if target in refs: oidlist.append(oid) return oidlist def doSearch(target, refmap): '''for a target oid find the path of objects that refer to it. break if we reach no more references or find a cycle ''' path = [target] additionals = [] while True: target = path[-1:].pop() brefs = backrefs(target, refmap) if not brefs: break bref = brefs[0] if bref in path: print 'cyclic', bref break if len(brefs) == 1: path.append(bref) print bref continue additionals.append( (target, brefs[1:]) ) print bref, brefs[1:] path.append(bref) return (path, additionals)