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):
"""
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_*
......
......@@ -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)
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