Browse Source

bug fixes, multiple pools of traversal starting points

Darren Ranalli 18 years ago
parent
commit
2144050758
1 changed files with 229 additions and 151 deletions
  1. 229 151
      direct/src/showbase/ContainerLeakDetector.py

+ 229 - 151
direct/src/showbase/ContainerLeakDetector.py

@@ -1,12 +1,12 @@
 from pandac.PandaModules import PStatCollector
 from pandac.PandaModules import PStatCollector
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
-from direct.showbase.PythonUtil import Queue, invertDictLossless
+from direct.showbase.PythonUtil import Queue, invertDictLossless, makeFlywheelGen
 from direct.showbase.PythonUtil import itype, serialNum, safeRepr, fastRepr
 from direct.showbase.PythonUtil import itype, serialNum, safeRepr, fastRepr
 from direct.showbase.Job import Job
 from direct.showbase.Job import Job
 import types, weakref, random, __builtin__
 import types, weakref, random, __builtin__
 
 
 def _createContainerLeak():
 def _createContainerLeak():
-    def leakContainer(task):
+    def leakContainer(task=None):
         base = getBase()
         base = getBase()
         if not hasattr(base, 'leakContainer'):
         if not hasattr(base, 'leakContainer'):
             base.leakContainer = {}
             base.leakContainer = {}
@@ -22,8 +22,10 @@ def _createContainerLeak():
             ContainerLeakDetector.notify.debug(
             ContainerLeakDetector.notify.debug(
                 'removing reference to leakContainer key %s so it will be garbage-collected' % safeRepr(key))
                 'removing reference to leakContainer key %s so it will be garbage-collected' % safeRepr(key))
             del base.leakContainer[key]
             del base.leakContainer[key]
-        return task.cont
-    task = taskMgr.add(leakContainer, 'leakContainer-%s' % serialNum())
+        taskMgr.doMethodLater(10, leakContainer, 'leakContainer-%s' % serialNum())
+        if task:
+            return task.done
+    leakContainer()
 
 
 class NoDictKey:
 class NoDictKey:
     pass
     pass
@@ -133,16 +135,19 @@ class ContainerRef:
     collection of the container if possible
     collection of the container if possible
     stored as a series of 'indirections' (obj.foo -> '.foo', dict[key] -> '[key]', etc.)
     stored as a series of 'indirections' (obj.foo -> '.foo', dict[key] -> '[key]', etc.)
     """
     """
+    notify = directNotify.newCategory("ContainerRef")
+
     class FailedEval(Exception):
     class FailedEval(Exception):
         pass
         pass
 
 
     def __init__(self, indirection, other=None):
     def __init__(self, indirection, other=None):
         self._indirections = []
         self._indirections = []
-        # if no other passed in, try ContainerRef.BaseRef
+        # are we building off of an existing ref?
         if other is not None:
         if other is not None:
             for ind in other._indirections:
             for ind in other._indirections:
                 self.addIndirection(ind)
                 self.addIndirection(ind)
         self.addIndirection(indirection)
         self.addIndirection(indirection)
+        self.notify.debug(repr(self))
 
 
     def destroy(self):
     def destroy(self):
         # re-entrant
         # re-entrant
@@ -154,20 +159,27 @@ class ContainerRef:
         indirection.acquire()
         indirection.acquire()
         self._indirections.append(indirection)
         self._indirections.append(indirection)
 
 
-    def _getContainerByEval(self, evalStr):
+    def getNumIndirections(self):
+        return len(self._indirections)
+
+    def _getContainerByEval(self, evalStr, curObj=None):
+        if curObj is not None:
+            # eval('curObj.foo.bar.someDict')
+            evalStr = 'curObj%s' % evalStr
+        else:
+            # this eval is not based off of curObj, use the global__builtin__ namespace
+            # put __builtin__ at the start if it's not already there
+            bis = '__builtin__'
+            if evalStr[:len(bis)] != bis:
+                evalStr = '%s.%s' % (bis, evalStr)
         try:
         try:
             container = eval(evalStr)
             container = eval(evalStr)
         except NameError, ne:
         except NameError, ne:
             return None
             return None
+        except AttributeError, ne:
+            return None
         return container
         return container
 
 
-    def _evalWithObj(self, evalStr, curObj=None):
-        # eval an evalStr, optionally based off of an existing object
-        if curObj is not None:
-            # eval('curObj.foo.bar.someDict')
-            evalStr = 'curObj%s' % evalStr
-        return self._getContainerByEval(evalStr)
-
     def getContainer(self):
     def getContainer(self):
         # try to get a handle on the container by eval'ing and looking things
         # try to get a handle on the container by eval'ing and looking things
         # up in dictionaries, depending on the type of each indirection
         # up in dictionaries, depending on the type of each indirection
@@ -184,7 +196,7 @@ class ContainerRef:
                 # build up a string to be eval'd
                 # build up a string to be eval'd
                 evalStr += indirection.getString()
                 evalStr += indirection.getString()
             else:
             else:
-                curObj = self._evalWithObj(evalStr, curObj)
+                curObj = self._getContainerByEval(evalStr, curObj=curObj)
                 if curObj is None:
                 if curObj is None:
                     raise FailedEval(evalStr)
                     raise FailedEval(evalStr)
                 # try to look up this key in the curObj dictionary
                 # try to look up this key in the curObj dictionary
@@ -194,7 +206,8 @@ class ContainerRef:
             yield None
             yield None
             indirection.release()
             indirection.release()
 
 
-        yield self._evalWithObj(evalStr, curObj)
+        # TODO: check that this is still the object we originally pointed to
+        yield self._getContainerByEval(evalStr, curObj=curObj)
         
         
     def getNameGen(self):
     def getNameGen(self):
         str = ''
         str = ''
@@ -236,38 +249,57 @@ class FindContainers(Job):
         Job.__init__(self, name)
         Job.__init__(self, name)
         self._leakDetector = leakDetector
         self._leakDetector = leakDetector
         self._id2ref = self._leakDetector._id2ref
         self._id2ref = self._leakDetector._id2ref
+        # these hold objects that we should start traversals from often and not-as-often,
+        # respectively
+        self._id2baseStartRef = {}
+        self._id2discoveredStartRef = {}
+        # these are working copies so that our iterations aren't disturbed by changes to the
+        # definitive ref sets
+        self._baseStartRefWorkingList = ScratchPad(refGen=nullGen(),
+                                                   source=self._id2baseStartRef)
+        self._discoveredStartRefWorkingList = ScratchPad(refGen=nullGen(),
+                                                         source=self._id2discoveredStartRef)
         self.notify = self._leakDetector.notify
         self.notify = self._leakDetector.notify
         ContainerLeakDetector.addPrivateObj(self.__dict__)
         ContainerLeakDetector.addPrivateObj(self.__dict__)
 
 
-        # set up the base/starting object
-        self._baseObjRef = ContainerRef(Indirection(evalStr='__builtin__.__dict__'))
-        for i in self._nameContainerGen(__builtin__.__dict__, self._baseObjRef):
+        # set up the base containers, the ones that hold most objects
+        ref = ContainerRef(Indirection(evalStr='__builtin__.__dict__'))
+        self._id2baseStartRef[id(__builtin__.__dict__)] = ref
+        for i in self._addContainerGen(__builtin__.__dict__, ref):
             pass
             pass
         try:
         try:
             base
             base
         except:
         except:
             pass
             pass
         else:
         else:
-            self._baseObjRef = ContainerRef(Indirection(evalStr='base.__dict__'))
-            for i in self._nameContainerGen(base.__dict__, self._baseObjRef):
+            ref = ContainerRef(Indirection(evalStr='base.__dict__'))
+            self._id2baseStartRef[id(base.__dict__)] = ref
+            for i in self._addContainerGen(base.__dict__, ref):
                 pass
                 pass
         try:
         try:
             simbase
             simbase
         except:
         except:
             pass
             pass
         else:
         else:
-            self._baseObjRef = ContainerRef(Indirection(evalStr='simbase.__dict__'))
-            for i in self._nameContainerGen(simbase.__dict__, self._baseObjRef):
+            ref = ContainerRef(Indirection(evalStr='simbase.__dict__'))
+            self._id2baseStartRef[id(simbase.__dict__)] = ref
+            for i in self._addContainerGen(simbase.__dict__, ref):
                 pass
                 pass
 
 
-        self._curObjRef = self._baseObjRef
-
     def destroy(self):
     def destroy(self):
         ContainerLeakDetector.removePrivateObj(self.__dict__)
         ContainerLeakDetector.removePrivateObj(self.__dict__)
         Job.destroy(self)
         Job.destroy(self)
 
 
     def getPriority(self):
     def getPriority(self):
         return Job.Priorities.Low
         return Job.Priorities.Low
+
+    @staticmethod
+    def getStartObjAffinity(startObj):
+        # how good of a starting object is this object for traversing the object graph?
+        try:
+            return len(startObj)
+        except:
+            return 1
     
     
     def _isDeadEnd(self, obj, objName=None):
     def _isDeadEnd(self, obj, objName=None):
         if type(obj) in (types.BooleanType, types.BuiltinFunctionType,
         if type(obj) in (types.BooleanType, types.BuiltinFunctionType,
@@ -293,19 +325,14 @@ class FindContainers(Job):
                 return True
                 return True
         return False
         return False
 
 
-    def _isContainer(self, obj):
+    def _hasLength(self, obj):
         try:
         try:
             len(obj)
             len(obj)
         except:
         except:
             return False
             return False
         return True
         return True
 
 
-    def _nameContainerGen(self, cont, objRef):
-        """
-        if self.notify.getDebug():
-            self.notify.debug('_nameContainer: %s' % objRef)
-            #printStack()
-            """
+    def _addContainerGen(self, cont, objRef):
         contId = id(cont)
         contId = id(cont)
         # if this container is new, or the objRef repr is shorter than what we already have,
         # if this container is new, or the objRef repr is shorter than what we already have,
         # put it in the table
         # put it in the table
@@ -319,44 +346,111 @@ class FindContainers(Job):
                 self._leakDetector.removeContainerById(contId)
                 self._leakDetector.removeContainerById(contId)
             self._id2ref[contId] = objRef
             self._id2ref[contId] = objRef
 
 
+    def _addDiscoveredStartRef(self, obj, ref):
+        # we've discovered an object that can be used to start an object graph traversal
+        objId = id(obj)
+        if objId in self._id2discoveredStartRef:
+            existingRef = self._id2discoveredStartRef[objId]
+            if type(existingRef) not in (types.IntType, types.LongType):
+                if (existingRef.getNumIndirections() >=
+                    ref.getNumIndirections()):
+                    # the ref that we already have is more concise than the new ref
+                    return
+        if objId in self._id2ref:
+            if (self._id2ref[objId].getNumIndirections() >=
+                ref.getNumIndirections()):
+                # the ref that we already have is more concise than the new ref
+                return
+        storedItem = ref
+        # if we already are storing a reference to this object, don't store a second reference
+        if objId in self._id2ref:
+            storedItem = objId
+        self._id2discoveredStartRef[objId] = storedItem
+
     def run(self):
     def run(self):
         try:
         try:
+            # this toggles between the sets of start refs every time we start a new traversal
+            workingListSelector = loopGen([self._baseStartRefWorkingList,
+                                           self._discoveredStartRefWorkingList])
+            # this holds the current step of the current traversal
+            curObjRef = None
             while True:
             while True:
                 # yield up here instead of at the end, since we skip back to the
                 # yield up here instead of at the end, since we skip back to the
                 # top of the while loop from various points
                 # top of the while loop from various points
                 yield None
                 yield None
                 #import pdb;pdb.set_trace()
                 #import pdb;pdb.set_trace()
-                curObj = None
-                if self._curObjRef is None:
-                    self._curObjRef = self._baseObjRef
+                if curObjRef is None:
+                    # choose an object to start a traversal from
+                    startRefWorkingList = workingListSelector.next()
+
+                    # grab the next start ref from this sequence and see if it's still valid
+                    while True:
+                        yield None
+                        try:
+                            curObjRef = startRefWorkingList.refGen.next()
+                            break
+                        except StopIteration:
+                            # we've run out of refs, grab a new set
+                            if len(startRefWorkingList.source) == 0:
+                                # ref set is empty, choose another
+                                break
+                            # make a generator that yields containers a # of times that is
+                            # proportional to their length
+                            for flywheel in makeFlywheelGen(
+                                startRefWorkingList.source.values(),
+                                countFunc=lambda x: self.getStartObjAffinity(x),
+                                scale=.05):
+                                yield None
+                            startRefWorkingList.refGen = flywheel
+                    if curObjRef is None:
+                        # this ref set is empty, choose another
+                        # the base set should never be empty (__builtin__ etc.)
+                        continue
+                    # do we need to go look up the object in _id2ref? sometimes we do that
+                    # to avoid storing multiple redundant refs to a single item
+                    if type(curObjRef) in (types.IntType, types.LongType):
+                        startId = curObjRef
+                        curObjRef = None
+                        try:
+                            for containerRef in self._leakDetector.getContainerByIdGen(startId):
+                                yield None
+                        except:
+                            # ref is invalid
+                            self.notify.debug('invalid startRef, stored as id %s' % startId)
+                            self._leakDetector.removeContainerById(startId)
+                            continue
+                        curObjRef = containerRef
+
                 try:
                 try:
-                    for result in self._curObjRef.getContainer():
+                    for curObj in curObjRef.getContainer():
                         yield None
                         yield None
-                    curObj = result
                 except:
                 except:
-                    self.notify.debug('lost current container: %s' % self._curObjRef)
+                    self.notify.debug('lost current container, ref.getContainer() failed')
                     # that container is gone, try again
                     # that container is gone, try again
-                    self._curObjRef = None
+                    curObjRef = None
                     continue
                     continue
-                self.notify.debug('--> %s' % self._curObjRef)
 
 
-                # keep a copy of this obj's eval str, it might not be in _id2ref
-                curObjRef = self._curObjRef
-                # if we hit a dead end, start over at a container we know about
-                self._curObjRef = None
+                self.notify.debug('--> %s' % curObjRef)
+                #import pdb;pdb.set_trace()
+
+                # store a copy of the current objRef
+                parentObjRef = curObjRef
+                # if we hit a dead end, start over from another container
+                curObjRef = None
 
 
                 if type(curObj) in (types.ModuleType, types.InstanceType):
                 if type(curObj) in (types.ModuleType, types.InstanceType):
                     child = curObj.__dict__
                     child = curObj.__dict__
-                    isContainer = self._isContainer(child)
+                    hasLength = self._hasLength(child)
                     notDeadEnd = not self._isDeadEnd(child)
                     notDeadEnd = not self._isDeadEnd(child)
-                    if isContainer or notDeadEnd:
-                        objRef = ContainerRef(Indirection(evalStr='.__dict__'), curObjRef)
+                    if hasLength or notDeadEnd:
+                        objRef = ContainerRef(Indirection(evalStr='.__dict__'), parentObjRef)
                         yield None
                         yield None
-                        if isContainer:
-                            for i in self._nameContainerGen(child, objRef):
+                        if hasLength:
+                            for i in self._addContainerGen(child, objRef):
                                 yield None
                                 yield None
                         if notDeadEnd:
                         if notDeadEnd:
-                            self._curObjRef = objRef
+                            self._addDiscoveredStartRef(child, objRef)
+                            curObjRef = objRef
                     continue
                     continue
 
 
                 if type(curObj) is types.DictType:
                 if type(curObj) is types.DictType:
@@ -365,47 +459,46 @@ class FindContainers(Job):
                     keys = curObj.keys()
                     keys = curObj.keys()
                     # we will continue traversing the object graph via one key of the dict,
                     # we will continue traversing the object graph via one key of the dict,
                     # choose it at random without taking a big chunk of CPU time
                     # choose it at random without taking a big chunk of CPU time
-                    numKeysLeft = len(keys)
-                    nextObjRef = None
+                    numKeysLeft = len(keys) + 1
                     for key in keys:
                     for key in keys:
                         yield None
                         yield None
+                        numKeysLeft -= 1
                         try:
                         try:
                             attr = curObj[key]
                             attr = curObj[key]
                         except KeyError, e:
                         except KeyError, e:
                             # this is OK because we are yielding during the iteration
                             # this is OK because we are yielding during the iteration
-                            self.notify.debug('could not index into %s with key %s' % (curObjRef, safeRepr(key)))
+                            self.notify.debug('could not index into %s with key %s' % (
+                                parentObjRef, safeRepr(key)))
                             continue
                             continue
-                        isContainer = self._isContainer(attr)
+                        hasLength = self._hasLength(attr)
                         notDeadEnd = False
                         notDeadEnd = False
-                        if nextObjRef is None:
+                        # if we haven't picked the next ref, check if this one is a candidate
+                        if curObjRef is None:
                             notDeadEnd = not self._isDeadEnd(attr, key)
                             notDeadEnd = not self._isDeadEnd(attr, key)
-                        if isContainer or notDeadEnd:
+                        if hasLength or notDeadEnd:
                             if curObj is __builtin__.__dict__:
                             if curObj is __builtin__.__dict__:
-                                objRef = ContainerRef(Indirection(evalStr=key))
+                                objRef = ContainerRef(Indirection(evalStr='%s' % key))
                             else:
                             else:
-                                objRef = ContainerRef(Indirection(dictKey=key), curObjRef)
+                                objRef = ContainerRef(Indirection(dictKey=key), parentObjRef)
                             yield None
                             yield None
-                            if isContainer:
-                                for i in self._nameContainerGen(attr, objRef):
+                            if hasLength:
+                                for i in self._addContainerGen(attr, objRef):
                                     yield None
                                     yield None
-                            if notDeadEnd and nextObjRef is None:
-                                if random.randrange(numKeysLeft) == 0:
-                                    nextObjRef = objRef
-                            numKeysLeft -= 1
-                    if nextObjRef is not None:
-                        self._curObjRef = nextObjRef
+                            if notDeadEnd:
+                                self._addDiscoveredStartRef(attr, objRef)
+                                if curObjRef is None and random.randrange(numKeysLeft) == 0:
+                                    curObjRef = objRef
                     del key
                     del key
                     del attr
                     del attr
                     continue
                     continue
 
 
-                if type(curObj) is not types.FileType:
                     try:
                     try:
-                        itr = iter(curObj)
+                        childNames = dir(curObj)
                     except:
                     except:
                         pass
                         pass
                     else:
                     else:
                         try:
                         try:
-                            index = 0
+                            index = -1
                             attrs = []
                             attrs = []
                             while 1:
                             while 1:
                                 yield None
                                 yield None
@@ -418,72 +511,38 @@ class FindContainers(Job):
                                 attrs.append(attr)
                                 attrs.append(attr)
                             # we will continue traversing the object graph via one attr,
                             # we will continue traversing the object graph via one attr,
                             # choose it at random without taking a big chunk of CPU time
                             # choose it at random without taking a big chunk of CPU time
-                            numAttrsLeft = len(attrs)
-                            nextObjRef = None
+                            numAttrsLeft = len(attrs) + 1
                             for attr in attrs:
                             for attr in attrs:
                                 yield None
                                 yield None
-                                isContainer = self._isContainer(attr)
+                                index += 1
+                                numAttrsLeft -= 1
+                                hasLength = self._hasLength(attr)
                                 notDeadEnd = False
                                 notDeadEnd = False
-                                if nextObjRef is None:
+                                if curObjRef is None:
                                     notDeadEnd = not self._isDeadEnd(attr)
                                     notDeadEnd = not self._isDeadEnd(attr)
-                                if isContainer or notDeadEnd:
-                                    objRef = ContainerRef(Indirection(evalStr='[%s]' % index), curObjRef)
+                                if hasLength or notDeadEnd:
+                                    objRef = ContainerRef(Indirection(evalStr='[%s]' % index),
+                                                          parentObjRef)
                                     yield None
                                     yield None
-                                    if isContainer:
-                                        for i in self._nameContainerGen(attr, objRef):
+                                    if hasLength:
+                                        for i in self._addContainerGen(attr, objRef):
                                             yield None
                                             yield None
-                                    if notDeadEnd and nextObjRef is None:
-                                        if random.randrange(numAttrsLeft) == 0:
-                                            nextObjRef = objRef
-                                numAttrsLeft -= 1
-                                index += 1
-                            if nextObjRef is not None:
-                                self._curObjRef = nextObjRef
+                                    if notDeadEnd:
+                                        self._addDiscoveredStartRef(attr, objRef)
+                                        if curObjRef is None and random.randrange(numAttrsLeft) == 0:
+                                            curObjRef = objRef
                             del attr
                             del attr
                         except StopIteration, e:
                         except StopIteration, e:
                             pass
                             pass
                         del itr
                         del itr
                         continue
                         continue
 
 
-                try:
-                    childNames = dir(curObj)
-                except:
-                    pass
-                else:
-                    childName = None
-                    child = None
-                    # we will continue traversing the object graph via one child,
-                    # choose it at random without taking a big chunk of CPU time
-                    numChildrenLeft = len(childNames)
-                    nextObjRef = None
-                    for childName in childNames:
-                        yield None
-                        child = getattr(curObj, childName)
-                        isContainer = self._isContainer(child)
-                        notDeadEnd = False
-                        if nextObjRef is None:
-                            notDeadEnd = not self._isDeadEnd(child, childName)
-                        if isContainer or notDeadEnd:
-                            objRef = ContainerRef(Indirection(evalStr='.%s' % childName), curObjRef)
-                            yield None
-                            if isContainer:
-                                for i in self._nameContainerGen(child, objRef):
-                                    yield None
-                            if notDeadEnd and nextObjRef is None:
-                                if random.randrange(numChildrenLeft) == 0:
-                                    nextObjRef = objRef
-                        numChildrenLeft -= 1
-                    if nextObjRef is not None:
-                        self._curObjRef = nextObjRef
-                    del childName
-                    del child
-                    continue
         except Exception, e:
         except Exception, e:
             print 'FindContainers job caught exception: %s' % e
             print 'FindContainers job caught exception: %s' % e
             if __dev__:
             if __dev__:
                 #raise e
                 #raise e
                 pass
                 pass
-            yield Job.done
+        yield Job.Done
 
 
 class CheckContainers(Job):
 class CheckContainers(Job):
     """
     """
@@ -510,60 +569,60 @@ class CheckContainers(Job):
             self._leakDetector._index2containerId2len[self._index] = {}
             self._leakDetector._index2containerId2len[self._index] = {}
             ids = self._leakDetector.getContainerIds()
             ids = self._leakDetector.getContainerIds()
             # record the current len of each container
             # record the current len of each container
-            for id in ids:
+            for objId in ids:
                 yield None
                 yield None
                 try:
                 try:
-                    for result in self._leakDetector.getContainerByIdGen(id):
+                    for result in self._leakDetector.getContainerByIdGen(objId):
                         yield None
                         yield None
                     container = result
                     container = result
                 except Exception, e:
                 except Exception, e:
                     # this container no longer exists
                     # this container no longer exists
                     if self.notify.getDebug():
                     if self.notify.getDebug():
-                        for contName in self._leakDetector.getContainerNameByIdGen(id):
+                        for contName in self._leakDetector.getContainerNameByIdGen(objId):
                             yield None
                             yield None
                         self.notify.debug(
                         self.notify.debug(
                             '%s no longer exists; caught exception in getContainerById (%s)' % (
                             '%s no longer exists; caught exception in getContainerById (%s)' % (
                             contName, e))
                             contName, e))
-                    self._leakDetector.removeContainerById(id)
+                    self._leakDetector.removeContainerById(objId)
                     continue
                     continue
                 if container is None:
                 if container is None:
                     # this container no longer exists
                     # this container no longer exists
                     if self.notify.getDebug():
                     if self.notify.getDebug():
-                        for contName in self._leakDetector.getContainerNameByIdGen(id):
+                        for contName in self._leakDetector.getContainerNameByIdGen(objId):
                             yield None
                             yield None
                         self.notify.debug('%s no longer exists; getContainerById returned None' %
                         self.notify.debug('%s no longer exists; getContainerById returned None' %
                                           contName)
                                           contName)
-                    self._leakDetector.removeContainerById(id)
+                    self._leakDetector.removeContainerById(objId)
                     continue
                     continue
                 try:
                 try:
                     cLen = len(container)
                     cLen = len(container)
                 except Exception, e:
                 except Exception, e:
                     # this container no longer exists
                     # this container no longer exists
                     if self.notify.getDebug():
                     if self.notify.getDebug():
-                        for contName in self._leakDetector.getContainerNameByIdGen(id):
+                        for contName in self._leakDetector.getContainerNameByIdGen(objId):
                             yield None
                             yield None
                         self.notify.debug(
                         self.notify.debug(
                             '%s is no longer a container, it is now %s (%s)' %
                             '%s is no longer a container, it is now %s (%s)' %
                             (contName, safeRepr(container), e))
                             (contName, safeRepr(container), e))
-                    self._leakDetector.removeContainerById(id)
+                    self._leakDetector.removeContainerById(objId)
                     continue
                     continue
-                self._leakDetector._index2containerId2len[self._index][id] = cLen
+                self._leakDetector._index2containerId2len[self._index][objId] = cLen
             # compare the current len of each container to past lens
             # compare the current len of each container to past lens
             if self._index > 0:
             if self._index > 0:
                 idx2id2len = self._leakDetector._index2containerId2len
                 idx2id2len = self._leakDetector._index2containerId2len
-                for id in idx2id2len[self._index]:
+                for objId in idx2id2len[self._index]:
                     yield None
                     yield None
-                    if id in idx2id2len[self._index-1]:
-                        diff = idx2id2len[self._index][id] - idx2id2len[self._index-1][id]
+                    if objId in idx2id2len[self._index-1]:
+                        diff = idx2id2len[self._index][objId] - idx2id2len[self._index-1][objId]
                         if diff > 0:
                         if diff > 0:
-                            if diff > idx2id2len[self._index-1][id]:
+                            if diff > idx2id2len[self._index-1][objId]:
                                 minutes = (self._leakDetector._index2delay[self._index] -
                                 minutes = (self._leakDetector._index2delay[self._index] -
                                            self._leakDetector._index2delay[self._index-1]) / 60.
                                            self._leakDetector._index2delay[self._index-1]) / 60.
-                                name = self._leakDetector.getContainerNameById(id)
-                                if idx2id2len[self._index-1][id] != 0:
-                                    percent = 100. * (float(diff) / float(idx2id2len[self._index-1][id]))
+                                name = self._leakDetector.getContainerNameById(objId)
+                                if idx2id2len[self._index-1][objId] != 0:
+                                    percent = 100. * (float(diff) / float(idx2id2len[self._index-1][objId]))
                                     try:
                                     try:
-                                        for container in self._leakDetector.getContainerByIdGen(id):
+                                        for container in self._leakDetector.getContainerByIdGen(objId):
                                             yield None
                                             yield None
                                     except:
                                     except:
                                         # TODO
                                         # TODO
@@ -571,19 +630,19 @@ class CheckContainers(Job):
                                     else:
                                     else:
                                         self.notify.warning(
                                         self.notify.warning(
                                             '%s (%s) grew %.2f%% in %.2f minutes (%s items at last measurement, current contents: %s)' % (
                                             '%s (%s) grew %.2f%% in %.2f minutes (%s items at last measurement, current contents: %s)' % (
-                                            name, itype(container), percent, minutes, idx2id2len[self._index][id],
+                                            name, itype(container), percent, minutes, idx2id2len[self._index][objId],
                                             fastRepr(container, maxLen=CheckContainers.ReprItems)))
                                             fastRepr(container, maxLen=CheckContainers.ReprItems)))
                                     yield None
                                     yield None
                         if (self._index > 2 and
                         if (self._index > 2 and
-                            id in idx2id2len[self._index-2] and
-                            id in idx2id2len[self._index-3]):
-                            diff2 = idx2id2len[self._index-1][id] - idx2id2len[self._index-2][id]
-                            diff3 = idx2id2len[self._index-2][id] - idx2id2len[self._index-3][id]
+                            objId in idx2id2len[self._index-2] and
+                            objId in idx2id2len[self._index-3]):
+                            diff2 = idx2id2len[self._index-1][objId] - idx2id2len[self._index-2][objId]
+                            diff3 = idx2id2len[self._index-2][objId] - idx2id2len[self._index-3][objId]
                             if self._index <= 4:
                             if self._index <= 4:
                                 if diff > 0 and diff2 > 0 and diff3 > 0:
                                 if diff > 0 and diff2 > 0 and diff3 > 0:
-                                    name = self._leakDetector.getContainerNameById(id)
+                                    name = self._leakDetector.getContainerNameById(objId)
                                     try:
                                     try:
-                                        for container in self._leakDetector.getContainerByIdGen(id):
+                                        for container in self._leakDetector.getContainerByIdGen(objId):
                                             yield None
                                             yield None
                                     except:
                                     except:
                                         # TODO
                                         # TODO
@@ -591,20 +650,20 @@ class CheckContainers(Job):
                                     else:
                                     else:
                                         msg = ('%s (%s) consistently increased in size over the last '
                                         msg = ('%s (%s) consistently increased in size over the last '
                                                '3 periods (%s items at last measurement, current contents: %s)' %
                                                '3 periods (%s items at last measurement, current contents: %s)' %
-                                               (name, itype(container), idx2id2len[self._index][id],
+                                               (name, itype(container), idx2id2len[self._index][objId],
                                                 fastRepr(container, maxLen=CheckContainers.ReprItems)))
                                                 fastRepr(container, maxLen=CheckContainers.ReprItems)))
                                         self.notify.warning(msg)
                                         self.notify.warning(msg)
                                     yield None
                                     yield None
-                            elif (id in idx2id2len[self._index-4] and
-                                  id in idx2id2len[self._index-5]):
+                            elif (objId in idx2id2len[self._index-4] and
+                                  objId in idx2id2len[self._index-5]):
                                 # if size has consistently increased over the last 5 checks,
                                 # if size has consistently increased over the last 5 checks,
                                 # send out a warning
                                 # send out a warning
-                                diff4 = idx2id2len[self._index-3][id] - idx2id2len[self._index-4][id]
-                                diff5 = idx2id2len[self._index-4][id] - idx2id2len[self._index-5][id]
+                                diff4 = idx2id2len[self._index-3][objId] - idx2id2len[self._index-4][objId]
+                                diff5 = idx2id2len[self._index-4][objId] - idx2id2len[self._index-5][objId]
                                 if diff > 0 and diff2 > 0 and diff3 > 0 and diff4 > 0 and diff5 > 0:
                                 if diff > 0 and diff2 > 0 and diff3 > 0 and diff4 > 0 and diff5 > 0:
-                                    name = self._leakDetector.getContainerNameById(id)
+                                    name = self._leakDetector.getContainerNameById(objId)
                                     try:
                                     try:
-                                        for container in self._leakDetector.getContainerByIdGen(id):
+                                        for container in self._leakDetector.getContainerByIdGen(objId):
                                             yield None
                                             yield None
                                     except:
                                     except:
                                         # TODO
                                         # TODO
@@ -612,7 +671,7 @@ class CheckContainers(Job):
                                     else:
                                     else:
                                         msg = ('%s (%s) consistently increased in size over the last '
                                         msg = ('%s (%s) consistently increased in size over the last '
                                                '5 periods (%s items at last measurement, current contents: %s)' %
                                                '5 periods (%s items at last measurement, current contents: %s)' %
-                                               (name, itype(container), idx2id2len[self._index][id],
+                                               (name, itype(container), idx2id2len[self._index][objId],
                                                 fastRepr(container, maxLen=CheckContainers.ReprItems)))
                                                 fastRepr(container, maxLen=CheckContainers.ReprItems)))
                                         self.notify.warning(msg)
                                         self.notify.warning(msg)
                                         self.notify.info('sending notification...')
                                         self.notify.info('sending notification...')
@@ -649,12 +708,31 @@ class PruneContainerRefs(Job):
             for id in ids:
             for id in ids:
                 yield None
                 yield None
                 try:
                 try:
-                    for result in self._leakDetector.getContainerByIdGen(id):
+                    for container in self._leakDetector.getContainerByIdGen(id):
                         yield None
                         yield None
-                    container = result
                 except:
                 except:
                     # reference is invalid, remove it
                     # reference is invalid, remove it
                     self._leakDetector.removeContainerById(id)
                     self._leakDetector.removeContainerById(id)
+            _id2baseStartRef = self._leakDetector._findContainersJob._id2baseStartRef
+            ids = _id2baseStartRef.keys()
+            for id in ids:
+                yield None
+                try:
+                    for container in _id2baseStartRef[id].getContainer():
+                        yield None
+                except:
+                    # reference is invalid, remove it
+                    del _id2baseStartRef[id]
+            _id2discoveredStartRef = self._leakDetector._findContainersJob._id2discoveredStartRef
+            ids = _id2discoveredStartRef.keys()
+            for id in ids:
+                yield None
+                try:
+                    for container in _id2discoveredStartRef[id].getContainer():
+                        yield None
+                except:
+                    # reference is invalid, remove it
+                    del _id2discoveredStartRef[id]
         except Exception, e:
         except Exception, e:
             print 'PruneContainerRefs job caught exception: %s' % e
             print 'PruneContainerRefs job caught exception: %s' % e
             if __dev__:
             if __dev__: