ExceptionVarDump.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. from pandac.PandaModules import getConfigShowbase
  2. from direct.directnotify.DirectNotifyGlobal import directNotify
  3. from direct.showbase.PythonUtil import fastRepr
  4. from exceptions import Exception
  5. import sys
  6. import types
  7. import traceback
  8. notify = directNotify.newCategory("ExceptionVarDump")
  9. config = getConfigShowbase()
  10. reentry = 0
  11. def _varDump__init__(self, *args, **kArgs):
  12. global reentry
  13. if reentry > 0:
  14. return
  15. reentry += 1
  16. # frame zero is this frame
  17. f = 1
  18. self._savedExcString = None
  19. self._savedStackFrames = []
  20. while True:
  21. try:
  22. frame = sys._getframe(f)
  23. except ValueError, e:
  24. break
  25. else:
  26. f += 1
  27. self._savedStackFrames.append(frame)
  28. self._moved__init__(*args, **kArgs)
  29. reentry -= 1
  30. sReentry = 0
  31. def _varDump__print(exc):
  32. global sReentry
  33. global notify
  34. if sReentry > 0:
  35. return
  36. sReentry += 1
  37. if not exc._savedExcString:
  38. s = ''
  39. foundRun = False
  40. for frame in reversed(exc._savedStackFrames):
  41. filename = frame.f_code.co_filename
  42. codename = frame.f_code.co_name
  43. if not foundRun and codename != 'run':
  44. # don't print stack frames before run(),
  45. # they contain builtins and are huge
  46. continue
  47. foundRun = True
  48. s += '\nlocals for %s:%s\n' % (filename, codename)
  49. locals = frame.f_locals
  50. for var in locals:
  51. obj = locals[var]
  52. rep = fastRepr(obj)
  53. s += '::%s = %s\n' % (var, rep)
  54. exc._savedExcString = s
  55. exc._savedStackFrames = None
  56. notify.info(exc._savedExcString)
  57. sReentry -= 1
  58. oldExcepthook = None
  59. # store these values here so that Task.py can always reliably access them
  60. # from its main exception handler
  61. wantStackDumpLog = False
  62. wantStackDumpUpload = False
  63. variableDumpReasons = []
  64. dumpOnExceptionInit = False
  65. class _AttrNotFound:
  66. pass
  67. def _excepthookDumpVars(eType, eValue, tb):
  68. origTb = tb
  69. excStrs = traceback.format_exception(eType, eValue, origTb)
  70. s = 'printing traceback in case variable repr crashes the process...\n'
  71. for excStr in excStrs:
  72. s += excStr
  73. notify.info(s)
  74. s = 'DUMPING STACK FRAME VARIABLES'
  75. #import pdb;pdb.set_trace()
  76. #foundRun = False
  77. foundRun = True
  78. while tb is not None:
  79. frame = tb.tb_frame
  80. code = frame.f_code
  81. # this is a list of every string identifier used in this stack frame's code
  82. codeNames = set(code.co_names)
  83. # skip everything before the 'run' method, those frames have lots of
  84. # not-useful information
  85. if not foundRun:
  86. if code.co_name == 'run':
  87. foundRun = True
  88. else:
  89. tb = tb.tb_next
  90. continue
  91. s += '\n File "%s", line %s, in %s' % (
  92. code.co_filename, frame.f_lineno, code.co_name)
  93. stateStack = Stack()
  94. # prime the stack with the variables we should visit from the frame's data structures
  95. # grab all of the local, builtin and global variables that appear in the code's name list
  96. name2obj = {}
  97. for name, obj in frame.f_builtins.items():
  98. if name in codeNames:
  99. name2obj[name] = obj
  100. for name, obj in frame.f_globals.items():
  101. if name in codeNames:
  102. name2obj[name] = obj
  103. for name, obj in frame.f_locals.items():
  104. if name in codeNames:
  105. name2obj[name] = obj
  106. # show them in alphabetical order
  107. names = name2obj.keys()
  108. names.sort()
  109. # push them in reverse order so they'll be popped in the correct order
  110. names.reverse()
  111. traversedIds = set()
  112. for name in names:
  113. stateStack.push([name, name2obj[name], traversedIds])
  114. while len(stateStack) > 0:
  115. name, obj, traversedIds = stateStack.pop()
  116. #notify.info('%s, %s, %s' % (name, fastRepr(obj), traversedIds))
  117. r = fastRepr(obj, maxLen=10)
  118. if type(r) is types.StringType:
  119. r = r.replace('\n', '\\n')
  120. s += '\n %s = %s' % (name, r)
  121. # if we've already traversed through this object, don't traverse through it again
  122. if id(obj) not in traversedIds:
  123. attrName2obj = {}
  124. for attrName in codeNames:
  125. attr = getattr(obj, attrName, _AttrNotFound)
  126. if (attr is not _AttrNotFound):
  127. # prevent infinite recursion on method wrappers (__init__.__init__.__init__...)
  128. try:
  129. className = attr.__class__.__name__
  130. except:
  131. pass
  132. else:
  133. if className == 'method-wrapper':
  134. continue
  135. attrName2obj[attrName] = attr
  136. if len(attrName2obj):
  137. # show them in alphabetical order
  138. attrNames = attrName2obj.keys()
  139. attrNames.sort()
  140. # push them in reverse order so they'll be popped in the correct order
  141. attrNames.reverse()
  142. ids = set(traversedIds)
  143. ids.add(id(obj))
  144. for attrName in attrNames:
  145. obj = attrName2obj[attrName]
  146. stateStack.push(['%s.%s' % (name, attrName), obj, ids])
  147. tb = tb.tb_next
  148. if foundRun:
  149. s += '\n'
  150. if wantStackDumpLog:
  151. notify.info(s)
  152. if wantStackDumpUpload:
  153. excStrs = traceback.format_exception(eType, eValue, origTb)
  154. for excStr in excStrs:
  155. s += excStr
  156. timeMgr = None
  157. try:
  158. timeMgr = base.cr.timeManager
  159. except:
  160. try:
  161. timeMgr = simbase.air.timeManager
  162. except:
  163. pass
  164. if timeMgr:
  165. timeMgr.setStackDump(s)
  166. oldExcepthook(eType, eValue, origTb)
  167. def install(log, upload):
  168. global oldExcepthook
  169. global wantStackDumpLog
  170. global wantStackDumpUpload
  171. global dumpOnExceptionInit
  172. wantStackDumpLog = log
  173. wantStackDumpUpload = upload
  174. dumpOnExceptionInit = config.GetBool('variable-dump-on-exception-init', 0)
  175. if dumpOnExceptionInit:
  176. # this mode doesn't completely work because exception objects
  177. # thrown by the interpreter don't get created until the
  178. # stack has been unwound and an except block has been reached
  179. if not hasattr(Exception, '_moved__init__'):
  180. Exception._moved__init__ = Exception.__init__
  181. Exception.__init__ = _varDump__init__
  182. else:
  183. if sys.excepthook is not _excepthookDumpVars:
  184. oldExcepthook = sys.excepthook
  185. sys.excepthook = _excepthookDumpVars