Commit ebbf5499 authored by Mark Florisson's avatar Mark Florisson

More efficient step-into and step-over which hopefully makes for a more...

More efficient step-into and step-over which hopefully makes for a more performing 'cy step' and 'cy next'
parent 3aec761b
...@@ -274,7 +274,6 @@ class CythonBase(object): ...@@ -274,7 +274,6 @@ class CythonBase(object):
""" """
name = frame.name() name = frame.name()
older_frame = frame.older() older_frame = frame.older()
# print 'is_relevant_function', name
if self.is_cython_function(frame) or self.is_python_function(frame): if self.is_cython_function(frame) or self.is_python_function(frame):
return True return True
elif (parameters.step_into_c_code and elif (parameters.step_into_c_code and
...@@ -829,44 +828,49 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper): ...@@ -829,44 +828,49 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
return libpython.py_step.lineno(frame) return libpython.py_step.lineno(frame)
def get_source_line(self, 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: try:
line = super(CythonCodeStepper, self).get_source_line(frame) line = super(CythonCodeStepper, self).get_source_line(frame)
except gdb.GdbError: except gdb.GdbError:
pass return None
else: else:
result = line.lstrip() return line.strip() or None
return result
@classmethod @classmethod
def register(cls): 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): class CyStep(CythonCodeStepper):
"Step through Cython, Python or C code." "Step through Cython, Python or C code."
name = 'cy step' name = 'cy step'
stepper = True stepinto = True
@dispatch_on_frame(c_command='step')
def invoke(self, *args, **kwargs):
super(CythonCodeStepper, self).invoke(*args, **kwargs)
class CyNext(CythonCodeStepper): class CyNext(CythonCodeStepper):
"Step-over Python code." "Step-over Python code."
name = 'cy next' name = 'cy next'
stepper = False stepinto = False
@dispatch_on_frame(c_command='next')
def invoke(self, *args, **kwargs):
super(CythonCodeStepper, self).invoke(*args, **kwargs)
class CyRun(CythonCodeStepper): class CyRun(CythonCodeStepper):
...@@ -879,8 +883,7 @@ class CyRun(CythonCodeStepper): ...@@ -879,8 +883,7 @@ class CyRun(CythonCodeStepper):
_command = 'run' _command = 'run'
def invoke(self, *args): def invoke(self, *args):
self.result = gdb.execute(self._command, to_string=True) self.finish_executing(gdb.execute(self._command, to_string=True))
self.end_stepping()
class CyCont(CyRun): class CyCont(CyRun):
...@@ -893,7 +896,7 @@ class CyCont(CyRun): ...@@ -893,7 +896,7 @@ class CyCont(CyRun):
_command = 'cont' _command = 'cont'
class CyUp(CythonCodeStepper): class CyUp(CythonCommand):
""" """
Go up a Cython, Python or relevant C frame. Go up a Cython, Python or relevant C frame.
""" """
...@@ -961,6 +964,7 @@ class CyBacktrace(CythonCommand): ...@@ -961,6 +964,7 @@ class CyBacktrace(CythonCommand):
selected_frame.select() selected_frame.select()
class CyList(CythonCommand): class CyList(CythonCommand):
""" """
List Cython source code. To disable to customize colouring see the cy_* List Cython source code. To disable to customize colouring see the cy_*
......
...@@ -46,6 +46,7 @@ The module also extends gdb with some python-specific commands. ...@@ -46,6 +46,7 @@ The module also extends gdb with some python-specific commands.
from __future__ import with_statement from __future__ import with_statement
import os import os
import re
import sys import sys
import atexit import atexit
import tempfile import tempfile
...@@ -1568,7 +1569,7 @@ class _LoggingState(object): ...@@ -1568,7 +1569,7 @@ class _LoggingState(object):
_execute("set pagination on") _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' 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 argument (new in 7.2). Have it properly capture stderr also. Ensure
...@@ -1600,6 +1601,10 @@ class GenericCodeStepper(gdb.Command): ...@@ -1600,6 +1601,10 @@ class GenericCodeStepper(gdb.Command):
frame). If the source code cannot be frame). If the source code cannot be
retrieved this function should retrieved this function should
return None 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' This class provides an 'invoke' method that invokes a 'step' or 'step-over'
depending on the 'stepper' argument. depending on the 'stepper' argument.
...@@ -1607,60 +1612,69 @@ class GenericCodeStepper(gdb.Command): ...@@ -1607,60 +1612,69 @@ class GenericCodeStepper(gdb.Command):
stepper = False stepper = False
def __init__(self, name, stepper=False): def __init__(self, name, stepinto=False):
super(GenericCodeStepper, self).__init__(name, super(GenericCodeStepper, self).__init__(name,
gdb.COMMAND_RUNNING, gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE) gdb.COMPLETE_NONE)
self.stepper = stepper self.stepinto = stepinto
def init_stepping(self): def _step(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):
""" """
Teturns whether to continue stepping. This method sets the instance Do a single step or step-over. Returns the result of the last gdb
attribute 'result'. 'result' hold the output of the executed gdb command that made execution stop.
command ('step' or 'next')
""" """
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)
beginframe = gdb.selected_frame()
beginline = self.lineno(beginframe)
if not self.stepinto:
depth = self._stackdepth(beginframe)
newframe = beginframe
result = ''
while True:
if self.stopped(): if self.stopped():
return False break
newframe = gdb.selected_frame() 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
hit_breakpoint = self.result.startswith('Breakpoint') newframe = gdb.selected_frame()
is_relevant_function = self.is_relevant_function(newframe) is_relevant_function = self.is_relevant_function(newframe)
if newframe != self.beginframe: if newframe != beginframe:
# new function # new function
if not self.stepper:
is_relevant_function = ( if not self.stepinto:
self._stackdepth(newframe) < self.depth and # see if we returned to the caller
newdepth = self._stackdepth(newframe)
is_relevant_function = (newdepth < depth and
is_relevant_function) is_relevant_function)
new_lineno = False if is_relevant_function:
break
else: else:
is_relevant_function = False if self.lineno(newframe) > beginline:
new_lineno = self.lineno(newframe) > self.beginline break
return not (hit_breakpoint or new_lineno or is_relevant_function) if self.stepinto:
for bp in breakpoints:
gdb.execute('delete %s' % bp, to_string=True)
def end_stepping(self): return result
"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): def stopped(self):
return gdb.inferiors()[0].pid == 0 return gdb.inferiors()[0].pid == 0
...@@ -1674,16 +1688,22 @@ class GenericCodeStepper(gdb.Command): ...@@ -1674,16 +1688,22 @@ class GenericCodeStepper(gdb.Command):
return depth return depth
def invoke(self, args, from_tty): def invoke(self, args, from_tty):
if self.stepper: self.finish_executing(self._step())
gdb_command = 'step'
def finish_executing(self, result):
if self.stopped():
print result.strip()
else: else:
gdb_command= 'next' frame = gdb.selected_frame()
output = None
self.init_stepping() if self.is_relevant_function(frame):
while self.next_step(gdb_command): output = self.get_source_line(frame)
pass
self.end_stepping() if output is None:
print result.strip()
else:
print output
class PythonCodeStepper(GenericCodeStepper): class PythonCodeStepper(GenericCodeStepper):
...@@ -1709,11 +1729,15 @@ class PythonCodeStepper(GenericCodeStepper): ...@@ -1709,11 +1729,15 @@ class PythonCodeStepper(GenericCodeStepper):
except IOError, e: except IOError, e:
return None return None
def break_functions(self):
yield 'PyEval_EvalFrameEx'
class PyStep(PythonCodeStepper): class PyStep(PythonCodeStepper):
"Step through Python code." "Step through Python code."
class PyNext(PythonCodeStepper): class PyNext(PythonCodeStepper):
"Step-over Python code." "Step-over Python code."
py_step = PyStep('py-step', stepper=True) py_step = PyStep('py-step', stepinto=True)
py_next = PyNext('py-next', stepper=False) py_next = PyNext('py-next', stepinto=False)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment