added 'safe' mode to GarbageReport for dealing with C++ crashes in __repr__

This commit is contained in:
Darren Ranalli 2007-11-13 21:23:28 +00:00
parent 7f31a5fb83
commit 8127bcb6b3
2 changed files with 49 additions and 14 deletions

View File

@ -3,7 +3,7 @@
__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 from direct.showbase.PythonUtil import gcDebugOn, safeRepr, fastRepr, printListEnumGen, printNumberedTypesGen
from direct.showbase.Job import Job from direct.showbase.Job import Job
import gc import gc
@ -26,7 +26,8 @@ class GarbageReport(Job):
NotGarbage = 'NG' NotGarbage = 'NG'
def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True, def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True,
threaded=False, doneCallback=None, autoDestroy=False, priority=None): threaded=False, doneCallback=None, autoDestroy=False, priority=None,
safeMode=False):
# if autoDestroy is True, GarbageReport will self-destroy after logging # if autoDestroy is True, GarbageReport will self-destroy after logging
# if false, caller is responsible for calling destroy() # if false, caller is responsible for calling destroy()
# if threaded is True, processing will be performed over multiple frames # if threaded is True, processing will be performed over multiple frames
@ -34,7 +35,7 @@ class GarbageReport(Job):
# stick the arguments onto a ScratchPad so we can delete them all at once # stick the arguments onto a ScratchPad so we can delete them all at once
self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport, self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport,
findCycles=findCycles, doneCallback=doneCallback, findCycles=findCycles, doneCallback=doneCallback,
autoDestroy=autoDestroy) autoDestroy=autoDestroy, safeMode=safeMode)
if priority is not None: if priority is not None:
self.setPriority(priority) self.setPriority(priority)
jobMgr.add(self) jobMgr.add(self)
@ -48,25 +49,34 @@ class GarbageReport(Job):
if not wasOn: if not wasOn:
gc.set_debug(gc.DEBUG_SAVEALL) gc.set_debug(gc.DEBUG_SAVEALL)
gc.collect() gc.collect()
yield None
# don't repr the garbage list if we don't have to
if self.notify.getDebug():
self.notify.debug('gc.garbage == %s' % fastRepr(gc.garbage))
yield None
self.garbage = list(gc.garbage) self.garbage = list(gc.garbage)
# only yield if there's more time-consuming work to do,
# if there's no garbage, give instant feedback
if len(self.garbage) > 0:
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' % self.garbage) self.notify.debug('self.garbage == %s' % safeRepr(self.garbage))
del gc.garbage[:] del gc.garbage[:]
if not wasOn: if not wasOn:
gc.set_debug(oldFlags) gc.set_debug(oldFlags)
self.numGarbage = len(self.garbage) self.numGarbage = len(self.garbage)
yield None # only yield if there's more time-consuming work to do,
# if there's no garbage, give instant feedback
if self.numGarbage > 0:
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 items' % self.numGarbage)
# print the types of the garbage first, in case the repr of an object
# causes a crash
if self.numGarbage > 0:
self.notify.info('TYPES ONLY (this is only needed if a crash occurs before GarbageReport finishes):')
for result in printNumberedTypesGen(self.garbage):
yield None
self.referrersByReference = {} self.referrersByReference = {}
self.referrersByNumber = {} self.referrersByNumber = {}
@ -149,7 +159,11 @@ class GarbageReport(Job):
for i in xrange(numGarbage): for i in xrange(numGarbage):
yield None yield None
id = garbageIds[i] id = garbageIds[i]
objStr = safeRepr(self.garbage[id]) if self._args.safeMode:
# in safe mode, don't try to repr any of the objects
objStr = repr(itype(self.garbage[id]))
else:
objStr = safeRepr(self.garbage[id])
maxLen = 5000 maxLen = 5000
if len(objStr) > maxLen: if len(objStr) > maxLen:
snip = '<SNIP>' snip = '<SNIP>'

View File

@ -2159,7 +2159,7 @@ class Singleton(type):
class SingletonError(ValueError): class SingletonError(ValueError):
""" Used to indicate an inappropriate value for a Singleton.""" """ Used to indicate an inappropriate value for a Singleton."""
def printListEnum(l): def printListEnumGen(l):
# log each individual item with a number in front of it # log each individual item with a number in front of it
digits = 0 digits = 0
n = len(l) n = len(l)
@ -2169,6 +2169,11 @@ def printListEnum(l):
format = '%0' + '%s' % digits + 'i:%s' format = '%0' + '%s' % digits + 'i:%s'
for i in range(len(l)): for i in range(len(l)):
print format % (i, l[i]) print format % (i, l[i])
yield None
def printListEnum(l):
for result in printListEnumGen(l):
pass
def gcDebugOn(): def gcDebugOn():
import gc import gc
@ -2463,15 +2468,31 @@ def printNumberedTyped(items, maxLen=5000):
n /= 10 n /= 10
digits = digits digits = digits
format = '%0' + '%s' % digits + 'i:%s \t%s' format = '%0' + '%s' % digits + 'i:%s \t%s'
first = True
for i in xrange(len(items)): for i in xrange(len(items)):
first = False
objStr = fastRepr(items[i]) objStr = fastRepr(items[i])
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)
print format % (i, itype(items[i]), objStr) print format % (i, itype(items[i]), objStr)
def printNumberedTypesGen(items, maxLen=5000):
digits = 0
n = len(items)
while n > 0:
digits += 1
n /= 10
digits = digits
format = '%0' + '%s' % digits + 'i:%s'
for i in xrange(len(items)):
print format % (i, itype(items[i]))
yield None
def printNumberedTypes(items, maxLen=5000):
"""print out the type of each item of the list on its own line,
with each item numbered on the left from zero"""
for result in printNumberedTypesGen(items, maxLen):
yield result
class DelayedCall: class DelayedCall:
""" calls a func after a specified delay """ """ calls a func after a specified delay """
def __init__(self, func, name=None, delay=None): def __init__(self, func, name=None, delay=None):