From ebbf549946e06f5f06c9a68caff00f9d461f2235 Mon Sep 17 00:00:00 2001 From: Mark Florisson Date: Sun, 7 Nov 2010 13:15:12 +0100 Subject: [PATCH] More efficient step-into and step-over which hopefully makes for a more performing 'cy step' and 'cy next' --- Cython/Debugger/libcython.py | 58 ++++++++------- Cython/Debugger/libpython.py | 136 ++++++++++++++++++++--------------- 2 files changed, 111 insertions(+), 83 deletions(-) diff --git a/Cython/Debugger/libcython.py b/Cython/Debugger/libcython.py index 02c220ab..5c7c8912 100644 --- a/Cython/Debugger/libcython.py +++ b/Cython/Debugger/libcython.py @@ -274,7 +274,6 @@ class CythonBase(object): """ name = frame.name() older_frame = frame.older() - # print 'is_relevant_function', name if self.is_cython_function(frame) or self.is_python_function(frame): return True elif (parameters.step_into_c_code and @@ -829,44 +828,49 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper): return libpython.py_step.lineno(frame) def get_source_line(self, frame): - # We may have ended up in a Python, Cython, or C function - result = None - - if self.is_cython_function(frame) or self.is_python_function(frame): - try: - line = super(CythonCodeStepper, self).get_source_line(frame) - except gdb.GdbError: - pass - else: - result = line.lstrip() + try: + line = super(CythonCodeStepper, self).get_source_line(frame) + except gdb.GdbError: + return None + else: + return line.strip() or None - return result - @classmethod def register(cls): - return cls(cls.name, stepper=cls.stepper) + return cls(cls.name, stepinto=getattr(cls, 'stepinto', False)) + + def break_functions(self): + result = ['PyEval_EvalFrameEx'] + if self.is_cython_function(): + result.extend(self.get_cython_function().step_into_functions) + + result.extend(self.cy.functions_by_cname) + return result + + def invoke(self, args, from_tty): + if not self.is_cython_function() and not self.is_python_function(): + if self.stepinto: + command = 'step' + else: + comamnd = 'next' + + self.finish_executing(gdb.execute(command, to_string=True)) + else: + super(CythonCodeStepper, self).invoke(args, from_tty) class CyStep(CythonCodeStepper): "Step through Cython, Python or C code." name = 'cy step' - stepper = True - - @dispatch_on_frame(c_command='step') - def invoke(self, *args, **kwargs): - super(CythonCodeStepper, self).invoke(*args, **kwargs) + stepinto = True class CyNext(CythonCodeStepper): "Step-over Python code." name = 'cy next' - stepper = False - - @dispatch_on_frame(c_command='next') - def invoke(self, *args, **kwargs): - super(CythonCodeStepper, self).invoke(*args, **kwargs) + stepinto = False class CyRun(CythonCodeStepper): @@ -879,8 +883,7 @@ class CyRun(CythonCodeStepper): _command = 'run' def invoke(self, *args): - self.result = gdb.execute(self._command, to_string=True) - self.end_stepping() + self.finish_executing(gdb.execute(self._command, to_string=True)) class CyCont(CyRun): @@ -893,7 +896,7 @@ class CyCont(CyRun): _command = 'cont' -class CyUp(CythonCodeStepper): +class CyUp(CythonCommand): """ Go up a Cython, Python or relevant C frame. """ @@ -961,6 +964,7 @@ class CyBacktrace(CythonCommand): selected_frame.select() + class CyList(CythonCommand): """ List Cython source code. To disable to customize colouring see the cy_* diff --git a/Cython/Debugger/libpython.py b/Cython/Debugger/libpython.py index 7cc7240c..f7ac2d68 100644 --- a/Cython/Debugger/libpython.py +++ b/Cython/Debugger/libpython.py @@ -46,6 +46,7 @@ The module also extends gdb with some python-specific commands. from __future__ import with_statement import os +import re import sys import atexit import tempfile @@ -1568,7 +1569,7 @@ class _LoggingState(object): _execute("set pagination on") -def execute(command, from_tty=True, to_string=False): +def execute(command, from_tty=False, to_string=False): """ Replace gdb.execute() with this function and have it accept a 'to_string' argument (new in 7.2). Have it properly capture stderr also. Ensure @@ -1600,6 +1601,10 @@ class GenericCodeStepper(gdb.Command): frame). If the source code cannot be retrieved this function should return None + break_functions() - an iterable of function names that are + considered relevant and should halt + step-into execution. This is needed to + provide a performing step-into This class provides an 'invoke' method that invokes a 'step' or 'step-over' depending on the 'stepper' argument. @@ -1607,61 +1612,70 @@ class GenericCodeStepper(gdb.Command): stepper = False - def __init__(self, name, stepper=False): + def __init__(self, name, stepinto=False): super(GenericCodeStepper, self).__init__(name, gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE) - self.stepper = stepper + self.stepinto = stepinto - def init_stepping(self): - self.beginframe = gdb.selected_frame() - self.beginline = self.lineno(self.beginframe) - if not self.stepper: - self.depth = self._stackdepth(self.beginframe) - - def next_step(self, gdb_command): + def _step(self): """ - Teturns whether to continue stepping. This method sets the instance - attribute 'result'. 'result' hold the output of the executed gdb - command ('step' or 'next') + Do a single step or step-over. Returns the result of the last gdb + command that made execution stop. """ - self.result = gdb.execute(gdb_command, to_string=True) + if self.stepinto: + # set breakpoints for any function we may end up in that seems + # relevant + breakpoints = [] + for break_func in self.break_functions(): + result = gdb.execute('break %s' % break_func, to_string=True) + bp = re.search(r'Breakpoint (\d+)', result).group(1) + breakpoints.append(bp) - if self.stopped(): - return False - - newframe = gdb.selected_frame() + beginframe = gdb.selected_frame() + beginline = self.lineno(beginframe) + if not self.stepinto: + depth = self._stackdepth(beginframe) - hit_breakpoint = self.result.startswith('Breakpoint') - is_relevant_function = self.is_relevant_function(newframe) + newframe = beginframe + result = '' + + while True: + if self.stopped(): + break + + if self.is_relevant_function(newframe): + result = gdb.execute('next', to_string=True) + else: + result = gdb.execute('finish', to_string=True) + + if result.startswith('Breakpoint'): + break + + newframe = gdb.selected_frame() + is_relevant_function = self.is_relevant_function(newframe) + + if newframe != beginframe: + # new function + + if not self.stepinto: + # see if we returned to the caller + newdepth = self._stackdepth(newframe) + is_relevant_function = (newdepth < depth and + is_relevant_function) + + if is_relevant_function: + break + else: + if self.lineno(newframe) > beginline: + break - if newframe != self.beginframe: - # new function - if not self.stepper: - is_relevant_function = ( - self._stackdepth(newframe) < self.depth and - is_relevant_function) - - new_lineno = False - else: - is_relevant_function = False - new_lineno = self.lineno(newframe) > self.beginline + if self.stepinto: + for bp in breakpoints: + gdb.execute('delete %s' % bp, to_string=True) - return not (hit_breakpoint or new_lineno or is_relevant_function) + return result - def end_stepping(self): - "requires that the instance attribute self.result is set" - if self.stopped(): - sys.stdout.write(self.result) - else: - frame = gdb.selected_frame() - if self.is_relevant_function(frame): - output = self.get_source_line(frame) - if output is None: - sys.stdout.write(self.result) - else: - print output - def stopped(self): return gdb.inferiors()[0].pid == 0 @@ -1674,16 +1688,22 @@ class GenericCodeStepper(gdb.Command): return depth def invoke(self, args, from_tty): - if self.stepper: - gdb_command = 'step' + self.finish_executing(self._step()) + + def finish_executing(self, result): + if self.stopped(): + print result.strip() else: - gdb_command= 'next' - - self.init_stepping() - while self.next_step(gdb_command): - pass - - self.end_stepping() + frame = gdb.selected_frame() + output = None + + if self.is_relevant_function(frame): + output = self.get_source_line(frame) + + if output is None: + print result.strip() + else: + print output class PythonCodeStepper(GenericCodeStepper): @@ -1708,6 +1728,10 @@ class PythonCodeStepper(GenericCodeStepper): return self.pyframe(frame).current_line().rstrip() except IOError, e: return None + + def break_functions(self): + yield 'PyEval_EvalFrameEx' + class PyStep(PythonCodeStepper): "Step through Python code." @@ -1715,5 +1739,5 @@ class PyStep(PythonCodeStepper): class PyNext(PythonCodeStepper): "Step-over Python code." -py_step = PyStep('py-step', stepper=True) -py_next = PyNext('py-next', stepper=False) +py_step = PyStep('py-step', stepinto=True) +py_next = PyNext('py-next', stepinto=False) -- 2.26.2