From 233501d0a6e3d831ba670b198f335bccbf84ffb5 Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Mon, 28 Jul 2008 19:37:27 +0000 Subject: [PATCH] removed linear searches, fixed bugs that prevented some garbage cycles from printing --- direct/src/showbase/GarbageReport.py | 95 +++++++++++++++++----------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py index cafb737153..499012b5da 100755 --- a/direct/src/showbase/GarbageReport.py +++ b/direct/src/showbase/GarbageReport.py @@ -56,7 +56,7 @@ class GarbageReport(Job): yield None # don't repr the garbage list if we don't have to 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[:] if not wasOn: gc.set_debug(oldFlags) @@ -79,6 +79,9 @@ class GarbageReport(Job): yield None """ + # Py obj id -> garbage list index + self._id2index = {} + self.referrersByReference = {} self.referrersByNumber = {} @@ -86,9 +89,15 @@ class GarbageReport(Job): self.referentsByNumber = {} self.cycles = [] - self.cycleSets = [] + self.uniqueCycleSets = 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) if self._args.fullReport and (self.numGarbage != 0): if self._args.verbose: @@ -119,7 +128,7 @@ class GarbageReport(Job): self.notify.info('detecting cycles...') for i in xrange(self.numGarbage): yield None - for newCycles in self._getCycles(i, self.cycleSets): + for newCycles in self._getCycles(i, self.uniqueCycleSets): yield None self.cycles.extend(newCycles) # 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: # make a list of the ids we will actually be printing if self._args.fullReport: - garbageIds = range(self.numGarbage) + garbageIndices = range(self.numGarbage) 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 if not self._args.fullReport: @@ -160,17 +169,17 @@ class GarbageReport(Job): for i in xrange(numGarbage): yield None - id = garbageIds[i] + idx = garbageIndices[i] if self._args.safeMode: # 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: - objStr = safeRepr(self.garbage[id]) + objStr = fastRepr(self.garbage[idx]) maxLen = 5000 if len(objStr) > maxLen: 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: s.append('===== Garbage Cycles =====') @@ -255,14 +264,12 @@ class GarbageReport(Job): yield None # look to see if each referrer is another garbage item 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 def _getReferents(self, obj): @@ -274,24 +281,38 @@ class GarbageReport(Job): yield None # look to see if each referent is another garbage item 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 - 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 assert self.notify.debugCall() # returns list of lists, sublists are garbage reference 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() rootId = index stateStack.push(([rootId], rootId, 0)) @@ -310,14 +331,16 @@ class GarbageReport(Job): print ' : %s -> %s' % (curId, refId) if refId == rootId: # 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(): - 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: pass - else: + elif refId is not None: # this refId does not complete a cycle. Mark down # where we are in this list of referents, then # start looking through the referents of the new refId