Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(237)

Side by Side Diff: third_party/cython/src/Cython/Debugger/libcython.py

Issue 385073004: Adding cython v0.20.2 in third-party. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reference cython dev list thread. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 """
2 GDB extension that adds Cython support.
3 """
4
5 from __future__ import with_statement
6
7 import sys
8 import textwrap
9 import traceback
10 import functools
11 import itertools
12 import collections
13
14 import gdb
15
16 try:
17 from lxml import etree
18 have_lxml = True
19 except ImportError:
20 have_lxml = False
21 try:
22 # Python 2.5
23 from xml.etree import cElementTree as etree
24 except ImportError:
25 try:
26 # Python 2.5
27 from xml.etree import ElementTree as etree
28 except ImportError:
29 try:
30 # normal cElementTree install
31 import cElementTree as etree
32 except ImportError:
33 # normal ElementTree install
34 import elementtree.ElementTree as etree
35
36 try:
37 import pygments.lexers
38 import pygments.formatters
39 except ImportError:
40 pygments = None
41 sys.stderr.write("Install pygments for colorized source code.\n")
42
43 if hasattr(gdb, 'string_to_argv'):
44 from gdb import string_to_argv
45 else:
46 from shlex import split as string_to_argv
47
48 from Cython.Debugger import libpython
49
50 # C or Python type
51 CObject = 'CObject'
52 PythonObject = 'PythonObject'
53
54 _data_types = dict(CObject=CObject, PythonObject=PythonObject)
55 _filesystemencoding = sys.getfilesystemencoding() or 'UTF-8'
56
57 # decorators
58
59 def dont_suppress_errors(function):
60 "*sigh*, readline"
61 @functools.wraps(function)
62 def wrapper(*args, **kwargs):
63 try:
64 return function(*args, **kwargs)
65 except Exception:
66 traceback.print_exc()
67 raise
68
69 return wrapper
70
71 def default_selected_gdb_frame(err=True):
72 def decorator(function):
73 @functools.wraps(function)
74 def wrapper(self, frame=None, *args, **kwargs):
75 try:
76 frame = frame or gdb.selected_frame()
77 except RuntimeError:
78 raise gdb.GdbError("No frame is currently selected.")
79
80 if err and frame.name() is None:
81 raise NoFunctionNameInFrameError()
82
83 return function(self, frame, *args, **kwargs)
84 return wrapper
85 return decorator
86
87 def require_cython_frame(function):
88 @functools.wraps(function)
89 @require_running_program
90 def wrapper(self, *args, **kwargs):
91 frame = kwargs.get('frame') or gdb.selected_frame()
92 if not self.is_cython_function(frame):
93 raise gdb.GdbError('Selected frame does not correspond with a '
94 'Cython function we know about.')
95 return function(self, *args, **kwargs)
96 return wrapper
97
98 def dispatch_on_frame(c_command, python_command=None):
99 def decorator(function):
100 @functools.wraps(function)
101 def wrapper(self, *args, **kwargs):
102 is_cy = self.is_cython_function()
103 is_py = self.is_python_function()
104
105 if is_cy or (is_py and not python_command):
106 function(self, *args, **kwargs)
107 elif is_py:
108 gdb.execute(python_command)
109 elif self.is_relevant_function():
110 gdb.execute(c_command)
111 else:
112 raise gdb.GdbError("Not a function cygdb knows about. "
113 "Use the normal GDB commands instead.")
114
115 return wrapper
116 return decorator
117
118 def require_running_program(function):
119 @functools.wraps(function)
120 def wrapper(*args, **kwargs):
121 try:
122 gdb.selected_frame()
123 except RuntimeError:
124 raise gdb.GdbError("No frame is currently selected.")
125
126 return function(*args, **kwargs)
127 return wrapper
128
129
130 def gdb_function_value_to_unicode(function):
131 @functools.wraps(function)
132 def wrapper(self, string, *args, **kwargs):
133 if isinstance(string, gdb.Value):
134 string = string.string()
135
136 return function(self, string, *args, **kwargs)
137 return wrapper
138
139
140 # Classes that represent the debug information
141 # Don't rename the parameters of these classes, they come directly from the XML
142
143 class CythonModule(object):
144 def __init__(self, module_name, filename, c_filename):
145 self.name = module_name
146 self.filename = filename
147 self.c_filename = c_filename
148 self.globals = {}
149 # {cython_lineno: min(c_linenos)}
150 self.lineno_cy2c = {}
151 # {c_lineno: cython_lineno}
152 self.lineno_c2cy = {}
153 self.functions = {}
154
155 class CythonVariable(object):
156
157 def __init__(self, name, cname, qualified_name, type, lineno):
158 self.name = name
159 self.cname = cname
160 self.qualified_name = qualified_name
161 self.type = type
162 self.lineno = int(lineno)
163
164 class CythonFunction(CythonVariable):
165 def __init__(self,
166 module,
167 name,
168 cname,
169 pf_cname,
170 qualified_name,
171 lineno,
172 type=CObject,
173 is_initmodule_function="False"):
174 super(CythonFunction, self).__init__(name,
175 cname,
176 qualified_name,
177 type,
178 lineno)
179 self.module = module
180 self.pf_cname = pf_cname
181 self.is_initmodule_function = is_initmodule_function == "True"
182 self.locals = {}
183 self.arguments = []
184 self.step_into_functions = set()
185
186
187 # General purpose classes
188
189 class CythonBase(object):
190
191 @default_selected_gdb_frame(err=False)
192 def is_cython_function(self, frame):
193 return frame.name() in self.cy.functions_by_cname
194
195 @default_selected_gdb_frame(err=False)
196 def is_python_function(self, frame):
197 """
198 Tells if a frame is associated with a Python function.
199 If we can't read the Python frame information, don't regard it as such.
200 """
201 if frame.name() == 'PyEval_EvalFrameEx':
202 pyframe = libpython.Frame(frame).get_pyop()
203 return pyframe and not pyframe.is_optimized_out()
204 return False
205
206 @default_selected_gdb_frame()
207 def get_c_function_name(self, frame):
208 return frame.name()
209
210 @default_selected_gdb_frame()
211 def get_c_lineno(self, frame):
212 return frame.find_sal().line
213
214 @default_selected_gdb_frame()
215 def get_cython_function(self, frame):
216 result = self.cy.functions_by_cname.get(frame.name())
217 if result is None:
218 raise NoCythonFunctionInFrameError()
219
220 return result
221
222 @default_selected_gdb_frame()
223 def get_cython_lineno(self, frame):
224 """
225 Get the current Cython line number. Returns 0 if there is no
226 correspondence between the C and Cython code.
227 """
228 cyfunc = self.get_cython_function(frame)
229 return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame), 0)
230
231 @default_selected_gdb_frame()
232 def get_source_desc(self, frame):
233 filename = lineno = lexer = None
234 if self.is_cython_function(frame):
235 filename = self.get_cython_function(frame).module.filename
236 lineno = self.get_cython_lineno(frame)
237 if pygments:
238 lexer = pygments.lexers.CythonLexer(stripall=False)
239 elif self.is_python_function(frame):
240 pyframeobject = libpython.Frame(frame).get_pyop()
241
242 if not pyframeobject:
243 raise gdb.GdbError(
244 'Unable to read information on python frame')
245
246 filename = pyframeobject.filename()
247 lineno = pyframeobject.current_line_num()
248
249 if pygments:
250 lexer = pygments.lexers.PythonLexer(stripall=False)
251 else:
252 symbol_and_line_obj = frame.find_sal()
253 if not symbol_and_line_obj or not symbol_and_line_obj.symtab:
254 filename = None
255 lineno = 0
256 else:
257 filename = symbol_and_line_obj.symtab.fullname()
258 lineno = symbol_and_line_obj.line
259 if pygments:
260 lexer = pygments.lexers.CLexer(stripall=False)
261
262 return SourceFileDescriptor(filename, lexer), lineno
263
264 @default_selected_gdb_frame()
265 def get_source_line(self, frame):
266 source_desc, lineno = self.get_source_desc()
267 return source_desc.get_source(lineno)
268
269 @default_selected_gdb_frame()
270 def is_relevant_function(self, frame):
271 """
272 returns whether we care about a frame on the user-level when debugging
273 Cython code
274 """
275 name = frame.name()
276 older_frame = frame.older()
277 if self.is_cython_function(frame) or self.is_python_function(frame):
278 return True
279 elif older_frame and self.is_cython_function(older_frame):
280 # check for direct C function call from a Cython function
281 cython_func = self.get_cython_function(older_frame)
282 return name in cython_func.step_into_functions
283
284 return False
285
286 @default_selected_gdb_frame(err=False)
287 def print_stackframe(self, frame, index, is_c=False):
288 """
289 Print a C, Cython or Python stack frame and the line of source code
290 if available.
291 """
292 # do this to prevent the require_cython_frame decorator from
293 # raising GdbError when calling self.cy.cy_cvalue.invoke()
294 selected_frame = gdb.selected_frame()
295 frame.select()
296
297 try:
298 source_desc, lineno = self.get_source_desc(frame)
299 except NoFunctionNameInFrameError:
300 print '#%-2d Unknown Frame (compile with -g)' % index
301 return
302
303 if not is_c and self.is_python_function(frame):
304 pyframe = libpython.Frame(frame).get_pyop()
305 if pyframe is None or pyframe.is_optimized_out():
306 # print this python function as a C function
307 return self.print_stackframe(frame, index, is_c=True)
308
309 func_name = pyframe.co_name
310 func_cname = 'PyEval_EvalFrameEx'
311 func_args = []
312 elif self.is_cython_function(frame):
313 cyfunc = self.get_cython_function(frame)
314 f = lambda arg: self.cy.cy_cvalue.invoke(arg, frame=frame)
315
316 func_name = cyfunc.name
317 func_cname = cyfunc.cname
318 func_args = [] # [(arg, f(arg)) for arg in cyfunc.arguments]
319 else:
320 source_desc, lineno = self.get_source_desc(frame)
321 func_name = frame.name()
322 func_cname = func_name
323 func_args = []
324
325 try:
326 gdb_value = gdb.parse_and_eval(func_cname)
327 except RuntimeError:
328 func_address = 0
329 else:
330 # Seriously? Why is the address not an int?
331 func_address = int(str(gdb_value.address).split()[0], 0)
332
333 a = ', '.join('%s=%s' % (name, val) for name, val in func_args)
334 print '#%-2d 0x%016x in %s(%s)' % (index, func_address, func_name, a),
335
336 if source_desc.filename is not None:
337 print 'at %s:%s' % (source_desc.filename, lineno),
338
339 print
340
341 try:
342 print ' ' + source_desc.get_source(lineno)
343 except gdb.GdbError:
344 pass
345
346 selected_frame.select()
347
348 def get_remote_cython_globals_dict(self):
349 m = gdb.parse_and_eval('__pyx_m')
350
351 try:
352 PyModuleObject = gdb.lookup_type('PyModuleObject')
353 except RuntimeError:
354 raise gdb.GdbError(textwrap.dedent("""\
355 Unable to lookup type PyModuleObject, did you compile python
356 with debugging support (-g)?"""))
357
358 m = m.cast(PyModuleObject.pointer())
359 return m['md_dict']
360
361
362 def get_cython_globals_dict(self):
363 """
364 Get the Cython globals dict where the remote names are turned into
365 local strings.
366 """
367 remote_dict = self.get_remote_cython_globals_dict()
368 pyobject_dict = libpython.PyObjectPtr.from_pyobject_ptr(remote_dict)
369
370 result = {}
371 seen = set()
372 for k, v in pyobject_dict.iteritems():
373 result[k.proxyval(seen)] = v
374
375 return result
376
377 def print_gdb_value(self, name, value, max_name_length=None, prefix=''):
378 if libpython.pretty_printer_lookup(value):
379 typename = ''
380 else:
381 typename = '(%s) ' % (value.type,)
382
383 if max_name_length is None:
384 print '%s%s = %s%s' % (prefix, name, typename, value)
385 else:
386 print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename,
387 value)
388
389 def is_initialized(self, cython_func, local_name):
390 cyvar = cython_func.locals[local_name]
391 cur_lineno = self.get_cython_lineno()
392
393 if '->' in cyvar.cname:
394 # Closed over free variable
395 if cur_lineno > cython_func.lineno:
396 if cyvar.type == PythonObject:
397 return long(gdb.parse_and_eval(cyvar.cname))
398 return True
399 return False
400
401 return cur_lineno > cyvar.lineno
402
403
404 class SourceFileDescriptor(object):
405 def __init__(self, filename, lexer, formatter=None):
406 self.filename = filename
407 self.lexer = lexer
408 self.formatter = formatter
409
410 def valid(self):
411 return self.filename is not None
412
413 def lex(self, code):
414 if pygments and self.lexer and parameters.colorize_code:
415 bg = parameters.terminal_background.value
416 if self.formatter is None:
417 formatter = pygments.formatters.TerminalFormatter(bg=bg)
418 else:
419 formatter = self.formatter
420
421 return pygments.highlight(code, self.lexer, formatter)
422
423 return code
424
425 def _get_source(self, start, stop, lex_source, mark_line, lex_entire):
426 with open(self.filename) as f:
427 # to provide "correct" colouring, the entire code needs to be
428 # lexed. However, this makes a lot of things terribly slow, so
429 # we decide not to. Besides, it's unlikely to matter.
430
431 if lex_source and lex_entire:
432 f = self.lex(f.read()).splitlines()
433
434 slice = itertools.islice(f, start - 1, stop - 1)
435
436 for idx, line in enumerate(slice):
437 if start + idx == mark_line:
438 prefix = '>'
439 else:
440 prefix = ' '
441
442 if lex_source and not lex_entire:
443 line = self.lex(line)
444
445 yield '%s %4d %s' % (prefix, start + idx, line.rstrip())
446
447 def get_source(self, start, stop=None, lex_source=True, mark_line=0,
448 lex_entire=False):
449 exc = gdb.GdbError('Unable to retrieve source code')
450
451 if not self.filename:
452 raise exc
453
454 start = max(start, 1)
455 if stop is None:
456 stop = start + 1
457
458 try:
459 return '\n'.join(
460 self._get_source(start, stop, lex_source, mark_line, lex_entire) )
461 except IOError:
462 raise exc
463
464
465 # Errors
466
467 class CyGDBError(gdb.GdbError):
468 """
469 Base class for Cython-command related erorrs
470 """
471
472 def __init__(self, *args):
473 args = args or (self.msg,)
474 super(CyGDBError, self).__init__(*args)
475
476 class NoCythonFunctionInFrameError(CyGDBError):
477 """
478 raised when the user requests the current cython function, which is
479 unavailable
480 """
481 msg = "Current function is a function cygdb doesn't know about"
482
483 class NoFunctionNameInFrameError(NoCythonFunctionInFrameError):
484 """
485 raised when the name of the C function could not be determined
486 in the current C stack frame
487 """
488 msg = ('C function name could not be determined in the current C stack '
489 'frame')
490
491
492 # Parameters
493
494 class CythonParameter(gdb.Parameter):
495 """
496 Base class for cython parameters
497 """
498
499 def __init__(self, name, command_class, parameter_class, default=None):
500 self.show_doc = self.set_doc = self.__class__.__doc__
501 super(CythonParameter, self).__init__(name, command_class,
502 parameter_class)
503 if default is not None:
504 self.value = default
505
506 def __nonzero__(self):
507 return bool(self.value)
508
509 __bool__ = __nonzero__ # python 3
510
511 class CompleteUnqualifiedFunctionNames(CythonParameter):
512 """
513 Have 'cy break' complete unqualified function or method names.
514 """
515
516 class ColorizeSourceCode(CythonParameter):
517 """
518 Tell cygdb whether to colorize source code.
519 """
520
521 class TerminalBackground(CythonParameter):
522 """
523 Tell cygdb about the user's terminal background (light or dark).
524 """
525
526 class CythonParameters(object):
527 """
528 Simple container class that might get more functionality in the distant
529 future (mostly to remind us that we're dealing with parameters).
530 """
531
532 def __init__(self):
533 self.complete_unqualified = CompleteUnqualifiedFunctionNames(
534 'cy_complete_unqualified',
535 gdb.COMMAND_BREAKPOINTS,
536 gdb.PARAM_BOOLEAN,
537 True)
538 self.colorize_code = ColorizeSourceCode(
539 'cy_colorize_code',
540 gdb.COMMAND_FILES,
541 gdb.PARAM_BOOLEAN,
542 True)
543 self.terminal_background = TerminalBackground(
544 'cy_terminal_background_color',
545 gdb.COMMAND_FILES,
546 gdb.PARAM_STRING,
547 "dark")
548
549 parameters = CythonParameters()
550
551
552 # Commands
553
554 class CythonCommand(gdb.Command, CythonBase):
555 """
556 Base class for Cython commands
557 """
558
559 command_class = gdb.COMMAND_NONE
560
561 @classmethod
562 def _register(cls, clsname, args, kwargs):
563 if not hasattr(cls, 'completer_class'):
564 return cls(clsname, cls.command_class, *args, **kwargs)
565 else:
566 return cls(clsname, cls.command_class, cls.completer_class,
567 *args, **kwargs)
568
569 @classmethod
570 def register(cls, *args, **kwargs):
571 alias = getattr(cls, 'alias', None)
572 if alias:
573 cls._register(cls.alias, args, kwargs)
574
575 return cls._register(cls.name, args, kwargs)
576
577
578 class CyCy(CythonCommand):
579 """
580 Invoke a Cython command. Available commands are:
581
582 cy import
583 cy break
584 cy step
585 cy next
586 cy run
587 cy cont
588 cy finish
589 cy up
590 cy down
591 cy select
592 cy bt / cy backtrace
593 cy list
594 cy print
595 cy set
596 cy locals
597 cy globals
598 cy exec
599 """
600
601 name = 'cy'
602 command_class = gdb.COMMAND_NONE
603 completer_class = gdb.COMPLETE_COMMAND
604
605 def __init__(self, name, command_class, completer_class):
606 # keep the signature 2.5 compatible (i.e. do not use f(*a, k=v)
607 super(CythonCommand, self).__init__(name, command_class,
608 completer_class, prefix=True)
609
610 commands = dict(
611 # GDB commands
612 import_ = CyImport.register(),
613 break_ = CyBreak.register(),
614 step = CyStep.register(),
615 next = CyNext.register(),
616 run = CyRun.register(),
617 cont = CyCont.register(),
618 finish = CyFinish.register(),
619 up = CyUp.register(),
620 down = CyDown.register(),
621 select = CySelect.register(),
622 bt = CyBacktrace.register(),
623 list = CyList.register(),
624 print_ = CyPrint.register(),
625 locals = CyLocals.register(),
626 globals = CyGlobals.register(),
627 exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'),
628 _exec = CyExec.register(),
629 set = CySet.register(),
630
631 # GDB functions
632 cy_cname = CyCName('cy_cname'),
633 cy_cvalue = CyCValue('cy_cvalue'),
634 cy_lineno = CyLine('cy_lineno'),
635 cy_eval = CyEval('cy_eval'),
636 )
637
638 for command_name, command in commands.iteritems():
639 command.cy = self
640 setattr(self, command_name, command)
641
642 self.cy = self
643
644 # Cython module namespace
645 self.cython_namespace = {}
646
647 # maps (unique) qualified function names (e.g.
648 # cythonmodule.ClassName.method_name) to the CythonFunction object
649 self.functions_by_qualified_name = {}
650
651 # unique cnames of Cython functions
652 self.functions_by_cname = {}
653
654 # map function names like method_name to a list of all such
655 # CythonFunction objects
656 self.functions_by_name = collections.defaultdict(list)
657
658
659 class CyImport(CythonCommand):
660 """
661 Import debug information outputted by the Cython compiler
662 Example: cy import FILE...
663 """
664
665 name = 'cy import'
666 command_class = gdb.COMMAND_STATUS
667 completer_class = gdb.COMPLETE_FILENAME
668
669 def invoke(self, args, from_tty):
670 args = args.encode(_filesystemencoding)
671 for arg in string_to_argv(args):
672 try:
673 f = open(arg)
674 except OSError, e:
675 raise gdb.GdbError('Unable to open file %r: %s' %
676 (args, e.args[1]))
677
678 t = etree.parse(f)
679
680 for module in t.getroot():
681 cython_module = CythonModule(**module.attrib)
682 self.cy.cython_namespace[cython_module.name] = cython_module
683
684 for variable in module.find('Globals'):
685 d = variable.attrib
686 cython_module.globals[d['name']] = CythonVariable(**d)
687
688 for function in module.find('Functions'):
689 cython_function = CythonFunction(module=cython_module,
690 **function.attrib)
691
692 # update the global function mappings
693 name = cython_function.name
694 qname = cython_function.qualified_name
695
696 self.cy.functions_by_name[name].append(cython_function)
697 self.cy.functions_by_qualified_name[
698 cython_function.qualified_name] = cython_function
699 self.cy.functions_by_cname[
700 cython_function.cname] = cython_function
701
702 d = cython_module.functions[qname] = cython_function
703
704 for local in function.find('Locals'):
705 d = local.attrib
706 cython_function.locals[d['name']] = CythonVariable(**d)
707
708 for step_into_func in function.find('StepIntoFunctions'):
709 d = step_into_func.attrib
710 cython_function.step_into_functions.add(d['name'])
711
712 cython_function.arguments.extend(
713 funcarg.tag for funcarg in function.find('Arguments'))
714
715 for marker in module.find('LineNumberMapping'):
716 cython_lineno = int(marker.attrib['cython_lineno'])
717 c_linenos = map(int, marker.attrib['c_linenos'].split())
718 cython_module.lineno_cy2c[cython_lineno] = min(c_linenos)
719 for c_lineno in c_linenos:
720 cython_module.lineno_c2cy[c_lineno] = cython_lineno
721
722
723 class CyBreak(CythonCommand):
724 """
725 Set a breakpoint for Cython code using Cython qualified name notation, e.g.:
726
727 cy break cython_modulename.ClassName.method_name...
728
729 or normal notation:
730
731 cy break function_or_method_name...
732
733 or for a line number:
734
735 cy break cython_module:lineno...
736
737 Set a Python breakpoint:
738 Break on any function or method named 'func' in module 'modname'
739
740 cy break -p modname.func...
741
742 Break on any function or method named 'func'
743
744 cy break -p func...
745 """
746
747 name = 'cy break'
748 command_class = gdb.COMMAND_BREAKPOINTS
749
750 def _break_pyx(self, name):
751 modulename, _, lineno = name.partition(':')
752 lineno = int(lineno)
753 if modulename:
754 cython_module = self.cy.cython_namespace[modulename]
755 else:
756 cython_module = self.get_cython_function().module
757
758 if lineno in cython_module.lineno_cy2c:
759 c_lineno = cython_module.lineno_cy2c[lineno]
760 breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno)
761 gdb.execute('break ' + breakpoint)
762 else:
763 raise gdb.GdbError("Not a valid line number. "
764 "Does it contain actual code?")
765
766 def _break_funcname(self, funcname):
767 func = self.cy.functions_by_qualified_name.get(funcname)
768
769 if func and func.is_initmodule_function:
770 func = None
771
772 break_funcs = [func]
773
774 if not func:
775 funcs = self.cy.functions_by_name.get(funcname) or []
776 funcs = [f for f in funcs if not f.is_initmodule_function]
777
778 if not funcs:
779 gdb.execute('break ' + funcname)
780 return
781
782 if len(funcs) > 1:
783 # multiple functions, let the user pick one
784 print 'There are multiple such functions:'
785 for idx, func in enumerate(funcs):
786 print '%3d) %s' % (idx, func.qualified_name)
787
788 while True:
789 try:
790 result = raw_input(
791 "Select a function, press 'a' for all "
792 "functions or press 'q' or '^D' to quit: ")
793 except EOFError:
794 return
795 else:
796 if result.lower() == 'q':
797 return
798 elif result.lower() == 'a':
799 break_funcs = funcs
800 break
801 elif (result.isdigit() and
802 0 <= int(result) < len(funcs)):
803 break_funcs = [funcs[int(result)]]
804 break
805 else:
806 print 'Not understood...'
807 else:
808 break_funcs = [funcs[0]]
809
810 for func in break_funcs:
811 gdb.execute('break %s' % func.cname)
812 if func.pf_cname:
813 gdb.execute('break %s' % func.pf_cname)
814
815 def invoke(self, function_names, from_tty):
816 argv = string_to_argv(function_names.encode('UTF-8'))
817 if function_names.startswith('-p'):
818 argv = argv[1:]
819 python_breakpoints = True
820 else:
821 python_breakpoints = False
822
823 for funcname in argv:
824 if python_breakpoints:
825 gdb.execute('py-break %s' % funcname)
826 elif ':' in funcname:
827 self._break_pyx(funcname)
828 else:
829 self._break_funcname(funcname)
830
831 @dont_suppress_errors
832 def complete(self, text, word):
833 # Filter init-module functions (breakpoints can be set using
834 # modulename:linenumber).
835 names = [n for n, L in self.cy.functions_by_name.iteritems()
836 if any(not f.is_initmodule_function for f in L)]
837 qnames = [n for n, f in self.cy.functions_by_qualified_name.iteritems()
838 if not f.is_initmodule_function]
839
840 if parameters.complete_unqualified:
841 all_names = itertools.chain(qnames, names)
842 else:
843 all_names = qnames
844
845 words = text.strip().split()
846 if not words or '.' not in words[-1]:
847 # complete unqualified
848 seen = set(text[:-len(word)].split())
849 return [n for n in all_names
850 if n.startswith(word) and n not in seen]
851
852 # complete qualified name
853 lastword = words[-1]
854 compl = [n for n in qnames if n.startswith(lastword)]
855
856 if len(lastword) > len(word):
857 # readline sees something (e.g. a '.') as a word boundary, so don't
858 # "recomplete" this prefix
859 strip_prefix_length = len(lastword) - len(word)
860 compl = [n[strip_prefix_length:] for n in compl]
861
862 return compl
863
864
865 class CythonInfo(CythonBase, libpython.PythonInfo):
866 """
867 Implementation of the interface dictated by libpython.LanguageInfo.
868 """
869
870 def lineno(self, frame):
871 # Take care of the Python and Cython levels. We need to care for both
872 # as we can't simply dispath to 'py-step', since that would work for
873 # stepping through Python code, but it would not step back into Cython-
874 # related code. The C level should be dispatched to the 'step' command.
875 if self.is_cython_function(frame):
876 return self.get_cython_lineno(frame)
877 return super(CythonInfo, self).lineno(frame)
878
879 def get_source_line(self, frame):
880 try:
881 line = super(CythonInfo, self).get_source_line(frame)
882 except gdb.GdbError:
883 return None
884 else:
885 return line.strip() or None
886
887 def exc_info(self, frame):
888 if self.is_python_function:
889 return super(CythonInfo, self).exc_info(frame)
890
891 def runtime_break_functions(self):
892 if self.is_cython_function():
893 return self.get_cython_function().step_into_functions
894 return ()
895
896 def static_break_functions(self):
897 result = ['PyEval_EvalFrameEx']
898 result.extend(self.cy.functions_by_cname)
899 return result
900
901
902 class CythonExecutionControlCommand(CythonCommand,
903 libpython.ExecutionControlCommandBase):
904
905 @classmethod
906 def register(cls):
907 return cls(cls.name, cython_info)
908
909
910 class CyStep(CythonExecutionControlCommand, libpython.PythonStepperMixin):
911 "Step through Cython, Python or C code."
912
913 name = 'cy -step'
914 stepinto = True
915
916 def invoke(self, args, from_tty):
917 if self.is_python_function():
918 self.python_step(self.stepinto)
919 elif not self.is_cython_function():
920 if self.stepinto:
921 command = 'step'
922 else:
923 command = 'next'
924
925 self.finish_executing(gdb.execute(command, to_string=True))
926 else:
927 self.step(stepinto=self.stepinto)
928
929
930 class CyNext(CyStep):
931 "Step-over Cython, Python or C code."
932
933 name = 'cy -next'
934 stepinto = False
935
936
937 class CyRun(CythonExecutionControlCommand):
938 """
939 Run a Cython program. This is like the 'run' command, except that it
940 displays Cython or Python source lines as well
941 """
942
943 name = 'cy run'
944
945 invoke = CythonExecutionControlCommand.run
946
947
948 class CyCont(CythonExecutionControlCommand):
949 """
950 Continue a Cython program. This is like the 'run' command, except that it
951 displays Cython or Python source lines as well.
952 """
953
954 name = 'cy cont'
955 invoke = CythonExecutionControlCommand.cont
956
957
958 class CyFinish(CythonExecutionControlCommand):
959 """
960 Execute until the function returns.
961 """
962 name = 'cy finish'
963
964 invoke = CythonExecutionControlCommand.finish
965
966
967 class CyUp(CythonCommand):
968 """
969 Go up a Cython, Python or relevant C frame.
970 """
971 name = 'cy up'
972 _command = 'up'
973
974 def invoke(self, *args):
975 try:
976 gdb.execute(self._command, to_string=True)
977 while not self.is_relevant_function(gdb.selected_frame()):
978 gdb.execute(self._command, to_string=True)
979 except RuntimeError, e:
980 raise gdb.GdbError(*e.args)
981
982 frame = gdb.selected_frame()
983 index = 0
984 while frame:
985 frame = frame.older()
986 index += 1
987
988 self.print_stackframe(index=index - 1)
989
990
991 class CyDown(CyUp):
992 """
993 Go down a Cython, Python or relevant C frame.
994 """
995
996 name = 'cy down'
997 _command = 'down'
998
999
1000 class CySelect(CythonCommand):
1001 """
1002 Select a frame. Use frame numbers as listed in `cy backtrace`.
1003 This command is useful because `cy backtrace` prints a reversed backtrace.
1004 """
1005
1006 name = 'cy select'
1007
1008 def invoke(self, stackno, from_tty):
1009 try:
1010 stackno = int(stackno)
1011 except ValueError:
1012 raise gdb.GdbError("Not a valid number: %r" % (stackno,))
1013
1014 frame = gdb.selected_frame()
1015 while frame.newer():
1016 frame = frame.newer()
1017
1018 stackdepth = libpython.stackdepth(frame)
1019
1020 try:
1021 gdb.execute('select %d' % (stackdepth - stackno - 1,))
1022 except RuntimeError, e:
1023 raise gdb.GdbError(*e.args)
1024
1025
1026 class CyBacktrace(CythonCommand):
1027 'Print the Cython stack'
1028
1029 name = 'cy bt'
1030 alias = 'cy backtrace'
1031 command_class = gdb.COMMAND_STACK
1032 completer_class = gdb.COMPLETE_NONE
1033
1034 @require_running_program
1035 def invoke(self, args, from_tty):
1036 # get the first frame
1037 frame = gdb.selected_frame()
1038 while frame.older():
1039 frame = frame.older()
1040
1041 print_all = args == '-a'
1042
1043 index = 0
1044 while frame:
1045 try:
1046 is_relevant = self.is_relevant_function(frame)
1047 except CyGDBError:
1048 is_relevant = False
1049
1050 if print_all or is_relevant:
1051 self.print_stackframe(frame, index)
1052
1053 index += 1
1054 frame = frame.newer()
1055
1056
1057 class CyList(CythonCommand):
1058 """
1059 List Cython source code. To disable to customize colouring see the cy_*
1060 parameters.
1061 """
1062
1063 name = 'cy list'
1064 command_class = gdb.COMMAND_FILES
1065 completer_class = gdb.COMPLETE_NONE
1066
1067 # @dispatch_on_frame(c_command='list')
1068 def invoke(self, _, from_tty):
1069 sd, lineno = self.get_source_desc()
1070 source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno,
1071 lex_entire=True)
1072 print source
1073
1074
1075 class CyPrint(CythonCommand):
1076 """
1077 Print a Cython variable using 'cy-print x' or 'cy-print module.function.x'
1078 """
1079
1080 name = 'cy print'
1081 command_class = gdb.COMMAND_DATA
1082
1083 def invoke(self, name, from_tty, max_name_length=None):
1084 if self.is_python_function():
1085 return gdb.execute('py-print ' + name)
1086 elif self.is_cython_function():
1087 value = self.cy.cy_cvalue.invoke(name.lstrip('*'))
1088 for c in name:
1089 if c == '*':
1090 value = value.dereference()
1091 else:
1092 break
1093
1094 self.print_gdb_value(name, value, max_name_length)
1095 else:
1096 gdb.execute('print ' + name)
1097
1098 def complete(self):
1099 if self.is_cython_function():
1100 f = self.get_cython_function()
1101 return list(itertools.chain(f.locals, f.globals))
1102 else:
1103 return []
1104
1105
1106 sortkey = lambda (name, value): name.lower()
1107
1108 class CyLocals(CythonCommand):
1109 """
1110 List the locals from the current Cython frame.
1111 """
1112
1113 name = 'cy locals'
1114 command_class = gdb.COMMAND_STACK
1115 completer_class = gdb.COMPLETE_NONE
1116
1117 @dispatch_on_frame(c_command='info locals', python_command='py-locals')
1118 def invoke(self, args, from_tty):
1119 cython_function = self.get_cython_function()
1120
1121 if cython_function.is_initmodule_function:
1122 self.cy.globals.invoke(args, from_tty)
1123 return
1124
1125 local_cython_vars = cython_function.locals
1126 max_name_length = len(max(local_cython_vars, key=len))
1127 for name, cyvar in sorted(local_cython_vars.iteritems(), key=sortkey):
1128 if self.is_initialized(self.get_cython_function(), cyvar.name):
1129 value = gdb.parse_and_eval(cyvar.cname)
1130 if not value.is_optimized_out:
1131 self.print_gdb_value(cyvar.name, value,
1132 max_name_length, '')
1133
1134
1135 class CyGlobals(CyLocals):
1136 """
1137 List the globals from the current Cython module.
1138 """
1139
1140 name = 'cy globals'
1141 command_class = gdb.COMMAND_STACK
1142 completer_class = gdb.COMPLETE_NONE
1143
1144 @dispatch_on_frame(c_command='info variables', python_command='py-globals')
1145 def invoke(self, args, from_tty):
1146 global_python_dict = self.get_cython_globals_dict()
1147 module_globals = self.get_cython_function().module.globals
1148
1149 max_globals_len = 0
1150 max_globals_dict_len = 0
1151 if module_globals:
1152 max_globals_len = len(max(module_globals, key=len))
1153 if global_python_dict:
1154 max_globals_dict_len = len(max(global_python_dict))
1155
1156 max_name_length = max(max_globals_len, max_globals_dict_len)
1157
1158 seen = set()
1159 print 'Python globals:'
1160 for k, v in sorted(global_python_dict.iteritems(), key=sortkey):
1161 v = v.get_truncated_repr(libpython.MAX_OUTPUT_LEN)
1162 seen.add(k)
1163 print ' %-*s = %s' % (max_name_length, k, v)
1164
1165 print 'C globals:'
1166 for name, cyvar in sorted(module_globals.iteritems(), key=sortkey):
1167 if name not in seen:
1168 try:
1169 value = gdb.parse_and_eval(cyvar.cname)
1170 except RuntimeError:
1171 pass
1172 else:
1173 if not value.is_optimized_out:
1174 self.print_gdb_value(cyvar.name, value,
1175 max_name_length, ' ')
1176
1177
1178
1179 class EvaluateOrExecuteCodeMixin(object):
1180 """
1181 Evaluate or execute Python code in a Cython or Python frame. The 'evalcode'
1182 method evaluations Python code, prints a traceback if an exception went
1183 uncaught, and returns any return value as a gdb.Value (NULL on exception).
1184 """
1185
1186 def _fill_locals_dict(self, executor, local_dict_pointer):
1187 "Fill a remotely allocated dict with values from the Cython C stack"
1188 cython_func = self.get_cython_function()
1189
1190 for name, cyvar in cython_func.locals.iteritems():
1191 if (cyvar.type == PythonObject and
1192 self.is_initialized(cython_func, name)):
1193
1194 try:
1195 val = gdb.parse_and_eval(cyvar.cname)
1196 except RuntimeError:
1197 continue
1198 else:
1199 if val.is_optimized_out:
1200 continue
1201
1202 pystringp = executor.alloc_pystring(name)
1203 code = '''
1204 (PyObject *) PyDict_SetItem(
1205 (PyObject *) %d,
1206 (PyObject *) %d,
1207 (PyObject *) %s)
1208 ''' % (local_dict_pointer, pystringp, cyvar.cname)
1209
1210 try:
1211 if gdb.parse_and_eval(code) < 0:
1212 gdb.parse_and_eval('PyErr_Print()')
1213 raise gdb.GdbError("Unable to execute Python code.")
1214 finally:
1215 # PyDict_SetItem doesn't steal our reference
1216 executor.xdecref(pystringp)
1217
1218 def _find_first_cython_or_python_frame(self):
1219 frame = gdb.selected_frame()
1220 while frame:
1221 if (self.is_cython_function(frame) or
1222 self.is_python_function(frame)):
1223 frame.select()
1224 return frame
1225
1226 frame = frame.older()
1227
1228 raise gdb.GdbError("There is no Cython or Python frame on the stack.")
1229
1230
1231 def _evalcode_cython(self, executor, code, input_type):
1232 with libpython.FetchAndRestoreError():
1233 # get the dict of Cython globals and construct a dict in the
1234 # inferior with Cython locals
1235 global_dict = gdb.parse_and_eval(
1236 '(PyObject *) PyModule_GetDict(__pyx_m)')
1237 local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()')
1238
1239 try:
1240 self._fill_locals_dict(executor,
1241 libpython.pointervalue(local_dict))
1242 result = executor.evalcode(code, input_type, global_dict,
1243 local_dict)
1244 finally:
1245 executor.xdecref(libpython.pointervalue(local_dict))
1246
1247 return result
1248
1249 def evalcode(self, code, input_type):
1250 """
1251 Evaluate `code` in a Python or Cython stack frame using the given
1252 `input_type`.
1253 """
1254 frame = self._find_first_cython_or_python_frame()
1255 executor = libpython.PythonCodeExecutor()
1256 if self.is_python_function(frame):
1257 return libpython._evalcode_python(executor, code, input_type)
1258 return self._evalcode_cython(executor, code, input_type)
1259
1260
1261 class CyExec(CythonCommand, libpython.PyExec, EvaluateOrExecuteCodeMixin):
1262 """
1263 Execute Python code in the nearest Python or Cython frame.
1264 """
1265
1266 name = '-cy-exec'
1267 command_class = gdb.COMMAND_STACK
1268 completer_class = gdb.COMPLETE_NONE
1269
1270 def invoke(self, expr, from_tty):
1271 expr, input_type = self.readcode(expr)
1272 executor = libpython.PythonCodeExecutor()
1273 executor.xdecref(self.evalcode(expr, executor.Py_single_input))
1274
1275
1276 class CySet(CythonCommand):
1277 """
1278 Set a Cython variable to a certain value
1279
1280 cy set my_cython_c_variable = 10
1281 cy set my_cython_py_variable = $cy_eval("{'doner': 'kebab'}")
1282
1283 This is equivalent to
1284
1285 set $cy_value("my_cython_variable") = 10
1286 """
1287
1288 name = 'cy set'
1289 command_class = gdb.COMMAND_DATA
1290 completer_class = gdb.COMPLETE_NONE
1291
1292 @require_cython_frame
1293 def invoke(self, expr, from_tty):
1294 name_and_expr = expr.split('=', 1)
1295 if len(name_and_expr) != 2:
1296 raise gdb.GdbError("Invalid expression. Use 'cy set var = expr'.")
1297
1298 varname, expr = name_and_expr
1299 cname = self.cy.cy_cname.invoke(varname.strip())
1300 gdb.execute("set %s = %s" % (cname, expr))
1301
1302
1303 # Functions
1304
1305 class CyCName(gdb.Function, CythonBase):
1306 """
1307 Get the C name of a Cython variable in the current context.
1308 Examples:
1309
1310 print $cy_cname("function")
1311 print $cy_cname("Class.method")
1312 print $cy_cname("module.function")
1313 """
1314
1315 @require_cython_frame
1316 @gdb_function_value_to_unicode
1317 def invoke(self, cyname, frame=None):
1318 frame = frame or gdb.selected_frame()
1319 cname = None
1320
1321 if self.is_cython_function(frame):
1322 cython_function = self.get_cython_function(frame)
1323 if cyname in cython_function.locals:
1324 cname = cython_function.locals[cyname].cname
1325 elif cyname in cython_function.module.globals:
1326 cname = cython_function.module.globals[cyname].cname
1327 else:
1328 qname = '%s.%s' % (cython_function.module.name, cyname)
1329 if qname in cython_function.module.functions:
1330 cname = cython_function.module.functions[qname].cname
1331
1332 if not cname:
1333 cname = self.cy.functions_by_qualified_name.get(cyname)
1334
1335 if not cname:
1336 raise gdb.GdbError('No such Cython variable: %s' % cyname)
1337
1338 return cname
1339
1340
1341 class CyCValue(CyCName):
1342 """
1343 Get the value of a Cython variable.
1344 """
1345
1346 @require_cython_frame
1347 @gdb_function_value_to_unicode
1348 def invoke(self, cyname, frame=None):
1349 globals_dict = self.get_cython_globals_dict()
1350 cython_function = self.get_cython_function(frame)
1351
1352 if self.is_initialized(cython_function, cyname):
1353 cname = super(CyCValue, self).invoke(cyname, frame=frame)
1354 return gdb.parse_and_eval(cname)
1355 elif cyname in globals_dict:
1356 return globals_dict[cyname]._gdbval
1357 else:
1358 raise gdb.GdbError("Variable %s is not initialized." % cyname)
1359
1360
1361 class CyLine(gdb.Function, CythonBase):
1362 """
1363 Get the current Cython line.
1364 """
1365
1366 @require_cython_frame
1367 def invoke(self):
1368 return self.get_cython_lineno()
1369
1370
1371 class CyEval(gdb.Function, CythonBase, EvaluateOrExecuteCodeMixin):
1372 """
1373 Evaluate Python code in the nearest Python or Cython frame and return
1374 """
1375
1376 @gdb_function_value_to_unicode
1377 def invoke(self, python_expression):
1378 input_type = libpython.PythonCodeExecutor.Py_eval_input
1379 return self.evalcode(python_expression, input_type)
1380
1381
1382 cython_info = CythonInfo()
1383 cy = CyCy.register()
1384 cython_info.cy = cy
1385
1386 def register_defines():
1387 libpython.source_gdb_script(textwrap.dedent("""\
1388 define cy step
1389 cy -step
1390 end
1391
1392 define cy next
1393 cy -next
1394 end
1395
1396 document cy step
1397 %s
1398 end
1399
1400 document cy next
1401 %s
1402 end
1403 """) % (CyStep.__doc__, CyNext.__doc__))
1404
1405 register_defines()
OLDNEW
« no previous file with comments | « third_party/cython/src/Cython/Debugger/__init__.py ('k') | third_party/cython/src/Cython/Debugger/libpython.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698