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