ObjectReport tweaks for tracking down LawBot AI mem leak

This commit is contained in:
Darren Ranalli 2006-06-29 18:07:28 +00:00
parent 45a584ba05
commit 6d4def41b7
4 changed files with 83 additions and 31 deletions

View File

@ -1,5 +1,5 @@
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import gcDebugOn, safeRepr
from direct.showbase.PythonUtil import gcDebugOn, safeRepr, fastRepr
from direct.showbase.TaskThreaded import TaskThreaded, TaskThread
import gc
@ -37,7 +37,7 @@ class GarbageReport(TaskThreaded):
if not wasOn:
gc.set_debug(gc.DEBUG_SAVEALL)
gc.collect()
self.notify.debug('gc.garbage == %s' % gc.garbage)
self.notify.debug('gc.garbage == %s' % fastRepr(gc.garbage))
self.garbage = list(gc.garbage)
self.notify.debug('self.garbage == %s' % self.garbage)
del gc.garbage[:]

View File

@ -8,15 +8,15 @@ class Diff:
def __init__(self, lost, gained):
self.lost=lost
self.gained=gained
def __repr__(self):
s = 'lost %s objects, gained %s objects' % (len(self.lost), len(self.gained))
s += '\n\nself.lost\n'
s += self.lost.typeFreqStr()
s += '\n\nself.gained\n'
s += self.gained.typeFreqStr()
s += '\n\nGAINED-OBJECT REFERRERS\n'
s += self.gained.referrersStr(1)
return s
def printOut(self):
print 'lost %s objects, gained %s objects' % (len(self.lost), len(self.gained))
print '\n\nself.lost\n'
print self.lost.typeFreqStr()
print '\n\nself.gained\n'
print self.gained.typeFreqStr()
self.gained.printObjsByType()
print '\n\nGAINED-OBJECT REFERRERS\n'
print self.gained.referrersStr(1)
class ObjectPool:
"""manipulate a pool of Python objects"""
@ -26,16 +26,18 @@ class ObjectPool:
self._objs = list(objects)
self._type2objs = {}
self._count2types = {}
self._len2obj = {}
type2count = {}
for obj in self._objs:
typ = type(obj)
# <type 'instance'> isn't all that useful
if typ is types.InstanceType:
typ = '%s of %s' % (typ, repr(obj.__class__))
typ = itype(obj)
type2count.setdefault(typ, 0)
type2count[typ] += 1
self._type2objs.setdefault(typ, [])
self._type2objs[typ].append(obj)
try:
self._len2obj[len(obj)] = obj
except:
pass
self._count2types = invertDict(type2count)
def _getInternalObjs(self):
@ -67,7 +69,6 @@ class ObjectPool:
thisIds = set(thisId2obj.keys())
otherIds = set(otherId2obj.keys())
lostIds = thisIds.difference(otherIds)
print 'lost: %s' % lostIds
gainedIds = otherIds.difference(thisIds)
del thisIds
del otherIds
@ -91,6 +92,27 @@ class ObjectPool:
s += '\n%s\t%s' % (count, typ)
return s
def printObjsByType(self):
print 'Object Pool: Objects By Type'
print '\n============================'
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
types = makeList(self._count2types[count])
for typ in types:
print 'TYPE: %s' % typ
print getNumberedTypedString(self._type2objs[typ])
def containerLenStr(self):
s = 'Object Pool: Container Lengths'
s += '\n=============================='
lengths = list(self._len2obj.keys())
lengths.sort()
lengths.reverse()
for count in counts:
pass
def referrersStr(self, numEach=3):
"""referrers of the first few of each type of object"""
s = ''
@ -106,7 +128,7 @@ class ObjectPool:
s += '\nOBJ: %s\n' % safeRepr(obj)
referrers = gc.get_referrers(obj)
if len(referrers):
s += getNumberedTypedString(referrers)
s += getNumberedTypedString(referrers, maxLen=80)
else:
s += '<No Referrers>'
return s

View File

@ -19,6 +19,7 @@ import sys
class ExclusiveObjectPool(DirectObject.DirectObject):
"""ObjectPool specialization that excludes particular objects"""
# IDs of objects to globally exclude from reporting
_ExclObjs = []
_ExclObjIds = {}
_SyncMaster = Sync('ExclusiveObjectPool.ExcludedObjectList')
_SerialNumGen = SerialNumGen()
@ -26,6 +27,8 @@ class ExclusiveObjectPool(DirectObject.DirectObject):
@classmethod
def addExclObjs(cls, *objs):
for obj in makeList(objs):
if id(obj) not in cls._ExclObjIds:
cls._ExclObjs.append(obj)
cls._ExclObjIds.setdefault(id(obj), 0)
cls._ExclObjIds[id(obj)] += 1
cls._SyncMaster.change()
@ -33,7 +36,10 @@ class ExclusiveObjectPool(DirectObject.DirectObject):
def removeExclObjs(cls, *objs):
for obj in makeList(objs):
assert id(obj) in cls._ExclObjIds
del cls._ExclObjIds[id(obj)]
cls._ExclObjIds[id(obj)] -= 1
if cls._ExclObjIds[id(obj)] == 0:
del cls._ExclObjIds[id(obj)]
cls._ExclObjs.remove(obj)
cls._SyncMaster.change()
def __init__(self, objects):
@ -116,6 +122,8 @@ class ObjectReport:
return self._pool.diff(other._pool)
def _getObjectList(self):
return sys.getobjects(0)
"""
if hasattr(sys, 'getobjects'):
return sys.getobjects(0)
else:
@ -149,3 +157,5 @@ class ObjectReport:
stateStack.push((obj, adjacents, i+1))
stateStack.push((adj, None, 0))
continue
"""

View File

@ -2000,20 +2000,27 @@ def safeRepr(obj):
except:
return '<** FAILED REPR OF %s **>' % obj.__class__.__name__
def fastRepr(obj, maxLen=200, strFactor=10):
def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
""" caps the length of iterable types """
if _visitedIds is None:
_visitedIds = set()
if id(obj) in _visitedIds:
return '<ALREADY-VISITED %s>' % itype(obj)
_visitedIds.add(id(obj))
if type(obj) in (types.TupleType, types.ListType):
s = ''
s += {types.TupleType: '(',
types.ListType: '[',}[type(obj)]
if len(obj) > maxLen:
o = obj[:maxLen]
ellips = '...'
else:
o = obj
ellips = ''
for item in o:
s += fastRepr(item, maxLen)
s += fastRepr(item, maxLen, _visitedIds=_visitedIds)
s += ', '
s += '...'
s += ellips
s += {types.TupleType: ')',
types.ListType: ']',}[type(obj)]
return s
@ -2025,7 +2032,8 @@ def fastRepr(obj, maxLen=200, strFactor=10):
o = obj.keys()
for key in o:
value = obj[key]
s += '%s: %s, ' % (fastRepr(key, maxLen), fastRepr(value, maxLen))
s += '%s: %s, ' % (fastRepr(key, maxLen, _visitedIds=_visitedIds),
fastRepr(value, maxLen, _visitedIds=_visitedIds))
s += '...}'
return s
elif type(obj) is types.StringType:
@ -2178,18 +2186,11 @@ class RefCounter:
del self._refCounts[key]
return result
class VerboseInstanceType(types.InstanceType.__class__):
def __init__(self, obj):
types.InstanceType(self, obj)
self._reprStr = '%s of <class %s>' % (repr(types.InstanceType),
str(obj.__class__))
def __repr__(self):
return self._reprStr
def itype(obj):
t = type(obj)
if t is types.InstanceType:
return VerboseInstanceType(obj)
return '%s of <class %s>' % (repr(types.InstanceType),
str(obj.__class__))
else:
return t
@ -2216,6 +2217,25 @@ def getNumberedTypedString(items, maxLen=5000):
s += format % (i, itype(items[i]), objStr)
return s
def printNumberedTyped(items, maxLen=5000):
"""print out each item of the list on its own line,
with each item numbered on the left from zero"""
digits = 0
n = len(items)
while n > 0:
digits += 1
n /= 10
digits = digits
format = '%0' + '%s' % digits + 'i:%s \t%s'
first = True
for i in xrange(len(items)):
first = False
objStr = fastRepr(items[i])
if len(objStr) > maxLen:
snip = '<SNIP>'
objStr = '%s%s' % (objStr[:(maxLen-len(snip))], snip)
print format % (i, itype(items[i]), objStr)
import __builtin__
__builtin__.Functor = Functor
__builtin__.Stack = Stack