working exception variable dump

This commit is contained in:
Darren Ranalli 2008-09-03 21:16:09 +00:00
parent 9c9d3d0707
commit e79a864125
4 changed files with 111 additions and 20 deletions

View File

@ -1,8 +1,11 @@
from pandac.PandaModules import ConfigConfigureGetConfigConfigShowbase as config
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import fastRepr
from exceptions import Exception
import sys
import traceback
import types
notify = directNotify.newCategory("ExceptionVarDump")
reentry = 0
@ -11,30 +14,33 @@ def _varDump__init__(self, *args, **kArgs):
if reentry > 0:
return
reentry += 1
# frame zero is this frame
f = 1
self._savedExcString = None
self._savedStackFrames = []
while True:
try:
frame = sys._getframe(f)
except ValueError, e:
break
else:
f += 1
self._savedStackFrames.append(frame)
except:
break
self._moved__init__(*args, **kArgs)
reentry -= 1
sReentry = 0
def _varDump__str__(self, *args, **kArgs):
def _varDump__print(exc):
global sReentry
global notify
if sReentry > 0:
return
sReentry += 1
if not self._savedExcString:
if not exc._savedExcString:
s = ''
foundRun = False
for frame in reversed(self._savedStackFrames):
for frame in reversed(exc._savedStackFrames):
filename = frame.f_code.co_filename
codename = frame.f_code.co_name
if not foundRun and codename != 'run':
@ -48,17 +54,59 @@ def _varDump__str__(self, *args, **kArgs):
obj = locals[var]
rep = fastRepr(obj)
s += '::%s = %s\n' % (var, rep)
self._savedExcString = s
self._savedStackFrames = None
notify = directNotify.newCategory("ExceptionVarDump")
notify.info(self._savedExcString)
str = self._moved__str__(*args, **kArgs)
exc._savedExcString = s
exc._savedStackFrames = None
notify.info(exc._savedExcString)
sReentry -= 1
return str
oldExcepthook = None
# store these values here so that Task.py can always reliably access these values
# from its main exception handler
wantVariableDump = False
dumpOnExceptionInit = False
def _excepthookDumpVars(eType, eValue, traceback):
s = 'DUMPING STACK FRAME VARIABLES'
tb = traceback
#import pdb;pdb.set_trace()
foundRun = False
while tb is not None:
frame = tb.tb_frame
code = frame.f_code
tb = tb.tb_next
# skip everything before the 'run' method, those frames have lots of
# not-useful information
if not foundRun:
if code.co_name == 'run':
foundRun = True
else:
continue
s += '\n File "%s", line %s, in %s' % (
code.co_filename, frame.f_lineno, code.co_name)
for name, val in frame.f_locals.iteritems():
r = fastRepr(val)
if type(r) is types.StringType:
r = r.replace('\n', '\\n')
s += '\n %s=%s' % (name, r)
s += '\n'
notify.info(s)
oldExcepthook(eType, eValue, traceback)
def install():
if not hasattr(Exception, '_moved__init__'):
Exception._moved__init__ = Exception.__init__
Exception.__init__ = _varDump__init__
Exception._moved__str__ = Exception.__str__
Exception.__str__ = _varDump__str__
global oldExcepthook
global wantVariableDump
global dumpOnExceptionInit
wantVariableDump = True
dumpOnExceptionInit = config.GetBool('variable-dump-on-exception-init', 0)
if dumpOnExceptionInit:
# this mode doesn't completely work because exception objects
# thrown by the interpreter don't get created until the
# stack has been unwound and an except block has been reached
if not hasattr(Exception, '_moved__init__'):
Exception._moved__init__ = Exception.__init__
Exception.__init__ = _varDump__init__
else:
if sys.excepthook is not _excepthookDumpVars:
oldExcepthook = sys.excepthook
sys.excepthook = _excepthookDumpVars

View File

@ -2244,11 +2244,29 @@ def gcDebugOn():
import gc
return (gc.get_debug() & gc.DEBUG_SAVEALL) == gc.DEBUG_SAVEALL
# base class for all Panda C++ objects
# libdtoolconfig doesn't seem to have this, grab it off of PandaNode
dtoolSuperBase = None
def _getDtoolSuperBase():
global dtoolSuperBase
from pandac.PandaModules import PandaNode
dtoolSuperBase = PandaNode('').__class__.__bases__[0].__bases__[0].__bases__[0]
def safeRepr(obj):
global dtoolSuperBase
if dtoolSuperBase is None:
_getDtoolSuperBase()
if isinstance(obj, dtoolSuperBase):
# repr of C++ object could crash, particularly if the object has been deleted
return '<%s.%s instance at %s>' % (
obj.__class__.__module__, obj.__class__.__name__, hex(id(obj)))
try:
return repr(obj)
except:
return '<** FAILED REPR OF %s **>' % obj.__class__.__name__
return '<** FAILED REPR OF %s instance at %s **>' % (obj.__class__.__name__, hex(id(obj)))
def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
""" caps the length of iterable types, so very large objects will print faster.
@ -2302,7 +2320,10 @@ def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
else:
return safeRepr(obj)
else:
return safeRepr(obj)
r = safeRepr(obj)
if len(r) > maxLen:
r = r[:maxLen]
return r
except:
return '<** FAILED REPR OF %s **>' % obj.__class__.__name__
@ -2445,11 +2466,20 @@ class RefCounter:
return result
def itype(obj):
# version of type that gives more complete information about instance types
global dtoolSuperBase
t = type(obj)
if t is types.InstanceType:
return '%s of <class %s>>' % (repr(types.InstanceType)[:-1],
str(obj.__class__))
str(obj.__class__))
else:
# C++ object instances appear to be types via type()
# check if this is a C++ object
if dtoolSuperBase is None:
_getDtoolSuperBase()
if isinstance(obj, dtoolSuperBase):
return '%s of %s>' % (repr(types.InstanceType)[:-1],
str(obj.__class__))
return t
def deeptype(obj, maxLen=100, _visitedIds=None):

View File

@ -35,6 +35,7 @@ import Loader
import time
from direct.fsm import ClassicFSM
from direct.fsm import State
from direct.showbase import ExceptionVarDump
import DirectObject
import SfxPlayer
if __debug__:
@ -69,6 +70,8 @@ class ShowBase(DirectObject.DirectObject):
notify = directNotify.newCategory("ShowBase")
def __init__(self):
if config.GetBool('want-variable-dump', 1):
ExceptionVarDump.install()
# Locate the directory containing the main program
maindir=os.path.abspath(sys.path[0])

View File

@ -14,6 +14,7 @@ from pandac.libpandaexpressModules import *
from direct.directnotify.DirectNotifyGlobal import *
from direct.showbase.PythonUtil import *
from direct.showbase.MessengerGlobal import *
from direct.showbase import ExceptionVarDump
import time
import fnmatch
import string
@ -981,6 +982,15 @@ class TaskManager:
self.stop()
else:
raise
except Exception, e:
if self.extendedExceptions:
self.stop()
print_exc_plus()
else:
if (ExceptionVarDump.wantVariableDump and
ExceptionVarDump.dumpOnExceptionInit):
ExceptionVarDump._varDump__print(e)
raise
except:
if self.extendedExceptions:
self.stop()