def test_cython_step(self):
gdb.execute('cy break codefile.spam')
- libcython.parameters.step_into_c_code.value = False
gdb.execute('run', to_string=True)
self.lineno_equals('def spam(a=0):')
self.step([('b', 1), ('c', 0)], source_line='c = 2')
self.step([('c', 2)], source_line='int(10)')
self.step([], source_line='puts("spam")')
- self.step([], source_line='os.path.join("foo", "bar")')
gdb.execute('cont', to_string=True)
self.assertEqual(len(gdb.inferiors()), 1)
self.assertEqual(gdb.inferiors()[0].pid, 0)
def test_c_step(self):
- libcython.parameters.step_into_c_code.value = True
self.break_and_run('some_c_function()')
gdb.execute('cy step', to_string=True)
self.assertEqual(gdb.selected_frame().name(), 'some_c_function')
class TestNext(DebugStepperTestCase):
def test_cython_next(self):
- libcython.parameters.step_into_c_code.value = True
self.break_and_run('c = 2')
lines = (
older_frame = frame.older()
if self.is_cython_function(frame) or self.is_python_function(frame):
return True
- elif (parameters.step_into_c_code and
- older_frame and self.is_cython_function(older_frame)):
+ elif older_frame and self.is_cython_function(older_frame):
# direct C function call from a Cython function
cython_func = self.get_cython_function(older_frame)
return name in cython_func.step_into_functions
Tell cygdb about the user's terminal background (light or dark).
"""
-class StepIntoCCode(CythonParameter):
- """
- Tells cygdb whether to step into C functions called directly from Cython
- code.
- """
-
class CythonParameters(object):
"""
Simple container class that might get more functionality in the distant
gdb.COMMAND_FILES,
gdb.PARAM_STRING,
"dark")
- self.step_into_c_code = StepIntoCCode(
- 'cy_step_into_c_code',
- gdb.COMMAND_RUNNING,
- gdb.PARAM_BOOLEAN,
- True)
parameters = CythonParameters()
next = CyNext.register(),
run = CyRun.register(),
cont = CyCont.register(),
+ finish = CyFinish.register(),
up = CyUp.register(),
down = CyDown.register(),
bt = CyBacktrace.register(),
self.finish_executing(gdb.execute(command, to_string=True))
else:
- super(CythonCodeStepper, self).invoke(args, from_tty)
+ self.step()
class CyStep(CythonCodeStepper):
"""
name = 'cy run'
- _command = 'run'
- def invoke(self, *args):
- self.finish_executing(gdb.execute(self._command, to_string=True))
+ invoke = CythonCodeStepper.run
class CyCont(CyRun):
"""
name = 'cy cont'
- _command = 'cont'
+ invoke = CythonCodeStepper.cont
+
+
+class CyFinish(CyRun):
+ """
+ Execute until the function returns.
+ """
+ name = 'cy finish'
+
+ invoke = CythonCodeStepper.finish
class CyUp(CythonCommand):
context.
"""
+ def stopped(self):
+ return gdb.inferiors()[0].pid == 0
+
+ def _stackdepth(self, frame):
+ depth = 0
+ while frame:
+ frame = frame.older()
+ depth += 1
+
+ return depth
+
+ 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).
+ """
+ if self.stopped():
+ print result.strip()
+ 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()
+ else:
+ print output
+
+ def _finish(self):
+ """
+ Execute until the function returns (or until something else makes it
+ stop)
+ """
+ if gdb.selected_frame().older() is not None:
+ return gdb.execute('finish', to_string=True)
+ else:
+ # outermost frame, continue
+ return gdb.execute('cont', to_string=True)
+
+ def finish(self, *args):
+ """
+ Execute until the function returns to a relevant caller.
+ """
+
+ while True:
+ result = self._finish()
+
+ try:
+ frame = gdb.selected_frame()
+ except RuntimeError:
+ break
+
+ hitbp = re.search(r'Breakpoint (\d+)', result)
+ is_relavant = self.is_relevant_function(frame)
+ if hitbp or is_relavant or self.stopped():
+ break
+
+ self.finish_executing(result)
+
def _step(self):
"""
Do a single step or step-over. Returns the result of the last gdb
if self.is_relevant_function(newframe):
result = gdb.execute('next', to_string=True)
else:
- if self._stackdepth(newframe) == 1:
- result = gdb.execute('cont', to_string=True)
- else:
- result = gdb.execute('finish', to_string=True)
+ result = self._finish()
if self.stopped():
break
self.disable_breakpoints()
return result
-
- def stopped(self):
- return gdb.inferiors()[0].pid == 0
-
- def _stackdepth(self, frame):
- depth = 0
- while frame:
- frame = frame.older()
- depth += 1
-
- return depth
- def invoke(self, args, from_tty):
- self.finish_executing(self._step())
+ def step(self):
+ return self.finish_executing(self._step())
+
+ def run(self, *args):
+ self.finish_executing(gdb.execute('run', to_string=True))
- def finish_executing(self, result):
- if self.stopped():
- print result.strip()
- 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()
- else:
- print output
+ def cont(self, *args):
+ self.finish_executing(gdb.execute('cont', to_string=True))
class PythonCodeStepper(GenericCodeStepper):
class PyStep(PythonCodeStepper):
"Step through Python code."
-
+
+ invoke = PythonCodeStepper.step
+
class PyNext(PythonCodeStepper):
"Step-over Python code."
+
+ invoke = PythonCodeStepper.step
+
+class PyFinish(PythonCodeStepper):
+ "Execute until function returns to a caller."
+
+ invoke = PythonCodeStepper.finish
+
+class PyRun(PythonCodeStepper):
+ "Run the program."
+
+ invoke = PythonCodeStepper.run
+
+class PyCont(PythonCodeStepper):
+
+ invoke = PythonCodeStepper.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.init_breakpoints()
\ No newline at end of file