mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
stop using gc.DEBUG_SAVEALL (not necessary), GarbageReport output readability improvements
This commit is contained in:
parent
04ed456dbe
commit
fb1aa31b78
@ -98,10 +98,6 @@ class ConnectionRepository(
|
|||||||
|
|
||||||
self._serverAddress = ''
|
self._serverAddress = ''
|
||||||
|
|
||||||
if self.config.GetBool('want-debug-leak', 1):
|
|
||||||
import gc
|
|
||||||
gc.set_debug(gc.DEBUG_SAVEALL)
|
|
||||||
|
|
||||||
def generateGlobalObject(self, doId, dcname, values=None):
|
def generateGlobalObject(self, doId, dcname, values=None):
|
||||||
def applyFieldValues(distObj, dclass, values):
|
def applyFieldValues(distObj, dclass, values):
|
||||||
for i in range(dclass.getNumInheritedFields()):
|
for i in range(dclass.getNumInheritedFields()):
|
||||||
|
@ -3,14 +3,15 @@
|
|||||||
__all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger']
|
__all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger']
|
||||||
|
|
||||||
from direct.directnotify.DirectNotifyGlobal import directNotify
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
||||||
from direct.showbase.PythonUtil import gcDebugOn, safeRepr, fastRepr, printListEnumGen, printNumberedTypesGen
|
from direct.showbase.PythonUtil import safeRepr, fastRepr, printListEnumGen, printNumberedTypesGen
|
||||||
from direct.showbase.PythonUtil import AlphabetCounter
|
from direct.showbase.PythonUtil import AlphabetCounter
|
||||||
from direct.showbase.Job import Job
|
from direct.showbase.Job import Job
|
||||||
import gc
|
import gc
|
||||||
import types
|
import types
|
||||||
|
|
||||||
class FakeObject:
|
class FakeObject:
|
||||||
pass
|
def __del__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def _createGarbage(num=1):
|
def _createGarbage(num=1):
|
||||||
for i in xrange(num):
|
for i in xrange(num):
|
||||||
@ -46,12 +47,39 @@ class GarbageReport(Job):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# do the garbage collection
|
# do the garbage collection
|
||||||
wasOn = gcDebugOn()
|
|
||||||
oldFlags = gc.get_debug()
|
oldFlags = gc.get_debug()
|
||||||
if not wasOn:
|
|
||||||
gc.set_debug(gc.DEBUG_SAVEALL)
|
# do a collect without SAVEALL, to get the instances that define __del__
|
||||||
|
# cycles that do not involve any instances that define __del__ are cleaned up
|
||||||
|
# automatically by Python, but they also appear in gc.garbage when SAVEALL is set
|
||||||
|
gc.set_debug(0)
|
||||||
gc.collect()
|
gc.collect()
|
||||||
self.garbage = list(gc.garbage)
|
garbageInstances = gc.garbage[:]
|
||||||
|
del gc.garbage[:]
|
||||||
|
# only yield if there's more time-consuming work to do,
|
||||||
|
# if there's no garbage, give instant feedback
|
||||||
|
if len(garbageInstances) > 0:
|
||||||
|
yield None
|
||||||
|
# don't repr the garbage list if we don't have to
|
||||||
|
if self.notify.getDebug():
|
||||||
|
self.notify.debug('garbageInstances == %s' % fastRepr(garbageInstances))
|
||||||
|
|
||||||
|
self.numGarbageInstances = len(garbageInstances)
|
||||||
|
# grab the ids of the garbage instances (objects with __del__)
|
||||||
|
self.garbageInstanceIds = set()
|
||||||
|
for i in xrange(len(garbageInstances)):
|
||||||
|
self.garbageInstanceIds.add(id(garbageInstances[i]))
|
||||||
|
if not (i % 20):
|
||||||
|
yield None
|
||||||
|
# then release the list of instances so that it doesn't interfere with the gc.collect() below
|
||||||
|
del garbageInstances
|
||||||
|
|
||||||
|
# do a SAVEALL pass so that we have all of the objects involved in legitimate garbage cycles
|
||||||
|
# without SAVEALL, gc.garbage only contains objects with __del__ methods
|
||||||
|
gc.set_debug(gc.DEBUG_SAVEALL)
|
||||||
|
gc.collect()
|
||||||
|
self.garbage = gc.garbage[:]
|
||||||
|
del gc.garbage[:]
|
||||||
# only yield if there's more time-consuming work to do,
|
# only yield if there's more time-consuming work to do,
|
||||||
# if there's no garbage, give instant feedback
|
# if there's no garbage, give instant feedback
|
||||||
if len(self.garbage) > 0:
|
if len(self.garbage) > 0:
|
||||||
@ -59,9 +87,7 @@ class GarbageReport(Job):
|
|||||||
# 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' % fastRepr(self.garbage))
|
self.notify.debug('self.garbage == %s' % fastRepr(self.garbage))
|
||||||
del gc.garbage[:]
|
gc.set_debug(oldFlags)
|
||||||
if not wasOn:
|
|
||||||
gc.set_debug(oldFlags)
|
|
||||||
|
|
||||||
self.numGarbage = len(self.garbage)
|
self.numGarbage = len(self.garbage)
|
||||||
# only yield if there's more time-consuming work to do,
|
# only yield if there's more time-consuming work to do,
|
||||||
@ -70,7 +96,7 @@ class GarbageReport(Job):
|
|||||||
yield None
|
yield None
|
||||||
|
|
||||||
if self._args.verbose:
|
if self._args.verbose:
|
||||||
self.notify.info('found %s garbage items' % self.numGarbage)
|
self.notify.info('found %s garbage instances' % self.numGarbageInstances)
|
||||||
|
|
||||||
""" spammy
|
""" spammy
|
||||||
# print the types of the garbage first, in case the repr of an object
|
# print the types of the garbage first, in case the repr of an object
|
||||||
@ -102,7 +128,7 @@ class GarbageReport(Job):
|
|||||||
yield None
|
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.numGarbageInstances != 0):
|
||||||
if self._args.verbose:
|
if self._args.verbose:
|
||||||
self.notify.info('getting referrers...')
|
self.notify.info('getting referrers...')
|
||||||
for i in xrange(self.numGarbage):
|
for i in xrange(self.numGarbage):
|
||||||
@ -114,7 +140,7 @@ class GarbageReport(Job):
|
|||||||
self.referrersByReference[i] = byRef
|
self.referrersByReference[i] = byRef
|
||||||
|
|
||||||
# grab the referents (pointed to by garbage)
|
# grab the referents (pointed to by garbage)
|
||||||
if self.numGarbage > 0:
|
if self.numGarbageInstances > 0:
|
||||||
if self._args.verbose:
|
if self._args.verbose:
|
||||||
self.notify.info('getting referents...')
|
self.notify.info('getting referents...')
|
||||||
for i in xrange(self.numGarbage):
|
for i in xrange(self.numGarbage):
|
||||||
@ -126,7 +152,7 @@ class GarbageReport(Job):
|
|||||||
self.referentsByReference[i] = byRef
|
self.referentsByReference[i] = byRef
|
||||||
|
|
||||||
# find the cycles
|
# find the cycles
|
||||||
if self._args.findCycles and self.numGarbage > 0:
|
if self._args.findCycles and self.numGarbageInstances > 0:
|
||||||
if self._args.verbose:
|
if self._args.verbose:
|
||||||
self.notify.info('detecting cycles...')
|
self.notify.info('detecting cycles...')
|
||||||
for i in xrange(self.numGarbage):
|
for i in xrange(self.numGarbage):
|
||||||
@ -221,12 +247,13 @@ class GarbageReport(Job):
|
|||||||
self.cycleIds.update(set(cycle))
|
self.cycleIds.update(set(cycle))
|
||||||
|
|
||||||
if self._args.findCycles:
|
if self._args.findCycles:
|
||||||
s = ['===== GarbageReport: \'%s\' (%s items, %s cycles) =====' % (
|
s = ['===== GarbageReport: \'%s\' (%s %s) =====' % (
|
||||||
self._args.name, self.numGarbage, len(self.cycles))]
|
self._args.name, len(self.cycles),
|
||||||
|
choice(len(self.cycles) == 1, 'cycle', 'cycles'))]
|
||||||
else:
|
else:
|
||||||
s = ['===== GarbageReport: \'%s\' (%s items) =====' % (
|
s = ['===== GarbageReport: \'%s\' =====' % (
|
||||||
self._args.name, self.numGarbage)]
|
self._args.name)]
|
||||||
if self.numGarbage > 0:
|
if self.numGarbageInstances > 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:
|
||||||
garbageIndices = range(self.numGarbage)
|
garbageIndices = range(self.numGarbage)
|
||||||
@ -277,14 +304,14 @@ class GarbageReport(Job):
|
|||||||
s.append(format % (idx, itype(self.garbage[idx]), objStr))
|
s.append(format % (idx, itype(self.garbage[idx]), objStr))
|
||||||
|
|
||||||
if self._args.findCycles:
|
if self._args.findCycles:
|
||||||
s.append('===== Garbage Cycles By Index =====')
|
s.append('===== Garbage Cycles (Garbage Item Numbers) =====')
|
||||||
ac = AlphabetCounter()
|
ac = AlphabetCounter()
|
||||||
for i in xrange(len(self.cycles)):
|
for i in xrange(len(self.cycles)):
|
||||||
yield None
|
yield None
|
||||||
s.append('%s:%s' % (ac.next(), self.cycles[i]))
|
s.append('%s:%s' % (ac.next(), self.cycles[i]))
|
||||||
|
|
||||||
if self._args.findCycles:
|
if self._args.findCycles:
|
||||||
s.append('===== Garbage Cycles By Python Syntax =====')
|
s.append('===== Garbage Cycles (Python Syntax) =====')
|
||||||
ac = AlphabetCounter()
|
ac = AlphabetCounter()
|
||||||
for i in xrange(len(self.cyclesBySyntax)):
|
for i in xrange(len(self.cyclesBySyntax)):
|
||||||
yield None
|
yield None
|
||||||
@ -314,7 +341,7 @@ class GarbageReport(Job):
|
|||||||
if self._args.log:
|
if self._args.log:
|
||||||
self.printingBegin()
|
self.printingBegin()
|
||||||
for i in xrange(len(self._report)):
|
for i in xrange(len(self._report)):
|
||||||
if self.numGarbage > 0:
|
if self.numGarbageInstances > 0:
|
||||||
yield None
|
yield None
|
||||||
self.notify.info(self._report[i])
|
self.notify.info(self._report[i])
|
||||||
self.printingEnd()
|
self.printingEnd()
|
||||||
@ -418,15 +445,18 @@ class GarbageReport(Job):
|
|||||||
uniqueCycleSets = set()
|
uniqueCycleSets = set()
|
||||||
stateStack = Stack()
|
stateStack = Stack()
|
||||||
rootId = index
|
rootId = index
|
||||||
stateStack.push(([rootId], rootId, 0))
|
# check if the root object is one of the garbage instances (has __del__)
|
||||||
|
objId = id(self.garbage[rootId])
|
||||||
|
numDelInstances = choice(objId in self.garbageInstanceIds, 1, 0)
|
||||||
|
stateStack.push(([rootId], rootId, numDelInstances, 0))
|
||||||
while True:
|
while True:
|
||||||
yield None
|
yield None
|
||||||
if len(stateStack) == 0:
|
if len(stateStack) == 0:
|
||||||
break
|
break
|
||||||
candidateCycle, curId, resumeIndex = stateStack.pop()
|
candidateCycle, curId, numDelInstances, resumeIndex = stateStack.pop()
|
||||||
if self.notify.getDebug():
|
if self.notify.getDebug():
|
||||||
print 'restart: %s root=%s cur=%s resume=%s' % (
|
print 'restart: %s root=%s cur=%s numDelInstances=%s resume=%s' % (
|
||||||
candidateCycle, rootId, curId, resumeIndex)
|
candidateCycle, rootId, curId, numDelInstances, resumeIndex)
|
||||||
for index in xrange(resumeIndex, len(self.referentsByNumber[curId])):
|
for index in xrange(resumeIndex, len(self.referentsByNumber[curId])):
|
||||||
yield None
|
yield None
|
||||||
refId = self.referentsByNumber[curId][index]
|
refId = self.referentsByNumber[curId][index]
|
||||||
@ -437,18 +467,24 @@ class GarbageReport(Job):
|
|||||||
normCandidateCycle = self._getNormalizedCycle(candidateCycle)
|
normCandidateCycle = self._getNormalizedCycle(candidateCycle)
|
||||||
normCandidateCycleTuple = tuple(normCandidateCycle)
|
normCandidateCycleTuple = tuple(normCandidateCycle)
|
||||||
if not normCandidateCycleTuple in uniqueCycleSets:
|
if not normCandidateCycleTuple in uniqueCycleSets:
|
||||||
if self.notify.getDebug():
|
# cycles with no instances that define __del__ will be
|
||||||
print ' FOUND: ', normCandidateCycle + [normCandidateCycle[0],]
|
# cleaned up by Python
|
||||||
cycles.append(normCandidateCycle + [normCandidateCycle[0],])
|
if numDelInstances >= 1:
|
||||||
uniqueCycleSets.add(normCandidateCycleTuple)
|
if self.notify.getDebug():
|
||||||
|
print ' FOUND: ', normCandidateCycle + [normCandidateCycle[0],]
|
||||||
|
cycles.append(normCandidateCycle + [normCandidateCycle[0],])
|
||||||
|
uniqueCycleSets.add(normCandidateCycleTuple)
|
||||||
elif refId in candidateCycle:
|
elif refId in candidateCycle:
|
||||||
pass
|
pass
|
||||||
elif refId is not None:
|
elif refId is not None:
|
||||||
|
# check if this object is one of the garbage instances (has __del__)
|
||||||
|
objId = id(self.garbage[refId])
|
||||||
|
numDelInstances += choice(objId in self.garbageInstanceIds, 1, 0)
|
||||||
# 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
|
||||||
stateStack.push((list(candidateCycle), curId, index+1))
|
stateStack.push((list(candidateCycle), curId, numDelInstances, index+1))
|
||||||
stateStack.push((list(candidateCycle) + [refId], refId, 0))
|
stateStack.push((list(candidateCycle) + [refId], refId, numDelInstances, 0))
|
||||||
break
|
break
|
||||||
yield cycles
|
yield cycles
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# objects that report different types of leaks to the ContainerLeakDetector
|
# objects that report different types of leaks to the ContainerLeakDetector
|
||||||
|
|
||||||
from pandac.PandaModules import *
|
from pandac.PandaModules import *
|
||||||
from direct.showbase.PythonUtil import gcDebugOn
|
|
||||||
import __builtin__, gc
|
import __builtin__, gc
|
||||||
|
|
||||||
class LeakDetector:
|
class LeakDetector:
|
||||||
@ -24,15 +23,12 @@ class GarbageLeakDetector(LeakDetector):
|
|||||||
# are we accumulating Python garbage?
|
# are we accumulating Python garbage?
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
# do a garbage collection
|
# do a garbage collection
|
||||||
wasOn = gcDebugOn()
|
|
||||||
oldFlags = gc.get_debug()
|
oldFlags = gc.get_debug()
|
||||||
if not wasOn:
|
gc.set_debug(0)
|
||||||
gc.set_debug(gc.DEBUG_SAVEALL)
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
numGarbage = len(gc.garbage)
|
numGarbage = len(gc.garbage)
|
||||||
del gc.garbage[:]
|
del gc.garbage[:]
|
||||||
if not wasOn:
|
gc.set_debug(oldFlags)
|
||||||
gc.set_debug(oldFlags)
|
|
||||||
return numGarbage
|
return numGarbage
|
||||||
|
|
||||||
class SceneGraphLeakDetector(LeakDetector):
|
class SceneGraphLeakDetector(LeakDetector):
|
||||||
|
@ -18,7 +18,7 @@ __all__ = ['enumerate', 'unique', 'indent', 'nonRepeatingRandomList',
|
|||||||
'clampScalar', 'weightedChoice', 'randFloat', 'normalDistrib',
|
'clampScalar', 'weightedChoice', 'randFloat', 'normalDistrib',
|
||||||
'weightedRand', 'randUint31', 'randInt32', 'randUint32',
|
'weightedRand', 'randUint31', 'randInt32', 'randUint32',
|
||||||
'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton',
|
'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton',
|
||||||
'SingletonError', 'printListEnum', 'gcDebugOn', 'safeRepr',
|
'SingletonError', 'printListEnum', 'safeRepr',
|
||||||
'fastRepr', 'tagRepr', 'tagWithCaller', 'isDefaultValue', 'set_trace', 'pm',
|
'fastRepr', 'tagRepr', 'tagWithCaller', 'isDefaultValue', 'set_trace', 'pm',
|
||||||
'ScratchPad', 'Sync', 'RefCounter', 'itype', 'getNumberedTypedString',
|
'ScratchPad', 'Sync', 'RefCounter', 'itype', 'getNumberedTypedString',
|
||||||
'getNumberedTypedSortedString', 'getNumberedTypedSortedStringWithReferrers',
|
'getNumberedTypedSortedString', 'getNumberedTypedSortedStringWithReferrers',
|
||||||
@ -2444,10 +2444,6 @@ def printListEnum(l):
|
|||||||
for result in printListEnumGen(l):
|
for result in printListEnumGen(l):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def gcDebugOn():
|
|
||||||
import gc
|
|
||||||
return (gc.get_debug() & gc.DEBUG_SAVEALL) == gc.DEBUG_SAVEALL
|
|
||||||
|
|
||||||
# base class for all Panda C++ objects
|
# base class for all Panda C++ objects
|
||||||
# libdtoolconfig doesn't seem to have this, grab it off of PandaNode
|
# libdtoolconfig doesn't seem to have this, grab it off of PandaNode
|
||||||
dtoolSuperBase = None
|
dtoolSuperBase = None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user