ObjectReport tweaks for tracking down LawBot AI mem leak
This commit is contained in:
parent
45a584ba05
commit
6d4def41b7
|
|
@ -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[:]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue