return compl
-class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
+class CythonInfo(CythonBase, libpython.LanguageInfo):
"""
- Base class for CyStep and CyNext. It implements the interface dictated by
- libpython.GenericCodeStepper.
+ Implementation of the interface dictated by libpython.LanguageInfo.
"""
def lineno(self, frame):
if self.is_cython_function(frame):
return self.get_cython_lineno(frame)
else:
- return libpython.py_step.lineno(frame)
+ return libpython.py_step.lang_info.lineno(frame)
def get_source_line(self, frame):
try:
- line = super(CythonCodeStepper, self).get_source_line(frame)
+ line = super(CythonInfo, self).get_source_line(frame)
except gdb.GdbError:
return None
else:
return line.strip() or None
- @classmethod
- def register(cls):
- return cls(cls.name, stepinto=getattr(cls, 'stepinto', False))
-
def runtime_break_functions(self):
if self.is_cython_function():
return self.get_cython_function().step_into_functions
return result
-class CyStep(CythonCodeStepper):
+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'
def invoke(self, args, from_tty):
if self.is_python_function():
- libpython.py_step.get_source_line = self.get_source_line
- libpython.py_step.invoke(args, from_tty)
+ self.python_step(self.stepinto)
elif not self.is_cython_function():
if self.stepinto:
command = 'step'
self.finish_executing(gdb.execute(command, to_string=True))
else:
- self.step()
+ self.step(stepinto=self.stepinto)
class CyNext(CyStep):
stepinto = False
-class CyRun(CythonCodeStepper):
+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 = CythonCodeStepper.run
+ invoke = CythonExecutionControlCommand.run
-class CyCont(CyRun):
+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 = CythonCodeStepper.cont
+ invoke = CythonExecutionControlCommand.cont
-class CyFinish(CyRun):
+class CyFinish(CythonExecutionControlCommand):
"""
Execute until the function returns.
"""
name = 'cy finish'
- invoke = CythonCodeStepper.finish
+ invoke = CythonExecutionControlCommand.finish
class CyUp(CythonCommand):
_command = 'down'
-class CySelect(CythonCodeStepper):
+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.
while frame.newer():
frame = frame.newer()
- stackdepth = self._stackdepth(frame)
+ stackdepth = libpython.stackdepth(frame)
try:
gdb.execute('select %d' % (stackdepth - stackno - 1,))
def invoke(self):
return self.get_cython_lineno()
-
+cython_info = CythonInfo()
cy = CyCy.register()
+cython_info.cy = cy
\ No newline at end of file
if thread == selected_thread:
return inferior
-
-class GenericCodeStepper(gdb.Command):
- """
- Superclass for code stepping. Subclasses must implement the following
- methods:
-
- lineno(frame)
- Tells the current line number (only called for a relevant frame).
- If lineno is a false value it is not checked for a difference.
-
- is_relevant_function(frame)
- tells whether we care about frame 'frame'
-
- get_source_line(frame)
- get the line of source code for the current line (only called for a
- relevant frame). If the source code cannot be retrieved this
- function should return None
+def stackdepth(frame):
+ "Tells the stackdepth of a gdb frame."
+ depth = 0
+ while frame:
+ frame = frame.older()
+ depth += 1
- static_break_functions()
- returns an iterable of function names that are considered relevant
- and should halt step-into execution. This is needed to provide a
- performing step-into
-
- runtime_break_functions
- list of functions that we should break into depending on the
- context
+ return depth
- This class provides an 'invoke' method that invokes a 'step' or 'step-over'
- depending on the 'stepinto' argument.
+class ExecutionControlCommandBase(gdb.Command):
+ """
+ Superclass for language specific execution control. Language specific
+ features should be implemented by lang_info using the LanguageInfo
+ interface. 'name' is the name of the command.
"""
stepper = False
static_breakpoints = {}
runtime_breakpoints = {}
- def __init__(self, name, stepinto=False):
- super(GenericCodeStepper, self).__init__(name,
- gdb.COMMAND_RUNNING,
- gdb.COMPLETE_NONE)
- self.stepinto = stepinto
+ def __init__(self, name, lang_info):
+ super(ExecutionControlCommandBase, self).__init__(
+ name, gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
+ self.lang_info = lang_info
def _break_func(self, funcname):
result = gdb.execute('break %s' % funcname, to_string=True)
This method must be called whenever the list of functions we should
step into changes. It can be called on any GenericCodeStepper instance.
"""
- break_funcs = set(self.static_break_functions())
+ break_funcs = set(self.lang_info.static_break_functions())
for funcname in break_funcs:
if funcname not in self.static_breakpoints:
for bp in self.static_breakpoints.itervalues():
gdb.execute('enable ' + bp)
- runtime_break_functions = self.runtime_break_functions()
+ runtime_break_functions = self.lang_info.runtime_break_functions()
if runtime_break_functions is None:
return
self.runtime_breakpoints.itervalues())
for bp in chain:
gdb.execute('disable ' + bp)
+
- def runtime_break_functions(self):
- """
- Implement this if the list of step-into functions depends on the
- context.
- """
-
- def stopped(self, result):
- match = re.search('^Program received signal .*', result, re.MULTILINE)
- if match:
- return match.group(0)
- elif get_selected_inferior().pid == 0:
- return result
- else:
- match = re.search('.*[Ww]arning.*', result, re.MULTILINE)
+ def filter_output(self, result):
+ output = []
+
+ match_finish = re.search(r'^Value returned is \$\d+ = (.*)', result,
+ re.MULTILINE)
+ if match_finish:
+ output.append('Value returned: %s' % match_finish.group(1))
+
+ reflags = re.MULTILINE
+ regexes = [
+ (r'^Program received signal .*', reflags|re.DOTALL),
+ (r'.*[Ww]arning.*', 0),
+ (r'^Program exited .*', reflags),
+ ]
+
+ for regex, flags in regexes:
+ match = re.search(regex, result, flags)
if match:
- print match.group(0)
-
- return ''
+ output.append(match.group(0))
- def _stackdepth(self, frame):
- depth = 0
- while frame:
- frame = frame.older()
- depth += 1
-
- return depth
+ return '\n'.join(output)
+
+ def stopped(self):
+ return get_selected_inferior().pid == 0
- def finish_executing(self, output):
+ def finish_executing(self, result):
"""
After doing some kind of code running in the inferior, print the line
of source code or the result of the last executed gdb command (passed
in as the `result` argument).
"""
- result = self.stopped(output)
- if result:
+ result = self.filter_output(result)
+
+ if self.stopped():
print result.strip()
- # check whether the program was killed by a signal, it should still
- # have a stack.
- try:
- frame = gdb.selected_frame()
- except RuntimeError:
- pass
- else:
- line = self.get_source_line(frame)
-
- if line is None:
- print output
- else:
- print result
- print line
else:
frame = gdb.selected_frame()
- output = None
-
- if self.is_relevant_function(frame):
- output = self.get_source_line(frame)
-
- if output is None:
- pframe = getattr(self, 'print_stackframe', None)
- if pframe:
- pframe(frame, index=0)
- else:
- print result.strip()
+ if self.lang_info.is_relevant_function(frame):
+ print self.lang_info.get_source_line(frame) or result
else:
- print output
+ print result
def _finish(self):
"""
# outermost frame, continue
return gdb.execute('cont', to_string=True)
- def finish(self, *args):
+ def _finish_frame(self):
"""
Execute until the function returns to a relevant caller.
"""
-
while True:
result = self._finish()
break
hitbp = re.search(r'Breakpoint (\d+)', result)
- is_relevant = self.is_relevant_function(frame)
- if hitbp or is_relevant or self.stopped(result):
+ is_relevant = self.lang_info.is_relevant_function(frame)
+ if hitbp or is_relevant or self.stopped():
break
-
+
+ return result
+
+ def finish(self, *args):
+ "Implements the finish command."
+ result = self._finish_frame()
self.finish_executing(result)
- def step(self, stepover_command='next'):
+ def step(self, stepinto, stepover_command='next'):
"""
Do a single step or step-over. Returns the result of the last gdb
command that made execution stop.
works properly with local trace functions, see
PyFrameObjectPtr.current_line_num and PyFrameObjectPtr.addr2line.
"""
- if self.stepinto:
+ if stepinto:
self.enable_breakpoints()
beginframe = gdb.selected_frame()
- if self.is_relevant_function(beginframe):
+ if self.lang_info.is_relevant_function(beginframe):
# If we start in a relevant frame, initialize stuff properly. If
# we don't start in a relevant frame, the loop will halt
- # immediately. So don't call self.lineno() as it may raise for
- # irrelevant frames.
- beginline = self.lineno(beginframe)
+ # immediately. So don't call self.lang_info.lineno() as it may
+ # raise for irrelevant frames.
+ beginline = self.lang_info.lineno(beginframe)
- if not self.stepinto:
- depth = self._stackdepth(beginframe)
+ if not stepinto:
+ depth = stackdepth(beginframe)
newframe = beginframe
while True:
- if self.is_relevant_function(newframe):
+ if self.lang_info.is_relevant_function(newframe):
result = gdb.execute(stepover_command, to_string=True)
else:
- self.finish()
+ result = self._finish_frame()
- if self.stopped(result):
+ if self.stopped():
break
newframe = gdb.selected_frame()
- is_relevant_function = self.is_relevant_function(newframe)
+ is_relevant_function = self.lang_info.is_relevant_function(newframe)
try:
framename = newframe.name()
except RuntimeError:
if newframe != beginframe:
# new function
- if not self.stepinto:
+ if not stepinto:
# see if we returned to the caller
- newdepth = self._stackdepth(newframe)
+ newdepth = stackdepth(newframe)
is_relevant_function = (newdepth < depth and
is_relevant_function)
else:
# newframe equals beginframe, check for a difference in the
# line number
- lineno = self.lineno(newframe)
+ lineno = self.lang_info.lineno(newframe)
if lineno and lineno != beginline:
break
- if self.stepinto:
+ if stepinto:
self.disable_breakpoints()
self.finish_executing(result)
self.finish_executing(gdb.execute('cont', to_string=True))
-class PythonCodeStepper(GenericCodeStepper):
+class LanguageInfo(object):
+ """
+ This class defines the interface that ExecutionControlCommandBase needs to
+ provide language-specific execution control.
+
+ Classes that implement this interface should implement:
+
+ lineno(frame)
+ Tells the current line number (only called for a relevant frame).
+ If lineno is a false value it is not checked for a difference.
+
+ is_relevant_function(frame)
+ tells whether we care about frame 'frame'
+
+ get_source_line(frame)
+ get the line of source code for the current line (only called for a
+ relevant frame). If the source code cannot be retrieved this
+ function should return None
+
+ exc_info(frame) -- optional
+ tells whether an exception was raised, if so, it should return a
+ string representation of the exception value, None otherwise.
+
+ static_break_functions()
+ returns an iterable of function names that are considered relevant
+ and should halt step-into execution. This is needed to provide a
+ performing step-into
+
+ runtime_break_functions() -- optional
+ list of functions that we should break into depending on the
+ context
+ """
+
+ def exc_info(self, frame):
+ "See this class' docstring."
+
+ def runtime_break_functions(self):
+ """
+ Implement this if the list of step-into functions depends on the
+ context.
+ """
+
+
+class PythonInfo(LanguageInfo):
def pyframe(self, frame):
pyframe = Frame(frame).get_pyop()
yield 'PyEval_EvalFrameEx'
-class PyStep(PythonCodeStepper):
- "Step through Python code."
-
- def __init__(self, *args, **kwargs):
- super(PyStep, self).__init__(*args, **kwargs)
- self.lastframe = None
+class PythonStepperMixin(object):
+ """
+ Make this a mixin so CyStep can also inherit from this and use a
+ CythonCodeStepper at the same time
+ """
- def invoke(self, args, from_tty):
+ def python_step(self, stepinto):
# Set a watchpoint for a frame once as deleting it will make py-step
# unrepeatable.
# See http://sourceware.org/bugzilla/show_bug.cgi?id=12216
newframe = gdb.selected_frame()
framewrapper = Frame(newframe)
- if newframe != self.lastframe and framewrapper.is_evalframeex():
+ if (newframe != getattr(self, 'lastframe', None) and
+ framewrapper.is_evalframeex()):
self.lastframe = newframe
output = gdb.execute('watch f->f_lasti', to_string=True)
- self.step(stepover_command='py-finish')
+ self.step(stepinto=stepinto, stepover_command='finish')
# match = re.search(r'[Ww]atchpoint (\d+):', output)
# if match:
# watchpoint = match.group(1)
# gdb.execute('delete %s' % watchpoint)
+
+
+class PyStep(ExecutionControlCommandBase, PythonStepperMixin):
+ "Step through Python code."
+
+ stepinto = True
def invoke(self, args, from_tty):
- self.step()
+ self.python_step(stepinto=self.stepinto)
-class PyNext(PythonCodeStepper):
+class PyNext(PyStep):
"Step-over Python code."
- invoke = PythonCodeStepper.step
+ stepinto = False
-class PyFinish(PythonCodeStepper):
+class PyFinish(ExecutionControlCommandBase):
"Execute until function returns to a caller."
- invoke = PythonCodeStepper.finish
+ invoke = ExecutionControlCommandBase.finish
-class PyRun(PythonCodeStepper):
+class PyRun(ExecutionControlCommandBase):
"Run the program."
- invoke = PythonCodeStepper.run
+ invoke = ExecutionControlCommandBase.run
-class PyCont(PythonCodeStepper):
+class PyCont(ExecutionControlCommandBase):
- invoke = PythonCodeStepper.cont
+ invoke = ExecutionControlCommandBase.cont
-py_step = PyStep('py-step', stepinto=True)
-py_next = PyNext('py-next', stepinto=False)
-py_finish = PyFinish('py-finish')
-py_run = PyRun('py-run')
-py_cont = PyCont('py-cont')
+py_step = PyStep('py-step', PythonInfo())
+py_next = PyNext('py-next', PythonInfo())
+py_finish = PyFinish('py-finish', PythonInfo())
+py_run = PyRun('py-run', PythonInfo())
+py_cont = PyCont('py-cont', PythonInfo())
gdb.execute('set breakpoint pending on')
py_step.init_breakpoints()