From 8127bcb6b37a62cf40a7db5bf6b6c91c86a370f3 Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Tue, 13 Nov 2007 21:23:28 +0000 Subject: [PATCH] added 'safe' mode to GarbageReport for dealing with C++ crashes in __repr__ --- direct/src/showbase/GarbageReport.py | 36 +++++++++++++++++++--------- direct/src/showbase/PythonUtil.py | 27 ++++++++++++++++++--- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py index c5995075a1..cb440c8e64 100755 --- a/direct/src/showbase/GarbageReport.py +++ b/direct/src/showbase/GarbageReport.py @@ -3,7 +3,7 @@ __all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger'] 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 import gc @@ -26,7 +26,8 @@ class GarbageReport(Job): NotGarbage = 'NG' 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 false, caller is responsible for calling destroy() # 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 self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport, findCycles=findCycles, doneCallback=doneCallback, - autoDestroy=autoDestroy) + autoDestroy=autoDestroy, safeMode=safeMode) if priority is not None: self.setPriority(priority) jobMgr.add(self) @@ -48,25 +49,34 @@ class GarbageReport(Job): if not wasOn: gc.set_debug(gc.DEBUG_SAVEALL) 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) + # 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 if self.notify.getDebug(): - self.notify.debug('self.garbage == %s' % self.garbage) + self.notify.debug('self.garbage == %s' % safeRepr(self.garbage)) del gc.garbage[:] if not wasOn: gc.set_debug(oldFlags) 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: 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.referrersByNumber = {} @@ -149,7 +159,11 @@ class GarbageReport(Job): for i in xrange(numGarbage): yield None 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 if len(objStr) > maxLen: snip = '' diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 336e80aead..9b5bc76bc8 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -2159,7 +2159,7 @@ class Singleton(type): class SingletonError(ValueError): """ 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 digits = 0 n = len(l) @@ -2169,6 +2169,11 @@ def printListEnum(l): format = '%0' + '%s' % digits + 'i:%s' for i in range(len(l)): print format % (i, l[i]) + yield None + +def printListEnum(l): + for result in printListEnumGen(l): + pass def gcDebugOn(): import gc @@ -2463,15 +2468,31 @@ def printNumberedTyped(items, maxLen=5000): n /= 10 digits = digits format = '%0' + '%s' % digits + 'i:%s \t%s' - first = True for i in xrange(len(items)): - first = False objStr = fastRepr(items[i]) if len(objStr) > maxLen: snip = '' objStr = '%s%s' % (objStr[:(maxLen-len(snip))], snip) 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: """ calls a func after a specified delay """ def __init__(self, func, name=None, delay=None):