| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- from pandac.PandaModules import getConfigShowbase
- from direct.directnotify.DirectNotifyGlobal import directNotify
- from direct.showbase.PythonUtil import fastRepr
- from exceptions import Exception
- import sys
- import types
- import traceback
- notify = directNotify.newCategory("ExceptionVarDump")
- config = getConfigShowbase()
- reentry = 0
- def _varDump__init__(self, *args, **kArgs):
- global reentry
- 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)
- self._moved__init__(*args, **kArgs)
- reentry -= 1
- sReentry = 0
- def _varDump__print(exc):
- global sReentry
- global notify
- if sReentry > 0:
- return
- sReentry += 1
- if not exc._savedExcString:
- s = ''
- foundRun = False
- for frame in reversed(exc._savedStackFrames):
- filename = frame.f_code.co_filename
- codename = frame.f_code.co_name
- if not foundRun and codename != 'run':
- # don't print stack frames before run(),
- # they contain builtins and are huge
- continue
- foundRun = True
- s += '\nlocals for %s:%s\n' % (filename, codename)
- locals = frame.f_locals
- for var in locals:
- obj = locals[var]
- rep = fastRepr(obj)
- s += '::%s = %s\n' % (var, rep)
- exc._savedExcString = s
- exc._savedStackFrames = None
- notify.info(exc._savedExcString)
- sReentry -= 1
- oldExcepthook = None
- # store these values here so that Task.py can always reliably access them
- # from its main exception handler
- wantStackDumpLog = False
- wantStackDumpUpload = False
- variableDumpReasons = []
- dumpOnExceptionInit = False
- class _AttrNotFound:
- pass
- def _excepthookDumpVars(eType, eValue, tb):
- origTb = tb
- excStrs = traceback.format_exception(eType, eValue, origTb)
- s = 'printing traceback in case variable repr crashes the process...\n'
- for excStr in excStrs:
- s += excStr
- notify.info(s)
- s = 'DUMPING STACK FRAME VARIABLES'
- #import pdb;pdb.set_trace()
- #foundRun = False
- foundRun = True
- while tb is not None:
- frame = tb.tb_frame
- code = frame.f_code
- # this is a list of every string identifier used in this stack frame's code
- codeNames = set(code.co_names)
- # 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:
- tb = tb.tb_next
- continue
- s += '\n File "%s", line %s, in %s' % (
- code.co_filename, frame.f_lineno, code.co_name)
- stateStack = Stack()
- # prime the stack with the variables we should visit from the frame's data structures
- # grab all of the local, builtin and global variables that appear in the code's name list
- name2obj = {}
- for name, obj in frame.f_builtins.items():
- if name in codeNames:
- name2obj[name] = obj
- for name, obj in frame.f_globals.items():
- if name in codeNames:
- name2obj[name] = obj
- for name, obj in frame.f_locals.items():
- if name in codeNames:
- name2obj[name] = obj
- # show them in alphabetical order
- names = name2obj.keys()
- names.sort()
- # push them in reverse order so they'll be popped in the correct order
- names.reverse()
- traversedIds = set()
- for name in names:
- stateStack.push([name, name2obj[name], traversedIds])
- while len(stateStack) > 0:
- name, obj, traversedIds = stateStack.pop()
- #notify.info('%s, %s, %s' % (name, fastRepr(obj), traversedIds))
- r = fastRepr(obj, maxLen=10)
- if type(r) is types.StringType:
- r = r.replace('\n', '\\n')
- s += '\n %s = %s' % (name, r)
- # if we've already traversed through this object, don't traverse through it again
- if id(obj) not in traversedIds:
- attrName2obj = {}
- for attrName in codeNames:
- attr = getattr(obj, attrName, _AttrNotFound)
- if (attr is not _AttrNotFound):
- # prevent infinite recursion on method wrappers (__init__.__init__.__init__...)
- try:
- className = attr.__class__.__name__
- except:
- pass
- else:
- if className == 'method-wrapper':
- continue
- attrName2obj[attrName] = attr
- if len(attrName2obj):
- # show them in alphabetical order
- attrNames = attrName2obj.keys()
- attrNames.sort()
- # push them in reverse order so they'll be popped in the correct order
- attrNames.reverse()
- ids = set(traversedIds)
- ids.add(id(obj))
- for attrName in attrNames:
- obj = attrName2obj[attrName]
- stateStack.push(['%s.%s' % (name, attrName), obj, ids])
-
- tb = tb.tb_next
- if foundRun:
- s += '\n'
- if wantStackDumpLog:
- notify.info(s)
- if wantStackDumpUpload:
- excStrs = traceback.format_exception(eType, eValue, origTb)
- for excStr in excStrs:
- s += excStr
- timeMgr = None
- try:
- timeMgr = base.cr.timeManager
- except:
- try:
- timeMgr = simbase.air.timeManager
- except:
- pass
- if timeMgr:
- timeMgr.setStackDump(s)
- oldExcepthook(eType, eValue, origTb)
- def install(log, upload):
- global oldExcepthook
- global wantStackDumpLog
- global wantStackDumpUpload
- global dumpOnExceptionInit
- wantStackDumpLog = log
- wantStackDumpUpload = upload
- 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
|