libcython.py revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1"""
2GDB extension that adds Cython support.
3"""
4
5from __future__ import with_statement
6
7import sys
8import textwrap
9import traceback
10import functools
11import itertools
12import collections
13
14import gdb
15
16try:
17    from lxml import etree
18    have_lxml = True
19except 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
36try:
37    import pygments.lexers
38    import pygments.formatters
39except ImportError:
40    pygments = None
41    sys.stderr.write("Install pygments for colorized source code.\n")
42
43if hasattr(gdb, 'string_to_argv'):
44    from gdb import string_to_argv
45else:
46    from shlex import split as string_to_argv
47
48from Cython.Debugger import libpython
49
50# C or Python type
51CObject = 'CObject'
52PythonObject = 'PythonObject'
53
54_data_types = dict(CObject=CObject, PythonObject=PythonObject)
55_filesystemencoding = sys.getfilesystemencoding() or 'UTF-8'
56
57# decorators
58
59def 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
71def 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
87def 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
98def 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
118def 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
130def 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
143class 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
155class 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
164class 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
189class 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
404class 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
467class 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
476class 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
483class 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
494class 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
511class CompleteUnqualifiedFunctionNames(CythonParameter):
512    """
513    Have 'cy break' complete unqualified function or method names.
514    """
515
516class ColorizeSourceCode(CythonParameter):
517    """
518    Tell cygdb whether to colorize source code.
519    """
520
521class TerminalBackground(CythonParameter):
522    """
523    Tell cygdb about the user's terminal background (light or dark).
524    """
525
526class 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
549parameters = CythonParameters()
550
551
552# Commands
553
554class 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
578class 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
659class 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
723class 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
865class 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
902class CythonExecutionControlCommand(CythonCommand,
903                                    libpython.ExecutionControlCommandBase):
904
905    @classmethod
906    def register(cls):
907        return cls(cls.name, cython_info)
908
909
910class 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
930class CyNext(CyStep):
931    "Step-over Cython, Python or C code."
932
933    name = 'cy -next'
934    stepinto = False
935
936
937class 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
948class 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
958class CyFinish(CythonExecutionControlCommand):
959    """
960    Execute until the function returns.
961    """
962    name = 'cy finish'
963
964    invoke = CythonExecutionControlCommand.finish
965
966
967class 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
991class CyDown(CyUp):
992    """
993    Go down a Cython, Python or relevant C frame.
994    """
995
996    name = 'cy down'
997    _command = 'down'
998
999
1000class 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
1026class 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
1057class 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
1075class 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
1106sortkey = lambda (name, value): name.lower()
1107
1108class 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
1135class 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
1179class 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
1261class 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
1276class 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
1305class 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
1341class 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
1361class 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
1371class 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
1382cython_info = CythonInfo()
1383cy = CyCy.register()
1384cython_info.cy = cy
1385
1386def 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
1405register_defines()
1406