Home | History | Annotate | Download | only in Debugger
      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()
   1406