From cfe5930ac405b943b5c902de4f599ad6b2802c67 Mon Sep 17 00:00:00 2001 From: Mark Florisson Date: Wed, 3 Nov 2010 00:41:34 +0100 Subject: [PATCH] Python breakpoints (cy break -p / py-break) --- .../Debugger/Tests/test_libcython_in_gdb.py | 6 +- Cython/Debugger/libcython.py | 23 +++++++- Cython/Debugger/libpython.py | 58 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/Cython/Debugger/Tests/test_libcython_in_gdb.py b/Cython/Debugger/Tests/test_libcython_in_gdb.py index f3126d9f..d9c3a068 100644 --- a/Cython/Debugger/Tests/test_libcython_in_gdb.py +++ b/Cython/Debugger/Tests/test_libcython_in_gdb.py @@ -131,7 +131,11 @@ class TestBreak(DebugTestCase): assert self.spam_func.cname in bp.location assert bp.enabled - + + def test_python_break(self): + gdb.execute('cy break -p join') + assert 'def join(' in gdb.execute('cy run', to_string=True) + class DebugStepperTestCase(DebugTestCase): def step(self, varnames_and_values, source_line=None, lineno=None): diff --git a/Cython/Debugger/libcython.py b/Cython/Debugger/libcython.py index d6df99be..50bcef6f 100644 --- a/Cython/Debugger/libcython.py +++ b/Cython/Debugger/libcython.py @@ -494,7 +494,6 @@ class CythonCommand(gdb.Command, CythonBase): """ command_class = gdb.COMMAND_NONE - completer_class = gdb.COMPLETE_NONE @classmethod def _register(cls, clsname, args, kwargs): @@ -656,6 +655,15 @@ class CyBreak(CythonCommand): 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' @@ -717,8 +725,17 @@ class CyBreak(CythonCommand): gdb.execute('break %s' % func.pf_cname) def invoke(self, function_names, from_tty): - for funcname in string_to_argv(function_names.encode('UTF-8')): - if ':' in funcname: + argv = string_to_argv(function_names.encode('UTF-8')) + 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) diff --git a/Cython/Debugger/libpython.py b/Cython/Debugger/libpython.py index 880105dc..1880b9af 100644 --- a/Cython/Debugger/libpython.py +++ b/Cython/Debugger/libpython.py @@ -1467,6 +1467,64 @@ PyLocals("py-locals", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) PyGlobals("py-globals", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) +class PyNameEquals(gdb.Function): + + def _get_pycurframe_attr(self, attr): + frame = Frame(gdb.selected_frame()) + if frame.is_evalframeex(): + pyframe = frame.get_pyop() + if pyframe is None: + return None + + return str(getattr(pyframe, attr)) + + return None + + def invoke(self, funcname): + attr = self._get_pycurframe_attr('co_name') + return attr is not None and attr == funcname.string() + +PyNameEquals("pyname_equals") + + +class PyModEquals(PyNameEquals): + + def invoke(self, modname): + attr = self._get_pycurframe_attr('co_filename') + if attr is not None: + filename, ext = os.path.splitext(os.path.basename(attr)) + return filename == modname.string() + return False + +PyModEquals("pymod_equals") + + +class PyBreak(gdb.Command): + """ + Set a Python breakpoint. Examples: + + Break on any function or method named 'func' in module 'modname' + + py-break modname.func + + Break on any function or method named 'func' + + py-break func + """ + + def invoke(self, funcname, from_tty): + if '.' in funcname: + modname, dot, funcname = funcname.rpartition('.') + cond = '$pyname_equals("%s") && $pymod_equals("%s")' % (funcname, + modname) + else: + cond = '$pyname_equals("%s")' % funcname + + gdb.execute('break PyEval_EvalFrameEx if ' + cond) + +PyBreak("py-break", gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE) + + class _LoggingState(object): """ State that helps to provide a reentrant gdb.execute() function. -- 2.26.2