diff options
author | alexv-smirnov <[email protected]> | 2023-03-28 22:25:04 +0300 |
---|---|---|
committer | alexv-smirnov <[email protected]> | 2023-03-28 22:25:04 +0300 |
commit | b8a17f9b1c166d2e9a26b99348a4c29d972caf55 (patch) | |
tree | 1a2d881f1a9452b9c6103dbf69d73da7624e98e5 /contrib/tools/cython/Cython/Debugger/libcython.py | |
parent | 25659221f18577ea38430a8ec3349836f5626b6a (diff) |
Revert ymake build from ydb oss export
Diffstat (limited to 'contrib/tools/cython/Cython/Debugger/libcython.py')
-rw-r--r-- | contrib/tools/cython/Cython/Debugger/libcython.py | 1434 |
1 files changed, 0 insertions, 1434 deletions
diff --git a/contrib/tools/cython/Cython/Debugger/libcython.py b/contrib/tools/cython/Cython/Debugger/libcython.py deleted file mode 100644 index 23153789b66..00000000000 --- a/contrib/tools/cython/Cython/Debugger/libcython.py +++ /dev/null @@ -1,1434 +0,0 @@ -""" -GDB extension that adds Cython support. -""" - -from __future__ import print_function - -try: - input = raw_input -except NameError: - pass - -import sys -import textwrap -import traceback -import functools -import itertools -import collections - -import gdb - -try: # python 2 - UNICODE = unicode - BYTES = str -except NameError: # python 3 - UNICODE = str - BYTES = bytes - -try: - from lxml import etree - have_lxml = True -except ImportError: - have_lxml = False - try: - # Python 2.5 - from xml.etree import cElementTree as etree - except ImportError: - try: - # Python 2.5 - from xml.etree import ElementTree as etree - except ImportError: - try: - # normal cElementTree install - import cElementTree as etree - except ImportError: - # normal ElementTree install - import elementtree.ElementTree as etree - -try: - import pygments.lexers - import pygments.formatters -except ImportError: - pygments = None - sys.stderr.write("Install pygments for colorized source code.\n") - -if hasattr(gdb, 'string_to_argv'): - from gdb import string_to_argv -else: - from shlex import split as string_to_argv - -from Cython.Debugger import libpython - -# C or Python type -CObject = 'CObject' -PythonObject = 'PythonObject' - -_data_types = dict(CObject=CObject, PythonObject=PythonObject) -_filesystemencoding = sys.getfilesystemencoding() or 'UTF-8' - - -# decorators - -def dont_suppress_errors(function): - "*sigh*, readline" - @functools.wraps(function) - def wrapper(*args, **kwargs): - try: - return function(*args, **kwargs) - except Exception: - traceback.print_exc() - raise - - return wrapper - - -def default_selected_gdb_frame(err=True): - def decorator(function): - @functools.wraps(function) - def wrapper(self, frame=None, *args, **kwargs): - try: - frame = frame or gdb.selected_frame() - except RuntimeError: - raise gdb.GdbError("No frame is currently selected.") - - if err and frame.name() is None: - raise NoFunctionNameInFrameError() - - return function(self, frame, *args, **kwargs) - return wrapper - return decorator - - -def require_cython_frame(function): - @functools.wraps(function) - @require_running_program - def wrapper(self, *args, **kwargs): - frame = kwargs.get('frame') or gdb.selected_frame() - if not self.is_cython_function(frame): - raise gdb.GdbError('Selected frame does not correspond with a ' - 'Cython function we know about.') - return function(self, *args, **kwargs) - return wrapper - - -def dispatch_on_frame(c_command, python_command=None): - def decorator(function): - @functools.wraps(function) - def wrapper(self, *args, **kwargs): - is_cy = self.is_cython_function() - is_py = self.is_python_function() - - if is_cy or (is_py and not python_command): - function(self, *args, **kwargs) - elif is_py: - gdb.execute(python_command) - elif self.is_relevant_function(): - gdb.execute(c_command) - else: - raise gdb.GdbError("Not a function cygdb knows about. " - "Use the normal GDB commands instead.") - - return wrapper - return decorator - - -def require_running_program(function): - @functools.wraps(function) - def wrapper(*args, **kwargs): - try: - gdb.selected_frame() - except RuntimeError: - raise gdb.GdbError("No frame is currently selected.") - - return function(*args, **kwargs) - return wrapper - - -def gdb_function_value_to_unicode(function): - @functools.wraps(function) - def wrapper(self, string, *args, **kwargs): - if isinstance(string, gdb.Value): - string = string.string() - - return function(self, string, *args, **kwargs) - return wrapper - - -# Classes that represent the debug information -# Don't rename the parameters of these classes, they come directly from the XML - -class CythonModule(object): - def __init__(self, module_name, filename, c_filename): - self.name = module_name - self.filename = filename - self.c_filename = c_filename - self.globals = {} - # {cython_lineno: min(c_linenos)} - self.lineno_cy2c = {} - # {c_lineno: cython_lineno} - self.lineno_c2cy = {} - self.functions = {} - - -class CythonVariable(object): - - def __init__(self, name, cname, qualified_name, type, lineno): - self.name = name - self.cname = cname - self.qualified_name = qualified_name - self.type = type - self.lineno = int(lineno) - - -class CythonFunction(CythonVariable): - def __init__(self, - module, - name, - cname, - pf_cname, - qualified_name, - lineno, - type=CObject, - is_initmodule_function="False"): - super(CythonFunction, self).__init__(name, - cname, - qualified_name, - type, - lineno) - self.module = module - self.pf_cname = pf_cname - self.is_initmodule_function = is_initmodule_function == "True" - self.locals = {} - self.arguments = [] - self.step_into_functions = set() - - -# General purpose classes - -class CythonBase(object): - - @default_selected_gdb_frame(err=False) - def is_cython_function(self, frame): - return frame.name() in self.cy.functions_by_cname - - @default_selected_gdb_frame(err=False) - def is_python_function(self, frame): - """ - Tells if a frame is associated with a Python function. - If we can't read the Python frame information, don't regard it as such. - """ - if frame.name() == 'PyEval_EvalFrameEx': - pyframe = libpython.Frame(frame).get_pyop() - return pyframe and not pyframe.is_optimized_out() - return False - - @default_selected_gdb_frame() - def get_c_function_name(self, frame): - return frame.name() - - @default_selected_gdb_frame() - def get_c_lineno(self, frame): - return frame.find_sal().line - - @default_selected_gdb_frame() - def get_cython_function(self, frame): - result = self.cy.functions_by_cname.get(frame.name()) - if result is None: - raise NoCythonFunctionInFrameError() - - return result - - @default_selected_gdb_frame() - def get_cython_lineno(self, frame): - """ - Get the current Cython line number. Returns 0 if there is no - correspondence between the C and Cython code. - """ - cyfunc = self.get_cython_function(frame) - return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame), 0) - - @default_selected_gdb_frame() - def get_source_desc(self, frame): - filename = lineno = lexer = None - if self.is_cython_function(frame): - filename = self.get_cython_function(frame).module.filename - lineno = self.get_cython_lineno(frame) - if pygments: - lexer = pygments.lexers.CythonLexer(stripall=False) - elif self.is_python_function(frame): - pyframeobject = libpython.Frame(frame).get_pyop() - - if not pyframeobject: - raise gdb.GdbError( - 'Unable to read information on python frame') - - filename = pyframeobject.filename() - lineno = pyframeobject.current_line_num() - - if pygments: - lexer = pygments.lexers.PythonLexer(stripall=False) - else: - symbol_and_line_obj = frame.find_sal() - if not symbol_and_line_obj or not symbol_and_line_obj.symtab: - filename = None - lineno = 0 - else: - filename = symbol_and_line_obj.symtab.fullname() - lineno = symbol_and_line_obj.line - if pygments: - lexer = pygments.lexers.CLexer(stripall=False) - - return SourceFileDescriptor(filename, lexer), lineno - - @default_selected_gdb_frame() - def get_source_line(self, frame): - source_desc, lineno = self.get_source_desc() - return source_desc.get_source(lineno) - - @default_selected_gdb_frame() - def is_relevant_function(self, frame): - """ - returns whether we care about a frame on the user-level when debugging - Cython code - """ - name = frame.name() - older_frame = frame.older() - if self.is_cython_function(frame) or self.is_python_function(frame): - return True - elif older_frame and self.is_cython_function(older_frame): - # check for direct C function call from a Cython function - cython_func = self.get_cython_function(older_frame) - return name in cython_func.step_into_functions - - return False - - @default_selected_gdb_frame(err=False) - def print_stackframe(self, frame, index, is_c=False): - """ - Print a C, Cython or Python stack frame and the line of source code - if available. - """ - # do this to prevent the require_cython_frame decorator from - # raising GdbError when calling self.cy.cy_cvalue.invoke() - selected_frame = gdb.selected_frame() - frame.select() - - try: - source_desc, lineno = self.get_source_desc(frame) - except NoFunctionNameInFrameError: - print('#%-2d Unknown Frame (compile with -g)' % index) - return - - if not is_c and self.is_python_function(frame): - pyframe = libpython.Frame(frame).get_pyop() - if pyframe is None or pyframe.is_optimized_out(): - # print this python function as a C function - return self.print_stackframe(frame, index, is_c=True) - - func_name = pyframe.co_name - func_cname = 'PyEval_EvalFrameEx' - func_args = [] - elif self.is_cython_function(frame): - cyfunc = self.get_cython_function(frame) - f = lambda arg: self.cy.cy_cvalue.invoke(arg, frame=frame) - - func_name = cyfunc.name - func_cname = cyfunc.cname - func_args = [] # [(arg, f(arg)) for arg in cyfunc.arguments] - else: - source_desc, lineno = self.get_source_desc(frame) - func_name = frame.name() - func_cname = func_name - func_args = [] - - try: - gdb_value = gdb.parse_and_eval(func_cname) - except RuntimeError: - func_address = 0 - else: - func_address = gdb_value.address - if not isinstance(func_address, int): - # Seriously? Why is the address not an int? - if not isinstance(func_address, (str, bytes)): - func_address = str(func_address) - func_address = int(func_address.split()[0], 0) - - a = ', '.join('%s=%s' % (name, val) for name, val in func_args) - sys.stdout.write('#%-2d 0x%016x in %s(%s)' % (index, func_address, func_name, a)) - - if source_desc.filename is not None: - sys.stdout.write(' at %s:%s' % (source_desc.filename, lineno)) - - sys.stdout.write('\n') - - try: - sys.stdout.write(' ' + source_desc.get_source(lineno)) - except gdb.GdbError: - pass - - selected_frame.select() - - def get_remote_cython_globals_dict(self): - m = gdb.parse_and_eval('__pyx_m') - - try: - PyModuleObject = gdb.lookup_type('PyModuleObject') - except RuntimeError: - raise gdb.GdbError(textwrap.dedent("""\ - Unable to lookup type PyModuleObject, did you compile python - with debugging support (-g)?""")) - - m = m.cast(PyModuleObject.pointer()) - return m['md_dict'] - - - def get_cython_globals_dict(self): - """ - Get the Cython globals dict where the remote names are turned into - local strings. - """ - remote_dict = self.get_remote_cython_globals_dict() - pyobject_dict = libpython.PyObjectPtr.from_pyobject_ptr(remote_dict) - - result = {} - seen = set() - for k, v in pyobject_dict.items(): - result[k.proxyval(seen)] = v - - return result - - def print_gdb_value(self, name, value, max_name_length=None, prefix=''): - if libpython.pretty_printer_lookup(value): - typename = '' - else: - typename = '(%s) ' % (value.type,) - - if max_name_length is None: - print('%s%s = %s%s' % (prefix, name, typename, value)) - else: - print('%s%-*s = %s%s' % (prefix, max_name_length, name, typename, value)) - - def is_initialized(self, cython_func, local_name): - cyvar = cython_func.locals[local_name] - cur_lineno = self.get_cython_lineno() - - if '->' in cyvar.cname: - # Closed over free variable - if cur_lineno > cython_func.lineno: - if cyvar.type == PythonObject: - return int(gdb.parse_and_eval(cyvar.cname)) - return True - return False - - return cur_lineno > cyvar.lineno - - -class SourceFileDescriptor(object): - def __init__(self, filename, lexer, formatter=None): - self.filename = filename - self.lexer = lexer - self.formatter = formatter - - def valid(self): - return self.filename is not None - - def lex(self, code): - if pygments and self.lexer and parameters.colorize_code: - bg = parameters.terminal_background.value - if self.formatter is None: - formatter = pygments.formatters.TerminalFormatter(bg=bg) - else: - formatter = self.formatter - - return pygments.highlight(code, self.lexer, formatter) - - return code - - def _get_source(self, start, stop, lex_source, mark_line, lex_entire): - with open(self.filename) as f: - # to provide "correct" colouring, the entire code needs to be - # lexed. However, this makes a lot of things terribly slow, so - # we decide not to. Besides, it's unlikely to matter. - - if lex_source and lex_entire: - f = self.lex(f.read()).splitlines() - - slice = itertools.islice(f, start - 1, stop - 1) - - for idx, line in enumerate(slice): - if start + idx == mark_line: - prefix = '>' - else: - prefix = ' ' - - if lex_source and not lex_entire: - line = self.lex(line) - - yield '%s %4d %s' % (prefix, start + idx, line.rstrip()) - - def get_source(self, start, stop=None, lex_source=True, mark_line=0, - lex_entire=False): - exc = gdb.GdbError('Unable to retrieve source code') - - if not self.filename: - raise exc - - start = max(start, 1) - if stop is None: - stop = start + 1 - - try: - return '\n'.join( - self._get_source(start, stop, lex_source, mark_line, lex_entire)) - except IOError: - raise exc - - -# Errors - -class CyGDBError(gdb.GdbError): - """ - Base class for Cython-command related errors - """ - - def __init__(self, *args): - args = args or (self.msg,) - super(CyGDBError, self).__init__(*args) - - -class NoCythonFunctionInFrameError(CyGDBError): - """ - raised when the user requests the current cython function, which is - unavailable - """ - msg = "Current function is a function cygdb doesn't know about" - - -class NoFunctionNameInFrameError(NoCythonFunctionInFrameError): - """ - raised when the name of the C function could not be determined - in the current C stack frame - """ - msg = ('C function name could not be determined in the current C stack ' - 'frame') - - -# Parameters - -class CythonParameter(gdb.Parameter): - """ - Base class for cython parameters - """ - - def __init__(self, name, command_class, parameter_class, default=None): - self.show_doc = self.set_doc = self.__class__.__doc__ - super(CythonParameter, self).__init__(name, command_class, - parameter_class) - if default is not None: - self.value = default - - def __bool__(self): - return bool(self.value) - - __nonzero__ = __bool__ # Python 2 - - - -class CompleteUnqualifiedFunctionNames(CythonParameter): - """ - Have 'cy break' complete unqualified function or method names. - """ - - -class ColorizeSourceCode(CythonParameter): - """ - Tell cygdb whether to colorize source code. - """ - - -class TerminalBackground(CythonParameter): - """ - Tell cygdb about the user's terminal background (light or dark). - """ - - -class CythonParameters(object): - """ - Simple container class that might get more functionality in the distant - future (mostly to remind us that we're dealing with parameters). - """ - - def __init__(self): - self.complete_unqualified = CompleteUnqualifiedFunctionNames( - 'cy_complete_unqualified', - gdb.COMMAND_BREAKPOINTS, - gdb.PARAM_BOOLEAN, - True) - self.colorize_code = ColorizeSourceCode( - 'cy_colorize_code', - gdb.COMMAND_FILES, - gdb.PARAM_BOOLEAN, - True) - self.terminal_background = TerminalBackground( - 'cy_terminal_background_color', - gdb.COMMAND_FILES, - gdb.PARAM_STRING, - "dark") - -parameters = CythonParameters() - - -# Commands - -class CythonCommand(gdb.Command, CythonBase): - """ - Base class for Cython commands - """ - - command_class = gdb.COMMAND_NONE - - @classmethod - def _register(cls, clsname, args, kwargs): - if not hasattr(cls, 'completer_class'): - return cls(clsname, cls.command_class, *args, **kwargs) - else: - return cls(clsname, cls.command_class, cls.completer_class, - *args, **kwargs) - - @classmethod - def register(cls, *args, **kwargs): - alias = getattr(cls, 'alias', None) - if alias: - cls._register(cls.alias, args, kwargs) - - return cls._register(cls.name, args, kwargs) - - -class CyCy(CythonCommand): - """ - Invoke a Cython command. Available commands are: - - cy import - cy break - cy step - cy next - cy run - cy cont - cy finish - cy up - cy down - cy select - cy bt / cy backtrace - cy list - cy print - cy set - cy locals - cy globals - cy exec - """ - - name = 'cy' - command_class = gdb.COMMAND_NONE - completer_class = gdb.COMPLETE_COMMAND - - def __init__(self, name, command_class, completer_class): - # keep the signature 2.5 compatible (i.e. do not use f(*a, k=v) - super(CythonCommand, self).__init__(name, command_class, - completer_class, prefix=True) - - commands = dict( - # GDB commands - import_ = CyImport.register(), - break_ = CyBreak.register(), - step = CyStep.register(), - next = CyNext.register(), - run = CyRun.register(), - cont = CyCont.register(), - finish = CyFinish.register(), - up = CyUp.register(), - down = CyDown.register(), - select = CySelect.register(), - bt = CyBacktrace.register(), - list = CyList.register(), - print_ = CyPrint.register(), - locals = CyLocals.register(), - globals = CyGlobals.register(), - exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'), - _exec = CyExec.register(), - set = CySet.register(), - - # GDB functions - cy_cname = CyCName('cy_cname'), - cy_cvalue = CyCValue('cy_cvalue'), - cy_lineno = CyLine('cy_lineno'), - cy_eval = CyEval('cy_eval'), - ) - - for command_name, command in commands.items(): - command.cy = self - setattr(self, command_name, command) - - self.cy = self - - # Cython module namespace - self.cython_namespace = {} - - # maps (unique) qualified function names (e.g. - # cythonmodule.ClassName.method_name) to the CythonFunction object - self.functions_by_qualified_name = {} - - # unique cnames of Cython functions - self.functions_by_cname = {} - - # map function names like method_name to a list of all such - # CythonFunction objects - self.functions_by_name = collections.defaultdict(list) - - -class CyImport(CythonCommand): - """ - Import debug information outputted by the Cython compiler - Example: cy import FILE... - """ - - name = 'cy import' - command_class = gdb.COMMAND_STATUS - completer_class = gdb.COMPLETE_FILENAME - - def invoke(self, args, from_tty): - if isinstance(args, BYTES): - args = args.decode(_filesystemencoding) - for arg in string_to_argv(args): - try: - f = open(arg) - except OSError as e: - raise gdb.GdbError('Unable to open file %r: %s' % (args, e.args[1])) - - t = etree.parse(f) - - for module in t.getroot(): - cython_module = CythonModule(**module.attrib) - self.cy.cython_namespace[cython_module.name] = cython_module - - for variable in module.find('Globals'): - d = variable.attrib - cython_module.globals[d['name']] = CythonVariable(**d) - - for function in module.find('Functions'): - cython_function = CythonFunction(module=cython_module, - **function.attrib) - - # update the global function mappings - name = cython_function.name - qname = cython_function.qualified_name - - self.cy.functions_by_name[name].append(cython_function) - self.cy.functions_by_qualified_name[ - cython_function.qualified_name] = cython_function - self.cy.functions_by_cname[ - cython_function.cname] = cython_function - - d = cython_module.functions[qname] = cython_function - - for local in function.find('Locals'): - d = local.attrib - cython_function.locals[d['name']] = CythonVariable(**d) - - for step_into_func in function.find('StepIntoFunctions'): - d = step_into_func.attrib - cython_function.step_into_functions.add(d['name']) - - cython_function.arguments.extend( - funcarg.tag for funcarg in function.find('Arguments')) - - for marker in module.find('LineNumberMapping'): - cython_lineno = int(marker.attrib['cython_lineno']) - c_linenos = list(map(int, marker.attrib['c_linenos'].split())) - cython_module.lineno_cy2c[cython_lineno] = min(c_linenos) - for c_lineno in c_linenos: - cython_module.lineno_c2cy[c_lineno] = cython_lineno - - -class CyBreak(CythonCommand): - """ - Set a breakpoint for Cython code using Cython qualified name notation, e.g.: - - cy break cython_modulename.ClassName.method_name... - - or normal notation: - - cy break function_or_method_name... - - or for a line number: - - cy break cython_module:lineno... - - Set a Python breakpoint: - Break on any function or method named 'func' in module 'modname' - - cy break -p modname.func... - - Break on any function or method named 'func' - - cy break -p func... - """ - - name = 'cy break' - command_class = gdb.COMMAND_BREAKPOINTS - - def _break_pyx(self, name): - modulename, _, lineno = name.partition(':') - lineno = int(lineno) - if modulename: - cython_module = self.cy.cython_namespace[modulename] - else: - cython_module = self.get_cython_function().module - - if lineno in cython_module.lineno_cy2c: - c_lineno = cython_module.lineno_cy2c[lineno] - breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno) - gdb.execute('break ' + breakpoint) - else: - raise gdb.GdbError("Not a valid line number. " - "Does it contain actual code?") - - def _break_funcname(self, funcname): - func = self.cy.functions_by_qualified_name.get(funcname) - - if func and func.is_initmodule_function: - func = None - - break_funcs = [func] - - if not func: - funcs = self.cy.functions_by_name.get(funcname) or [] - funcs = [f for f in funcs if not f.is_initmodule_function] - - if not funcs: - gdb.execute('break ' + funcname) - return - - if len(funcs) > 1: - # multiple functions, let the user pick one - print('There are multiple such functions:') - for idx, func in enumerate(funcs): - print('%3d) %s' % (idx, func.qualified_name)) - - while True: - try: - result = input( - "Select a function, press 'a' for all " - "functions or press 'q' or '^D' to quit: ") - except EOFError: - return - else: - if result.lower() == 'q': - return - elif result.lower() == 'a': - break_funcs = funcs - break - elif (result.isdigit() and - 0 <= int(result) < len(funcs)): - break_funcs = [funcs[int(result)]] - break - else: - print('Not understood...') - else: - break_funcs = [funcs[0]] - - for func in break_funcs: - gdb.execute('break %s' % func.cname) - if func.pf_cname: - gdb.execute('break %s' % func.pf_cname) - - def invoke(self, function_names, from_tty): - if isinstance(function_names, BYTES): - function_names = function_names.decode(_filesystemencoding) - argv = string_to_argv(function_names) - if function_names.startswith('-p'): - argv = argv[1:] - python_breakpoints = True - else: - python_breakpoints = False - - for funcname in argv: - if python_breakpoints: - gdb.execute('py-break %s' % funcname) - elif ':' in funcname: - self._break_pyx(funcname) - else: - self._break_funcname(funcname) - - @dont_suppress_errors - def complete(self, text, word): - # Filter init-module functions (breakpoints can be set using - # modulename:linenumber). - names = [n for n, L in self.cy.functions_by_name.items() - if any(not f.is_initmodule_function for f in L)] - qnames = [n for n, f in self.cy.functions_by_qualified_name.items() - if not f.is_initmodule_function] - - if parameters.complete_unqualified: - all_names = itertools.chain(qnames, names) - else: - all_names = qnames - - words = text.strip().split() - if not words or '.' not in words[-1]: - # complete unqualified - seen = set(text[:-len(word)].split()) - return [n for n in all_names - if n.startswith(word) and n not in seen] - - # complete qualified name - lastword = words[-1] - compl = [n for n in qnames if n.startswith(lastword)] - - if len(lastword) > len(word): - # readline sees something (e.g. a '.') as a word boundary, so don't - # "recomplete" this prefix - strip_prefix_length = len(lastword) - len(word) - compl = [n[strip_prefix_length:] for n in compl] - - return compl - - -class CythonInfo(CythonBase, libpython.PythonInfo): - """ - Implementation of the interface dictated by libpython.LanguageInfo. - """ - - def lineno(self, frame): - # Take care of the Python and Cython levels. We need to care for both - # as we can't simply dispatch to 'py-step', since that would work for - # stepping through Python code, but it would not step back into Cython- - # related code. The C level should be dispatched to the 'step' command. - if self.is_cython_function(frame): - return self.get_cython_lineno(frame) - return super(CythonInfo, self).lineno(frame) - - def get_source_line(self, frame): - try: - line = super(CythonInfo, self).get_source_line(frame) - except gdb.GdbError: - return None - else: - return line.strip() or None - - def exc_info(self, frame): - if self.is_python_function: - return super(CythonInfo, self).exc_info(frame) - - def runtime_break_functions(self): - if self.is_cython_function(): - return self.get_cython_function().step_into_functions - return () - - def static_break_functions(self): - result = ['PyEval_EvalFrameEx'] - result.extend(self.cy.functions_by_cname) - return result - - -class CythonExecutionControlCommand(CythonCommand, - libpython.ExecutionControlCommandBase): - - @classmethod - def register(cls): - return cls(cls.name, cython_info) - - -class CyStep(CythonExecutionControlCommand, libpython.PythonStepperMixin): - "Step through Cython, Python or C code." - - name = 'cy -step' - stepinto = True - - def invoke(self, args, from_tty): - if self.is_python_function(): - self.python_step(self.stepinto) - elif not self.is_cython_function(): - if self.stepinto: - command = 'step' - else: - command = 'next' - - self.finish_executing(gdb.execute(command, to_string=True)) - else: - self.step(stepinto=self.stepinto) - - -class CyNext(CyStep): - "Step-over Cython, Python or C code." - - name = 'cy -next' - stepinto = False - - -class CyRun(CythonExecutionControlCommand): - """ - Run a Cython program. This is like the 'run' command, except that it - displays Cython or Python source lines as well - """ - - name = 'cy run' - - invoke = CythonExecutionControlCommand.run - - -class CyCont(CythonExecutionControlCommand): - """ - Continue a Cython program. This is like the 'run' command, except that it - displays Cython or Python source lines as well. - """ - - name = 'cy cont' - invoke = CythonExecutionControlCommand.cont - - -class CyFinish(CythonExecutionControlCommand): - """ - Execute until the function returns. - """ - name = 'cy finish' - - invoke = CythonExecutionControlCommand.finish - - -class CyUp(CythonCommand): - """ - Go up a Cython, Python or relevant C frame. - """ - name = 'cy up' - _command = 'up' - - def invoke(self, *args): - try: - gdb.execute(self._command, to_string=True) - while not self.is_relevant_function(gdb.selected_frame()): - gdb.execute(self._command, to_string=True) - except RuntimeError as e: - raise gdb.GdbError(*e.args) - - frame = gdb.selected_frame() - index = 0 - while frame: - frame = frame.older() - index += 1 - - self.print_stackframe(index=index - 1) - - -class CyDown(CyUp): - """ - Go down a Cython, Python or relevant C frame. - """ - - name = 'cy down' - _command = 'down' - - -class CySelect(CythonCommand): - """ - Select a frame. Use frame numbers as listed in `cy backtrace`. - This command is useful because `cy backtrace` prints a reversed backtrace. - """ - - name = 'cy select' - - def invoke(self, stackno, from_tty): - try: - stackno = int(stackno) - except ValueError: - raise gdb.GdbError("Not a valid number: %r" % (stackno,)) - - frame = gdb.selected_frame() - while frame.newer(): - frame = frame.newer() - - stackdepth = libpython.stackdepth(frame) - - try: - gdb.execute('select %d' % (stackdepth - stackno - 1,)) - except RuntimeError as e: - raise gdb.GdbError(*e.args) - - -class CyBacktrace(CythonCommand): - 'Print the Cython stack' - - name = 'cy bt' - alias = 'cy backtrace' - command_class = gdb.COMMAND_STACK - completer_class = gdb.COMPLETE_NONE - - @require_running_program - def invoke(self, args, from_tty): - # get the first frame - frame = gdb.selected_frame() - while frame.older(): - frame = frame.older() - - print_all = args == '-a' - - index = 0 - while frame: - try: - is_relevant = self.is_relevant_function(frame) - except CyGDBError: - is_relevant = False - - if print_all or is_relevant: - self.print_stackframe(frame, index) - - index += 1 - frame = frame.newer() - - -class CyList(CythonCommand): - """ - List Cython source code. To disable to customize colouring see the cy_* - parameters. - """ - - name = 'cy list' - command_class = gdb.COMMAND_FILES - completer_class = gdb.COMPLETE_NONE - - # @dispatch_on_frame(c_command='list') - def invoke(self, _, from_tty): - sd, lineno = self.get_source_desc() - source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno, - lex_entire=True) - print(source) - - -class CyPrint(CythonCommand): - """ - Print a Cython variable using 'cy-print x' or 'cy-print module.function.x' - """ - - name = 'cy print' - command_class = gdb.COMMAND_DATA - - def invoke(self, name, from_tty, max_name_length=None): - if self.is_python_function(): - return gdb.execute('py-print ' + name) - elif self.is_cython_function(): - value = self.cy.cy_cvalue.invoke(name.lstrip('*')) - for c in name: - if c == '*': - value = value.dereference() - else: - break - - self.print_gdb_value(name, value, max_name_length) - else: - gdb.execute('print ' + name) - - def complete(self): - if self.is_cython_function(): - f = self.get_cython_function() - return list(itertools.chain(f.locals, f.globals)) - else: - return [] - - -sortkey = lambda item: item[0].lower() - - -class CyLocals(CythonCommand): - """ - List the locals from the current Cython frame. - """ - - name = 'cy locals' - command_class = gdb.COMMAND_STACK - completer_class = gdb.COMPLETE_NONE - - @dispatch_on_frame(c_command='info locals', python_command='py-locals') - def invoke(self, args, from_tty): - cython_function = self.get_cython_function() - - if cython_function.is_initmodule_function: - self.cy.globals.invoke(args, from_tty) - return - - local_cython_vars = cython_function.locals - max_name_length = len(max(local_cython_vars, key=len)) - for name, cyvar in sorted(local_cython_vars.items(), key=sortkey): - if self.is_initialized(self.get_cython_function(), cyvar.name): - value = gdb.parse_and_eval(cyvar.cname) - if not value.is_optimized_out: - self.print_gdb_value(cyvar.name, value, - max_name_length, '') - - -class CyGlobals(CyLocals): - """ - List the globals from the current Cython module. - """ - - name = 'cy globals' - command_class = gdb.COMMAND_STACK - completer_class = gdb.COMPLETE_NONE - - @dispatch_on_frame(c_command='info variables', python_command='py-globals') - def invoke(self, args, from_tty): - global_python_dict = self.get_cython_globals_dict() - module_globals = self.get_cython_function().module.globals - - max_globals_len = 0 - max_globals_dict_len = 0 - if module_globals: - max_globals_len = len(max(module_globals, key=len)) - if global_python_dict: - max_globals_dict_len = len(max(global_python_dict)) - - max_name_length = max(max_globals_len, max_globals_dict_len) - - seen = set() - print('Python globals:') - for k, v in sorted(global_python_dict.items(), key=sortkey): - v = v.get_truncated_repr(libpython.MAX_OUTPUT_LEN) - seen.add(k) - print(' %-*s = %s' % (max_name_length, k, v)) - - print('C globals:') - for name, cyvar in sorted(module_globals.items(), key=sortkey): - if name not in seen: - try: - value = gdb.parse_and_eval(cyvar.cname) - except RuntimeError: - pass - else: - if not value.is_optimized_out: - self.print_gdb_value(cyvar.name, value, - max_name_length, ' ') - - -class EvaluateOrExecuteCodeMixin(object): - """ - Evaluate or execute Python code in a Cython or Python frame. The 'evalcode' - method evaluations Python code, prints a traceback if an exception went - uncaught, and returns any return value as a gdb.Value (NULL on exception). - """ - - def _fill_locals_dict(self, executor, local_dict_pointer): - "Fill a remotely allocated dict with values from the Cython C stack" - cython_func = self.get_cython_function() - - for name, cyvar in cython_func.locals.items(): - if cyvar.type == PythonObject and self.is_initialized(cython_func, name): - try: - val = gdb.parse_and_eval(cyvar.cname) - except RuntimeError: - continue - else: - if val.is_optimized_out: - continue - - pystringp = executor.alloc_pystring(name) - code = ''' - (PyObject *) PyDict_SetItem( - (PyObject *) %d, - (PyObject *) %d, - (PyObject *) %s) - ''' % (local_dict_pointer, pystringp, cyvar.cname) - - try: - if gdb.parse_and_eval(code) < 0: - gdb.parse_and_eval('PyErr_Print()') - raise gdb.GdbError("Unable to execute Python code.") - finally: - # PyDict_SetItem doesn't steal our reference - executor.xdecref(pystringp) - - def _find_first_cython_or_python_frame(self): - frame = gdb.selected_frame() - while frame: - if (self.is_cython_function(frame) or - self.is_python_function(frame)): - frame.select() - return frame - - frame = frame.older() - - raise gdb.GdbError("There is no Cython or Python frame on the stack.") - - def _evalcode_cython(self, executor, code, input_type): - with libpython.FetchAndRestoreError(): - # get the dict of Cython globals and construct a dict in the - # inferior with Cython locals - global_dict = gdb.parse_and_eval( - '(PyObject *) PyModule_GetDict(__pyx_m)') - local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()') - - try: - self._fill_locals_dict(executor, - libpython.pointervalue(local_dict)) - result = executor.evalcode(code, input_type, global_dict, - local_dict) - finally: - executor.xdecref(libpython.pointervalue(local_dict)) - - return result - - def evalcode(self, code, input_type): - """ - Evaluate `code` in a Python or Cython stack frame using the given - `input_type`. - """ - frame = self._find_first_cython_or_python_frame() - executor = libpython.PythonCodeExecutor() - if self.is_python_function(frame): - return libpython._evalcode_python(executor, code, input_type) - return self._evalcode_cython(executor, code, input_type) - - -class CyExec(CythonCommand, libpython.PyExec, EvaluateOrExecuteCodeMixin): - """ - Execute Python code in the nearest Python or Cython frame. - """ - - name = '-cy-exec' - command_class = gdb.COMMAND_STACK - completer_class = gdb.COMPLETE_NONE - - def invoke(self, expr, from_tty): - expr, input_type = self.readcode(expr) - executor = libpython.PythonCodeExecutor() - executor.xdecref(self.evalcode(expr, executor.Py_single_input)) - - -class CySet(CythonCommand): - """ - Set a Cython variable to a certain value - - cy set my_cython_c_variable = 10 - cy set my_cython_py_variable = $cy_eval("{'doner': 'kebab'}") - - This is equivalent to - - set $cy_value("my_cython_variable") = 10 - """ - - name = 'cy set' - command_class = gdb.COMMAND_DATA - completer_class = gdb.COMPLETE_NONE - - @require_cython_frame - def invoke(self, expr, from_tty): - name_and_expr = expr.split('=', 1) - if len(name_and_expr) != 2: - raise gdb.GdbError("Invalid expression. Use 'cy set var = expr'.") - - varname, expr = name_and_expr - cname = self.cy.cy_cname.invoke(varname.strip()) - gdb.execute("set %s = %s" % (cname, expr)) - - -# Functions - -class CyCName(gdb.Function, CythonBase): - """ - Get the C name of a Cython variable in the current context. - Examples: - - print $cy_cname("function") - print $cy_cname("Class.method") - print $cy_cname("module.function") - """ - - @require_cython_frame - @gdb_function_value_to_unicode - def invoke(self, cyname, frame=None): - frame = frame or gdb.selected_frame() - cname = None - - if self.is_cython_function(frame): - cython_function = self.get_cython_function(frame) - if cyname in cython_function.locals: - cname = cython_function.locals[cyname].cname - elif cyname in cython_function.module.globals: - cname = cython_function.module.globals[cyname].cname - else: - qname = '%s.%s' % (cython_function.module.name, cyname) - if qname in cython_function.module.functions: - cname = cython_function.module.functions[qname].cname - - if not cname: - cname = self.cy.functions_by_qualified_name.get(cyname) - - if not cname: - raise gdb.GdbError('No such Cython variable: %s' % cyname) - - return cname - - -class CyCValue(CyCName): - """ - Get the value of a Cython variable. - """ - - @require_cython_frame - @gdb_function_value_to_unicode - def invoke(self, cyname, frame=None): - globals_dict = self.get_cython_globals_dict() - cython_function = self.get_cython_function(frame) - - if self.is_initialized(cython_function, cyname): - cname = super(CyCValue, self).invoke(cyname, frame=frame) - return gdb.parse_and_eval(cname) - elif cyname in globals_dict: - return globals_dict[cyname]._gdbval - else: - raise gdb.GdbError("Variable %s is not initialized." % cyname) - - -class CyLine(gdb.Function, CythonBase): - """ - Get the current Cython line. - """ - - @require_cython_frame - def invoke(self): - return self.get_cython_lineno() - - -class CyEval(gdb.Function, CythonBase, EvaluateOrExecuteCodeMixin): - """ - Evaluate Python code in the nearest Python or Cython frame and return - """ - - @gdb_function_value_to_unicode - def invoke(self, python_expression): - input_type = libpython.PythonCodeExecutor.Py_eval_input - return self.evalcode(python_expression, input_type) - - -cython_info = CythonInfo() -cy = CyCy.register() -cython_info.cy = cy - - -def register_defines(): - libpython.source_gdb_script(textwrap.dedent("""\ - define cy step - cy -step - end - - define cy next - cy -next - end - - document cy step - %s - end - - document cy next - %s - end - """) % (CyStep.__doc__, CyNext.__doc__)) - -register_defines() |