Browse Source

ObjectReport tweaks for tracking down LawBot AI mem leak

Darren Ranalli 19 years ago
parent
commit
6d4def41b7

+ 2 - 2
direct/src/showbase/GarbageReport.py

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

+ 37 - 15
direct/src/showbase/ObjectPool.py

@@ -8,15 +8,15 @@ class Diff:
     def __init__(self, lost, gained):
     def __init__(self, lost, gained):
         self.lost=lost
         self.lost=lost
         self.gained=gained
         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:
 class ObjectPool:
     """manipulate a pool of Python objects"""
     """manipulate a pool of Python objects"""
@@ -26,16 +26,18 @@ class ObjectPool:
         self._objs = list(objects)
         self._objs = list(objects)
         self._type2objs = {}
         self._type2objs = {}
         self._count2types = {}
         self._count2types = {}
+        self._len2obj = {}
         type2count = {}
         type2count = {}
         for obj in self._objs:
         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.setdefault(typ, 0)
             type2count[typ] += 1
             type2count[typ] += 1
             self._type2objs.setdefault(typ, [])
             self._type2objs.setdefault(typ, [])
             self._type2objs[typ].append(obj)
             self._type2objs[typ].append(obj)
+            try:
+                self._len2obj[len(obj)] = obj
+            except:
+                pass
         self._count2types = invertDict(type2count)
         self._count2types = invertDict(type2count)
 
 
     def _getInternalObjs(self):
     def _getInternalObjs(self):
@@ -67,7 +69,6 @@ class ObjectPool:
         thisIds = set(thisId2obj.keys())
         thisIds = set(thisId2obj.keys())
         otherIds = set(otherId2obj.keys())
         otherIds = set(otherId2obj.keys())
         lostIds = thisIds.difference(otherIds)
         lostIds = thisIds.difference(otherIds)
-        print 'lost: %s' % lostIds
         gainedIds = otherIds.difference(thisIds)
         gainedIds = otherIds.difference(thisIds)
         del thisIds
         del thisIds
         del otherIds
         del otherIds
@@ -91,6 +92,27 @@ class ObjectPool:
                 s += '\n%s\t%s' % (count, typ)
                 s += '\n%s\t%s' % (count, typ)
         return s
         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):
     def referrersStr(self, numEach=3):
         """referrers of the first few of each type of object"""
         """referrers of the first few of each type of object"""
         s = ''
         s = ''
@@ -106,7 +128,7 @@ class ObjectPool:
                     s += '\nOBJ: %s\n' % safeRepr(obj)
                     s += '\nOBJ: %s\n' % safeRepr(obj)
                     referrers = gc.get_referrers(obj)
                     referrers = gc.get_referrers(obj)
                     if len(referrers):
                     if len(referrers):
-                        s += getNumberedTypedString(referrers)
+                        s += getNumberedTypedString(referrers, maxLen=80)
                     else:
                     else:
                         s += '<No Referrers>'
                         s += '<No Referrers>'
         return s
         return s

+ 11 - 1
direct/src/showbase/ObjectReport.py

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

+ 33 - 13
direct/src/showbase/PythonUtil.py

@@ -2000,20 +2000,27 @@ def safeRepr(obj):
     except:
     except:
         return '<** FAILED REPR OF %s **>' % obj.__class__.__name__
         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 """
     """ 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):
     if type(obj) in (types.TupleType, types.ListType):
         s = ''
         s = ''
         s += {types.TupleType: '(',
         s += {types.TupleType: '(',
               types.ListType:  '[',}[type(obj)]
               types.ListType:  '[',}[type(obj)]
         if len(obj) > maxLen:
         if len(obj) > maxLen:
             o = obj[:maxLen]
             o = obj[:maxLen]
+            ellips = '...'
         else:
         else:
             o = obj
             o = obj
+            ellips = ''
         for item in o:
         for item in o:
-            s += fastRepr(item, maxLen)
+            s += fastRepr(item, maxLen, _visitedIds=_visitedIds)
             s += ', '
             s += ', '
-        s += '...'
+        s += ellips
         s += {types.TupleType: ')',
         s += {types.TupleType: ')',
               types.ListType:  ']',}[type(obj)]
               types.ListType:  ']',}[type(obj)]
         return s
         return s
@@ -2025,7 +2032,8 @@ def fastRepr(obj, maxLen=200, strFactor=10):
             o = obj.keys()
             o = obj.keys()
         for key in o:
         for key in o:
             value = obj[key]
             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 += '...}'
         s += '...}'
         return s
         return s
     elif type(obj) is types.StringType:
     elif type(obj) is types.StringType:
@@ -2178,18 +2186,11 @@ class RefCounter:
             del self._refCounts[key]
             del self._refCounts[key]
         return result
         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):
 def itype(obj):
     t = type(obj)
     t = type(obj)
     if t is types.InstanceType:
     if t is types.InstanceType:
-        return VerboseInstanceType(obj)
+        return '%s of <class %s>' % (repr(types.InstanceType),
+                                     str(obj.__class__))
     else:
     else:
         return t
         return t
 
 
@@ -2216,6 +2217,25 @@ def getNumberedTypedString(items, maxLen=5000):
         s += format % (i, itype(items[i]), objStr)
         s += format % (i, itype(items[i]), objStr)
     return s
     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__
 import __builtin__
 __builtin__.Functor = Functor
 __builtin__.Functor = Functor
 __builtin__.Stack = Stack
 __builtin__.Stack = Stack