PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/translator/tool/pdbplus.py

https://bitbucket.org/pypy/pypy/
Python | 459 lines | 451 code | 8 blank | 0 comment | 7 complexity | e0ef11e98b72d2bffc68aac4c306c66e MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import pdb, bdb
  2. import types
  3. import code
  4. import sys
  5. from rpython.flowspace.model import FunctionGraph
  6. class NoTTY(Exception):
  7. pass
  8. class PdbPlusShow(pdb.Pdb):
  9. def __init__(self, translator):
  10. pdb.Pdb.__init__(self)
  11. if self.prompt == "(Pdb) ":
  12. self.prompt = "(Pdb+) "
  13. else:
  14. self.prompt = self.prompt.replace("(", "(Pdb+ on ", 1)
  15. self.translator = translator
  16. self.exposed = {}
  17. def post_mortem(self, t):
  18. self.reset()
  19. while t.tb_next is not None:
  20. t = t.tb_next
  21. self.interaction(t.tb_frame, t)
  22. def preloop(self):
  23. if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
  24. raise NoTTY("Cannot start the debugger when stdout is captured.")
  25. pdb.Pdb.preloop(self)
  26. def expose(self, d):
  27. self.exposed.update(d)
  28. def _show(self, page):
  29. page.display_background()
  30. def _importobj(self, fullname):
  31. obj = None
  32. name = ''
  33. for comp in fullname.split('.'):
  34. name += comp
  35. obj = getattr(obj, comp, None)
  36. if obj is None:
  37. try:
  38. obj = __import__(name, {}, {}, ['*'])
  39. except ImportError:
  40. raise NameError
  41. name += '.'
  42. return obj
  43. TRYPREFIXES = ['','pypy.','pypy.objspace.','pypy.interpreter.', 'pypy.objspace.std.' ]
  44. def _mygetval(self, arg, errmsg):
  45. try:
  46. return eval(arg, self.curframe.f_globals,
  47. self.curframe.f_locals)
  48. except:
  49. t, v = sys.exc_info()[:2]
  50. if isinstance(t, str):
  51. exc_type_name = t
  52. else: exc_type_name = t.__name__
  53. if not isinstance(arg, str):
  54. print '*** %s' % errmsg, "\t[%s: %s]" % (exc_type_name, v)
  55. else:
  56. print '*** %s:' % errmsg, arg, "\t[%s: %s]" % (exc_type_name, v)
  57. raise
  58. def _getobj(self, name):
  59. if '.' in name:
  60. for pfx in self.TRYPREFIXES:
  61. try:
  62. return self._importobj(pfx+name)
  63. except NameError:
  64. pass
  65. try:
  66. return self._mygetval(name, "Not found")
  67. except (KeyboardInterrupt, SystemExit, MemoryError):
  68. raise
  69. except:
  70. pass
  71. return None
  72. def do_find(self, arg):
  73. """find obj [as var]
  74. find dotted named obj, possibly using prefixing with some packages
  75. in pypy (see help pypyprefixes); the result is assigned to var or _."""
  76. objarg, var = self._parse_modif(arg)
  77. obj = self._getobj(objarg)
  78. if obj is None:
  79. return
  80. print obj
  81. self._setvar(var, obj)
  82. def _parse_modif(self, arg, modif='as'):
  83. var = '_'
  84. aspos = arg.rfind(modif+' ')
  85. if aspos != -1:
  86. objarg = arg[:aspos].strip()
  87. var = arg[aspos+(1+len(modif)):].strip()
  88. else:
  89. objarg = arg
  90. return objarg, var
  91. def _setvar(self, var, obj):
  92. self.curframe.f_locals[var] = obj
  93. class GiveUp(Exception):
  94. pass
  95. def _getcdef(self, cls):
  96. try:
  97. return self.translator.annotator.bookkeeper.getuniqueclassdef(cls)
  98. except Exception:
  99. print "*** cannot get classdef: %s" % cls
  100. return None
  101. def _make_flt(self, expr):
  102. try:
  103. expr = compile(expr, '<filter>', 'eval')
  104. except SyntaxError:
  105. print "*** syntax: %s" % expr
  106. return None
  107. def flt(c):
  108. marker = object()
  109. try:
  110. old = self.curframe.f_locals.get('cand', marker)
  111. self.curframe.f_locals['cand'] = c
  112. try:
  113. return self._mygetval(expr, "oops")
  114. except (KeyboardInterrupt, SystemExit, MemoryError):
  115. raise
  116. except:
  117. raise self.GiveUp
  118. finally:
  119. if old is not marker:
  120. self.curframe.f_locals['cand'] = old
  121. else:
  122. del self.curframe.f_locals['cand']
  123. return flt
  124. def do_finddescs(self, arg):
  125. """finddescs kind expr [as var]
  126. find annotation descs of kind (ClassDesc|FuncionDesc|...)
  127. for which expr is true, cand in it referes to
  128. the candidate desc; the result list is assigned to var or _."""
  129. expr, var = self._parse_modif(arg)
  130. kind, expr = expr.split(None, 1)
  131. flt = self._make_flt(expr)
  132. if flt is None:
  133. return
  134. from rpython.annotator import description
  135. kind_cls = getattr(description, kind, None)
  136. if kind_cls is None:
  137. kind = kind.title()+'Desc'
  138. kind_cls = getattr(description, kind, None)
  139. if kind_cls is None:
  140. return
  141. descs = []
  142. try:
  143. for c in self.translator.annotator.bookkeeper.descs.itervalues():
  144. if isinstance(c, kind_cls) and flt(c):
  145. descs.append(c)
  146. except self.GiveUp:
  147. return
  148. self._setvar(var, descs)
  149. def do_showg(self, arg):
  150. """showg obj
  151. show graph for obj, obj can be an expression or a dotted name
  152. (in which case prefixing with some packages in pypy is tried (see help pypyprefixes)).
  153. if obj is a function or method, the localized call graph is shown;
  154. if obj is a class or ClassDef the class definition graph is shown"""
  155. from rpython.annotator.classdesc import ClassDef
  156. from rpython.translator.tool import graphpage
  157. translator = self.translator
  158. obj = self._getobj(arg)
  159. if obj is None:
  160. return
  161. if hasattr(obj, 'im_func'):
  162. obj = obj.im_func
  163. if isinstance(obj, types.FunctionType):
  164. page = graphpage.LocalizedCallGraphPage(translator, self._allgraphs(obj))
  165. elif isinstance(obj, FunctionGraph):
  166. page = graphpage.FlowGraphPage(translator, [obj])
  167. elif isinstance(obj, (type, types.ClassType)):
  168. classdef = self._getcdef(obj)
  169. if classdef is None:
  170. return
  171. page = graphpage.ClassDefPage(translator, classdef)
  172. elif isinstance(obj, ClassDef):
  173. page = graphpage.ClassDefPage(translator, obj)
  174. else:
  175. print "*** Nothing to do"
  176. return
  177. self._show(page)
  178. def do_findv(self, varname):
  179. """ findv [varname]
  180. find a stack frame that has a certain variable (the default is "graph")
  181. """
  182. if not varname:
  183. varname = "graph"
  184. printfr = self.print_stack_entry
  185. self.print_stack_entry = lambda *args: None
  186. try:
  187. num = 0
  188. while self.curindex:
  189. frame = self.curframe
  190. if varname in frame.f_locals:
  191. printfr(self.stack[self.curindex])
  192. print "%s = %s" % (varname, frame.f_locals[varname])
  193. return
  194. num += 1
  195. self.do_up(None)
  196. print "no %s found" % (varname, )
  197. for i in range(num):
  198. self.do_down(None)
  199. finally:
  200. del self.print_stack_entry
  201. def _attrs(self, arg, pr):
  202. arg, expr = self._parse_modif(arg, 'match')
  203. if expr == '_':
  204. expr = 'True'
  205. obj = self._getobj(arg)
  206. if obj is None:
  207. return
  208. try:
  209. obj = list(obj)
  210. except:
  211. obj = [obj]
  212. clsdefs = []
  213. for x in obj:
  214. if isinstance(x, (type, types.ClassType)):
  215. cdef = self._getcdef(x)
  216. if cdef is None:
  217. continue
  218. clsdefs.append(cdef)
  219. else:
  220. clsdefs.append(x)
  221. def longname(c):
  222. return c.name
  223. clsdefs.sort(lambda x,y: cmp(longname(x), longname(y)))
  224. flt = self._make_flt(expr)
  225. if flt is None:
  226. return
  227. for cdef in clsdefs:
  228. try:
  229. attrs = [a for a in cdef.attrs.itervalues() if flt(a)]
  230. except self.GiveUp:
  231. return
  232. if attrs:
  233. print "%s:" % cdef.name
  234. pr(attrs)
  235. def do_attrs(self, arg):
  236. """attrs obj [match expr]
  237. list annotated attrs of class|def obj or list of classe(def)s obj,
  238. obj can be an expression or a dotted name
  239. (in which case prefixing with some packages in pypy is tried (see help pypyprefixes));
  240. expr is an optional filtering expression; cand in it refer to the candidate Attribute
  241. information object, which has a .name and .s_value."""
  242. def pr(attrs):
  243. print " " + ' '.join([a.name for a in attrs])
  244. self._attrs(arg, pr)
  245. def do_attrsann(self, arg):
  246. """attrsann obj [match expr]
  247. list with their annotation annotated attrs of class|def obj or list of classe(def)s obj,
  248. obj can be an expression or a dotted name
  249. (in which case prefixing with some packages in pypy is tried (see help pypyprefixes));
  250. expr is an optional filtering expression; cand in it refer to the candidate Attribute
  251. information object, which has a .name and .s_value."""
  252. def pr(attrs):
  253. for a in attrs:
  254. print ' %s %s' % (a.name, a.s_value)
  255. self._attrs(arg, pr)
  256. def do_readpos(self, arg):
  257. """readpos obj attrname [match expr] [as var]
  258. list the read positions of annotated attr with attrname of class or classdef obj,
  259. obj can be an expression or a dotted name
  260. (in which case prefixing with some packages in pypy is tried (see help pypyprefixes));
  261. expr is an optional filtering expression; cand in it refer to the candidate read
  262. position information, which has a .func (which can be None), a .graph and .block and .i;
  263. the list of the read positions functions is set to var or _."""
  264. class Pos:
  265. def __init__(self, graph, func, block, i):
  266. self.graph = graph
  267. self.func = func
  268. self.block = block
  269. self.i = i
  270. arg, var = self._parse_modif(arg, 'as')
  271. arg, expr = self._parse_modif(arg, 'match')
  272. if expr == '_':
  273. expr = 'True'
  274. args = arg.split()
  275. if len(args) != 2:
  276. print "*** expected obj attrname:", arg
  277. return
  278. arg, attrname = args
  279. # allow quotes around attrname
  280. if (attrname.startswith("'") and attrname.endswith("'")
  281. or attrname.startswith('"') and attrname.endswith('"')):
  282. attrname = attrname[1:-1]
  283. obj = self._getobj(arg)
  284. if obj is None:
  285. return
  286. if isinstance(obj, (type, types.ClassType)):
  287. obj = self._getcdef(obj)
  288. if obj is None:
  289. return
  290. bk = self.translator.annotator.bookkeeper
  291. attrs = obj.attrs
  292. if attrname not in attrs:
  293. print "*** bogus:", attrname
  294. return
  295. pos = bk.getattr_locations(obj.classdesc, attrname)
  296. if not pos:
  297. return
  298. flt = self._make_flt(expr)
  299. if flt is None:
  300. return
  301. r = {}
  302. try:
  303. for p in pos:
  304. graph, block, i = p
  305. if hasattr(graph, 'func'):
  306. func = graph.func
  307. else:
  308. func = None
  309. if flt(Pos(graph, func, block, i)):
  310. if func is not None:
  311. print func.__module__ or '?', func.__name__, block, i
  312. else:
  313. print graph, block, i
  314. if i >= 0:
  315. op = block.operations[i]
  316. print " ", op
  317. print " ",
  318. for arg in op.args:
  319. print "%s: %s" % (arg, self.translator.annotator.binding(arg)),
  320. print
  321. r[func] = True
  322. except self.GiveUp:
  323. return
  324. self._setvar(var, r.keys())
  325. def do_flowg(self, arg):
  326. """flowg obj
  327. show flow graph for function obj, obj can be an expression or a dotted name
  328. (in which case prefixing with some packages in pypy is tried (see help pypyprefixes))"""
  329. from rpython.translator.tool import graphpage
  330. obj = self._getobj(arg)
  331. if obj is None:
  332. return
  333. if hasattr(obj, 'im_func'):
  334. obj = obj.im_func
  335. if isinstance(obj, types.FunctionType):
  336. graphs = self._allgraphs(obj)
  337. elif isinstance(obj, FunctionGraph):
  338. graphs = [obj]
  339. else:
  340. print "*** Not a function"
  341. return
  342. self._show(graphpage.FlowGraphPage(self.translator, graphs))
  343. def _allgraphs(self, func):
  344. graphs = {}
  345. funcdesc = self.translator.annotator.bookkeeper.getdesc(func)
  346. for graph in funcdesc._cache.itervalues():
  347. graphs[graph] = True
  348. for graph in self.translator.graphs:
  349. if getattr(graph, 'func', None) is func:
  350. graphs[graph] = True
  351. return graphs.keys()
  352. def do_callg(self, arg):
  353. """callg obj
  354. show localized call-graph for function obj, obj can be an expression or a dotted name
  355. (in which case prefixing with some packages in pypy is tried (see help pypyprefixes))"""
  356. from rpython.translator.tool import graphpage
  357. obj = self._getobj(arg)
  358. if obj is None:
  359. return
  360. if hasattr(obj, 'im_func'):
  361. obj = obj.im_func
  362. if isinstance(obj, types.FunctionType):
  363. graphs = self._allgraphs(obj)
  364. elif isinstance(obj, FunctionGraph):
  365. graphs = [obj]
  366. else:
  367. print "*** Not a function"
  368. return
  369. self._show(graphpage.LocalizedCallGraphPage(self.translator, graphs))
  370. def do_classhier(self, arg):
  371. """classhier
  372. show class hierarchy graph"""
  373. from rpython.translator.tool import graphpage
  374. self._show(graphpage.ClassHierarchyPage(self.translator))
  375. def do_callgraph(self, arg):
  376. """callgraph
  377. show the program's call graph"""
  378. from rpython.translator.tool import graphpage
  379. self._show(graphpage.TranslatorPage(self.translator, 100))
  380. def do_interact(self, arg):
  381. """invoke a code.py sub prompt"""
  382. ns = self.curframe.f_globals.copy()
  383. ns.update(self.curframe.f_locals)
  384. code.interact("*interactive*", local=ns)
  385. def help_graphs(self):
  386. print "graph commands are: callgraph, showg, flowg, callg, classhier"
  387. def help_ann_other(self):
  388. print "other annotation related commands are: find, finddescs, attrs, attrsann, readpos"
  389. def help_pypyprefixes(self):
  390. print "these prefixes are tried for dotted names in graph commands:"
  391. print self.TRYPREFIXES
  392. # start helpers
  393. def start(self, tb):
  394. if tb is None:
  395. fn, args = self.set_trace, ()
  396. else:
  397. fn, args = self.post_mortem, (tb,)
  398. try:
  399. t = self.translator # define enviroments, xxx more stuff
  400. exec ""
  401. locals().update(self.exposed)
  402. fn(*args)
  403. pass # for debugger to land
  404. except bdb.BdbQuit:
  405. pass
  406. def pdbcatch(f):
  407. "A decorator that throws you in a pdbplus if the given function raises."
  408. from rpython.tool.sourcetools import func_with_new_name
  409. def wrapper(*args, **kwds):
  410. try:
  411. return f(*args, **kwds)
  412. except:
  413. import sys
  414. PdbPlusShow(None).post_mortem(sys.exc_info()[2])
  415. raise
  416. wrapper = func_with_new_name(wrapper, f.__name__)
  417. return wrapper