ready for logout garbage detection

This commit is contained in:
Darren Ranalli 2006-05-15 20:11:41 +00:00
parent 2de65f7d85
commit ecb7a2f84e
1 changed files with 235 additions and 51 deletions

View File

@ -1,75 +1,259 @@
from direct.directnotify import DirectNotifyGlobal
from direct.showbase import PythonUtil
from direct.showbase.PythonUtil import POD
import gc
class FakeObject:
pass
def _createGarbage():
a = FakeObject()
b = FakeObject()
a.other = b
b.other = a
class CycleSearchState(POD):
DataSet = {
'curId': None,
'cycleByNumSoFar': [],
'cycleByRefSoFar': [],
'nonGarbageDepth': 0,
}
class GarbageReport:
notify = DirectNotifyGlobal.directNotify.newCategory("GarbageReport")
def __init__(self, name=None):
NotGarbage = 'NG'
def __init__(self, name=None, log=True, fullReport=False, findCycles=True):
# if log is True, GarbageReport will self-destroy after logging
# if false, caller is responsible for calling destroy()
wasOn = PythonUtil.gcDebugOn()
oldFlags = gc.get_debug()
if not wasOn:
gc.set_debug(gc.DEBUG_LEAK)
gc.set_debug(gc.DEBUG_SAVEALL)
gc.collect()
garbage = list(gc.garbage)
self.notify.debug('gc.garbage == %s' % gc.garbage)
self.garbage = list(gc.garbage)
self.notify.debug('self.garbage == %s' % self.garbage)
del gc.garbage[:]
if not wasOn:
gc.set_debug(0)
gc.set_debug(oldFlags)
numGarbage = len(garbage)
# grab the referrers
referrersByReference = {}
referrersByNumber = {}
for i in xrange(numGarbage):
referrers = gc.get_referrers(garbage[i])
referrersByReference[i] = list(referrers)
# look to see if each referrer is another garbage item
referrersByNumber[i] = list()
for referrer in referrers:
try:
num = garbage.index(referrer)
except:
num = 'NG' # Not Garbage
referrersByNumber[i].append(num)
# grab the referents
referentsByReference = {}
referentsByNumber = {}
for i in xrange(numGarbage):
referents = gc.get_referents(garbage[i])
referentsByReference[i] = list(referents)
# look to see if each referent is another garbage item
referentsByNumber[i] = list()
for referent in referents:
try:
num = garbage.index(referent)
except:
num = 'NG' # Not Garbage
referentsByNumber[i].append(num)
self.numGarbage = len(self.garbage)
s = '\n===== GarbageReport: \'%s\' (%s items) =====' % (name, numGarbage)
if numGarbage > 0:
# grab the referrers (pointing to garbage)
self.referrersByReference = {}
self.referrersByNumber = {}
for i in xrange(self.numGarbage):
byNum, byRef = self._getReferrers(self.garbage[i])
self.referrersByNumber[i] = byNum
self.referrersByReference[i] = byRef
# grab the referents (pointed to by garbage)
self.referentsByReference = {}
self.referentsByNumber = {}
for i in xrange(self.numGarbage):
byNum, byRef = self._getReferents(self.garbage[i])
self.referentsByNumber[i] = byNum
self.referentsByReference[i] = byRef
"""
# find the cycles
if findCycles:
self.cycles = self._getCycles()
"""
s = '\n===== GarbageReport: \'%s\' (%s items) =====' % (name, self.numGarbage)
if self.numGarbage > 0:
# log each individual item with a number in front of it
s += '\n\n===== Garbage Items ====='
digits = 0
n = numGarbage
n = self.numGarbage
while n > 0:
digits += 1
n /= 10
format = '\n%0' + '%s' % digits + 'i:%s \t%s'
for i in range(len(garbage)):
s += format % (i, type(garbage[i]), garbage[i])
for i in range(len(self.garbage)):
s += format % (i, type(self.garbage[i]), self.garbage[i])
"""
if findCycles:
format = '\n%s'
s += '\n\n===== Cycles ====='
for cycle in self.cycles:
s += format % cycle
"""
format = '\n%0' + '%s' % digits + 'i:%s'
s += '\n\n===== Referrers By Number (what is referring to garbage item?) NG=NonGarbage ====='
for i in xrange(numGarbage):
s += format % (i, referrersByNumber[i])
s += '\n\n===== Referents By Number (what is garbage item referring to?) NG=NonGarbage ====='
for i in xrange(numGarbage):
s += format % (i, referentsByNumber[i])
s += '\n\n===== Referrers (what is referring to garbage item?) ====='
for i in xrange(numGarbage):
s += format % (i, referrersByReference[i])
s += '\n\n===== Referents (what is garbage item referring to?) ====='
for i in xrange(numGarbage):
s += format % (i, referentsByReference[i])
s += '\n\n===== Referrers By Number (what is referring to garbage item?) ====='
for i in xrange(self.numGarbage):
s += format % (i, self.referrersByNumber[i])
s += '\n\n===== Referents By Number (what is garbage item referring to?) ====='
for i in xrange(self.numGarbage):
s += format % (i, self.referentsByNumber[i])
self.notify.info(s)
if fullReport:
s += '\n\n===== Referrers (what is referring to garbage item?) ====='
for i in xrange(self.numGarbage):
s += format % (i, self.referrersByReference[i])
s += '\n\n===== Referents (what is garbage item referring to?) ====='
for i in xrange(self.numGarbage):
s += format % (i, self.referentsByReference[i])
self._report = s
if log:
self.notify.info(self._report)
self.destroy()
def getNumItems(self):
return self.numGarbage
def getGarbage(self):
return self.garbage
def getReport(self):
return self._report
def destroy(self):
del self.garbage
del self.numGarbage
del self.referrersByReference
del self.referrersByNumber
del self.referentsByReference
del self.referentsByNumber
if hasattr(self, 'cycles'):
del self.cycles
del self._report
def _getReferrers(self, obj):
# referrers (pointing to garbage)
# returns two lists, first by index into gc.garbage, second by
# direct reference
byRef = gc.get_referrers(obj)
# look to see if each referrer is another garbage item
byNum = []
for referrer in byRef:
try:
num = self.garbage.index(referrer)
byNum.append(num)
except:
#num = GarbageReport.NotGarbage
pass
return byNum, byRef
def _getReferents(self, obj):
# referents (pointed to by garbage)
# returns two lists, first by index into gc.garbage, second by
# direct reference
byRef = gc.get_referents(obj)
# look to see if each referent is another garbage item
byNum = []
for referent in byRef:
try:
num = self.garbage.index(referent)
byNum.append(num)
except:
#num = GarbageReport.NotGarbage
pass
return byNum, byRef
def _getCycles(self):
assert self.notify.debugCall()
# returns list of lists, sublists are garbage reference cycles
cycles = []
# dict of already-found cycles as sets so we can avoid duplicate cycle reports
cycleSets = {}
# set of garbage item IDs involved in already-discovered cycles
visited = set()
self.stateStack = Stack()
for rootId in xrange(len(self.garbage)):
cycleSoFar = []
self.stateStack.push((rootId, rootId, cycles, cycleSoFar, cycleSets, visited))
self._findCycles()
self.stateStack.pop()
assert len(self.stateStack) == 0
return cycles
def _findCycles(self):
# rootId: id of first garbage item in potential cycle
# curId: id of current garbage item under examination
# cycles: list of complete cycles we have already found
# cycleSoFar: list of IDs leading from rootId up to curId
# cycleSets: dict of ID to list of cycle ID sets
# visited: set of garbage item IDs from existing (already-found) cycles
assert self.notify.debugCall()
rootId, curId, cycles, cycleSoFar, cycleSets, visited = self.stateStack.top()
cycleSoFar.append(curId)
# make sure this is not a cycle that is already in the list of cycles
if curId in cycleSets:
cycleSet = set(cycleSoFar)
cycleSet.add(curId)
if cycleSet in cycleSets[curId]:
return
for refId in self.referentsByNumber[curId]:
if refId == rootId:
# we found a cycle!
cycle = list(cycleSoFar) + [refId]
cycleSet = set(cycle)
# make sure this is not a duplicate of a previously-found cycle
if cycleSet not in cycleSets.get(rootId, []):
cycles.append(cycle)
for id in cycle:
visited.add(id)
cycleSets.setdefault(id, [])
if cycleSet not in cycleSets[id]:
cycleSets[id].append(cycleSet)
elif refId not in visited and refId not in cycleSoFar and refId != GarbageReport.NotGarbage:
#print rootId, curId, refId, visited, cycles, cycleSoFar, cycleSets
self.stateStack.push((rootId, refId, cycles, list(cycleSoFar), cycleSets, visited))
self._findCycles()
self.stateStack.pop()
"""
def _getCycles(self):
# returns list of lists, sublists are garbage reference cycles by number
cycles = []
for rootId in xrange(len(self.garbage)):
curId = rootId
stateStack = Stack()
initialState = CycleSearchState(curId=curId, cycleByNumSoFar=[rootId],
cycleByRefSoFar=[self.garbage[rootId]])
stateStack.push(initialState)
curState = stateStack.top()
while not stateStack.isEmpty():
for index in xrange(len(self.referentsByNumber[curId])):
refId = self.referentsByNumber[curId][index]
if refId is rootId:
# we found a cycle!
cyclesByNumber.append(curState.cycleByNumSoFar + [refId])
cyclesByReference.append(curState.cycleByRefSoFar + [
def _getCycles(self):
assert self.notify.debugCall()
# returns list of lists, sublists are garbage reference cycles
cycles = []
# dict of already-found cycles as sets so we can avoid duplicate cycle reports
cycleSets = {}
# set of garbage item IDs involved in already-discovered cycles
visited = set()
for rootId in xrange(len(self.garbage)):
for index in xrange(len(self.referentsByNumber[rootId])):
refId = self.referentsByNumber[rootId][index]
if refId is rootId:
# we found a cycle!
cycle = [rootId, rootId]
cycleSet = set(cycle)
# make sure this is not a duplicate of a previously-found cycle
if cycleSet not in cycleSets.get(rootId, []):
cycles.append(cycle)
for id in cycle:
visited.add(id)
cycleSets.setdefault(id, [])
if cycleSet not in cycleSets[id]:
cycleSets[id].append(cycleSet)
elif refId is not GarbageReport.NotGarbage:
cycleSoFar = [rootId]
self._findCycles(rootId, refId, cycles, cycleSoFar, cycleSets, visited)
return cycles
"""