Browse Source

removed linear searches, fixed bugs that prevented some garbage cycles from printing

Darren Ranalli 17 years ago
parent
commit
233501d0a6
1 changed files with 59 additions and 36 deletions
  1. 59 36
      direct/src/showbase/GarbageReport.py

+ 59 - 36
direct/src/showbase/GarbageReport.py

@@ -56,7 +56,7 @@ class GarbageReport(Job):
             yield None
             yield None
         # don't repr the garbage list if we don't have to
         # don't repr the garbage list if we don't have to
         if self.notify.getDebug():
         if self.notify.getDebug():
-            self.notify.debug('self.garbage == %s' % safeRepr(self.garbage))
+            self.notify.debug('self.garbage == %s' % fastRepr(self.garbage))
         del gc.garbage[:]
         del gc.garbage[:]
         if not wasOn:
         if not wasOn:
             gc.set_debug(oldFlags)
             gc.set_debug(oldFlags)
@@ -79,6 +79,9 @@ class GarbageReport(Job):
                 yield None
                 yield None
                 """
                 """
 
 
+        # Py obj id -> garbage list index
+        self._id2index = {}
+
         self.referrersByReference = {}
         self.referrersByReference = {}
         self.referrersByNumber = {}
         self.referrersByNumber = {}
 
 
@@ -86,9 +89,15 @@ class GarbageReport(Job):
         self.referentsByNumber = {}
         self.referentsByNumber = {}
 
 
         self.cycles = []
         self.cycles = []
-        self.cycleSets = []
+        self.uniqueCycleSets = set()
         self.cycleIds = set()
         self.cycleIds = set()
 
 
+        # make the id->index table to speed up the next steps
+        for i in xrange(self.numGarbage):
+            self._id2index[id(self.garbage[i])] = i
+            if not (i % 20):
+                yield None
+
         # grab the referrers (pointing to garbage)
         # grab the referrers (pointing to garbage)
         if self._args.fullReport and (self.numGarbage != 0):
         if self._args.fullReport and (self.numGarbage != 0):
             if self._args.verbose:
             if self._args.verbose:
@@ -119,7 +128,7 @@ class GarbageReport(Job):
                 self.notify.info('detecting cycles...')
                 self.notify.info('detecting cycles...')
             for i in xrange(self.numGarbage):
             for i in xrange(self.numGarbage):
                 yield None
                 yield None
-                for newCycles in self._getCycles(i, self.cycleSets):
+                for newCycles in self._getCycles(i, self.uniqueCycleSets):
                     yield None
                     yield None
                 self.cycles.extend(newCycles)
                 self.cycles.extend(newCycles)
                 # if we're not doing a full report, add this cycle's IDs to the master set
                 # if we're not doing a full report, add this cycle's IDs to the master set
@@ -137,11 +146,11 @@ class GarbageReport(Job):
         if self.numGarbage > 0:
         if self.numGarbage > 0:
             # make a list of the ids we will actually be printing
             # make a list of the ids we will actually be printing
             if self._args.fullReport:
             if self._args.fullReport:
-                garbageIds = range(self.numGarbage)
+                garbageIndices = range(self.numGarbage)
             else:
             else:
-                garbageIds = list(self.cycleIds)
-                garbageIds.sort()
-            numGarbage = len(garbageIds)
+                garbageIndices = list(self.cycleIds)
+                garbageIndices.sort()
+            numGarbage = len(garbageIndices)
 
 
             # log each individual item with a number in front of it
             # log each individual item with a number in front of it
             if not self._args.fullReport:
             if not self._args.fullReport:
@@ -160,17 +169,17 @@ class GarbageReport(Job):
 
 
             for i in xrange(numGarbage):
             for i in xrange(numGarbage):
                 yield None
                 yield None
-                id = garbageIds[i]
+                idx = garbageIndices[i]
                 if self._args.safeMode:
                 if self._args.safeMode:
                     # in safe mode, don't try to repr any of the objects
                     # in safe mode, don't try to repr any of the objects
-                    objStr = repr(itype(self.garbage[id]))
+                    objStr = repr(itype(self.garbage[idx]))
                 else:
                 else:
-                    objStr = safeRepr(self.garbage[id])
+                    objStr = fastRepr(self.garbage[idx])
                 maxLen = 5000
                 maxLen = 5000
                 if len(objStr) > maxLen:
                 if len(objStr) > maxLen:
                     snip = '<SNIP>'
                     snip = '<SNIP>'
                     objStr = '%s%s' % (objStr[:(maxLen-len(snip))], snip)
                     objStr = '%s%s' % (objStr[:(maxLen-len(snip))], snip)
-                s.append(format % (id, itype(self.garbage[id]), objStr))
+                s.append(format % (idx, itype(self.garbage[idx]), objStr))
 
 
             if self._args.findCycles:
             if self._args.findCycles:
                 s.append('===== Garbage Cycles =====')
                 s.append('===== Garbage Cycles =====')
@@ -255,14 +264,12 @@ class GarbageReport(Job):
         yield None
         yield None
         # look to see if each referrer is another garbage item
         # look to see if each referrer is another garbage item
         byNum = []
         byNum = []
-        for referrer in byRef:
-            yield None
-            try:
-                num = self.garbage.index(referrer)
-                byNum.append(num)
-            except:
-                #num = GarbageReport.NotGarbage
-                pass
+        for i in xrange(len(byRef)):
+            if not (i % 20):
+                yield None
+            referrer = byRef[i]
+            num = self._id2index.get(id(referrer), None)
+            byNum.append(num)
         yield byNum, byRef
         yield byNum, byRef
 
 
     def _getReferents(self, obj):
     def _getReferents(self, obj):
@@ -274,24 +281,38 @@ class GarbageReport(Job):
         yield None
         yield None
         # look to see if each referent is another garbage item
         # look to see if each referent is another garbage item
         byNum = []
         byNum = []
-        for referent in byRef:
-            yield None
-            try:
-                num = self.garbage.index(referent)
-                byNum.append(num)
-            except:
-                #num = GarbageReport.NotGarbage
-                pass
+        for i in xrange(len(byRef)):
+            if not (i % 20):
+                yield None
+            referent = byRef[i]
+            num = self._id2index.get(id(referent), None)
+            byNum.append(num)
         yield byNum, byRef
         yield byNum, byRef
 
 
-    def _getCycles(self, index, cycleSets=None):
+    def _getNormalizedCycle(self, cycle):
+        # returns a representation of a cycle (list of indices) that will be
+        # reliably derived from a unique cycle regardless of ordering
+        # this lets us detect duplicate cycles that appear different because of
+        # which element appears first
+        if len(cycle) == 0:
+            return cycle
+        min = 1<<30
+        minIndex = None
+        for i in xrange(len(cycle)):
+            elem = cycle[i]
+            if elem < min:
+                min = elem
+                minIndex = i
+        return cycle[minIndex:] + cycle[:minIndex]
+
+    def _getCycles(self, index, uniqueCycleSets=None):
         # detect garbage cycles for a particular item of garbage
         # detect garbage cycles for a particular item of garbage
         assert self.notify.debugCall()
         assert self.notify.debugCall()
         # returns list of lists, sublists are garbage reference cycles
         # returns list of lists, sublists are garbage reference cycles
         cycles = []
         cycles = []
-        # sets of cycle members, to avoid duplicates
-        if cycleSets is None:
-            cycleSets = []
+        # this lets us eliminate duplicate cycles
+        if uniqueCycleSets is None:
+            uniqueCycleSets = set()
         stateStack = Stack()
         stateStack = Stack()
         rootId = index
         rootId = index
         stateStack.push(([rootId], rootId, 0))
         stateStack.push(([rootId], rootId, 0))
@@ -310,14 +331,16 @@ class GarbageReport(Job):
                     print '       : %s -> %s' % (curId, refId)
                     print '       : %s -> %s' % (curId, refId)
                 if refId == rootId:
                 if refId == rootId:
                     # we found a cycle! mark it down and move on to the next refId
                     # we found a cycle! mark it down and move on to the next refId
-                    if not set(candidateCycle) in cycleSets:
+                    normCandidateCycle = self._getNormalizedCycle(candidateCycle)
+                    normCandidateCycleTuple = tuple(normCandidateCycle)
+                    if not normCandidateCycleTuple in uniqueCycleSets:
                         if self.notify.getDebug():
                         if self.notify.getDebug():
-                            print '  FOUND: ', list(candidateCycle) + [refId]
-                        cycles.append(list(candidateCycle) + [refId])
-                        cycleSets.append(set(candidateCycle))
+                            print '  FOUND: ', normCandidateCycle + [normCandidateCycle[0],]
+                        cycles.append(normCandidateCycle + [normCandidateCycle[0],])
+                        uniqueCycleSets.add(normCandidateCycleTuple)
                 elif refId in candidateCycle:
                 elif refId in candidateCycle:
                     pass
                     pass
-                else:
+                elif refId is not None:
                     # this refId does not complete a cycle. Mark down
                     # this refId does not complete a cycle. Mark down
                     # where we are in this list of referents, then
                     # where we are in this list of referents, then
                     # start looking through the referents of the new refId
                     # start looking through the referents of the new refId