Browse Source

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

Darren Ranalli 18 years ago
parent
commit
8127bcb6b3
2 changed files with 49 additions and 14 deletions
  1. 25 11
      direct/src/showbase/GarbageReport.py
  2. 24 3
      direct/src/showbase/PythonUtil.py

+ 25 - 11
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 = '<SNIP>'

+ 24 - 3
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 = '<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):