mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
added 'safe' mode to GarbageReport for dealing with C++ crashes in __repr__
This commit is contained in:
parent
7f31a5fb83
commit
8127bcb6b3
@ -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>'
|
||||||
|
@ -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):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user