Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
117ef042
Commit
117ef042
authored
Dec 14, 2010
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor inferior execution control code, better gdb message handling and re-fix runtests.py
parent
bd41e488
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
178 additions
and
161 deletions
+178
-161
Cython/Debugger/Tests/test_libcython_in_gdb.py
Cython/Debugger/Tests/test_libcython_in_gdb.py
+1
-1
Cython/Debugger/libcython.py
Cython/Debugger/libcython.py
+25
-22
Cython/Debugger/libpython.py
Cython/Debugger/libpython.py
+146
-132
runtests.py
runtests.py
+6
-6
No files found.
Cython/Debugger/Tests/test_libcython_in_gdb.py
View file @
117ef042
...
...
@@ -58,7 +58,7 @@ class DebugTestCase(unittest.TestCase):
if
source_line
is
not
None
:
lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
frame
=
gdb
.
selected_frame
()
self
.
assertEqual
(
libcython
.
cy
.
step
.
lineno
(
frame
),
lineno
)
self
.
assertEqual
(
libcython
.
cy
thon_info
.
lineno
(
frame
),
lineno
)
def
break_and_run
(
self
,
source_line
):
break_lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
...
...
Cython/Debugger/libcython.py
View file @
117ef042
...
...
@@ -835,10 +835,9 @@ class CyBreak(CythonCommand):
return
compl
class
Cython
CodeStepper
(
CythonCommand
,
libpython
.
GenericCodeStepper
):
class
Cython
Info
(
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
):
...
...
@@ -849,20 +848,16 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
if
self
.
is_cython_function
(
frame
):
return
self
.
get_cython_lineno
(
frame
)
else
:
return
libpython
.
py_step
.
lineno
(
frame
)
return
libpython
.
py_step
.
l
ang_info
.
l
ineno
(
frame
)
def
get_source_line
(
self
,
frame
):
try
:
line
=
super
(
Cython
CodeStepper
,
self
).
get_source_line
(
frame
)
line
=
super
(
Cython
Info
,
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
...
...
@@ -873,7 +868,15 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
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'
...
...
@@ -881,8 +884,7 @@ class CyStep(CythonCodeStepper):
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'
...
...
@@ -891,7 +893,7 @@ class CyStep(CythonCodeStepper):
self
.
finish_executing
(
gdb
.
execute
(
command
,
to_string
=
True
))
else
:
self
.
step
()
self
.
step
(
stepinto
=
self
.
stepinto
)
class
CyNext
(
CyStep
):
...
...
@@ -901,7 +903,7 @@ class CyNext(CyStep):
stepinto
=
False
class
CyRun
(
Cython
CodeStepper
):
class
CyRun
(
Cython
ExecutionControlCommand
):
"""
Run a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well
...
...
@@ -909,26 +911,26 @@ class CyRun(CythonCodeStepper):
name
=
'cy run'
invoke
=
Cython
CodeStepper
.
run
invoke
=
Cython
ExecutionControlCommand
.
run
class
CyCont
(
Cy
Run
):
class
CyCont
(
Cy
thonExecutionControlCommand
):
"""
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
=
Cython
CodeStepper
.
cont
invoke
=
Cython
ExecutionControlCommand
.
cont
class
CyFinish
(
Cy
Run
):
class
CyFinish
(
Cy
thonExecutionControlCommand
):
"""
Execute until the function returns.
"""
name
=
'cy finish'
invoke
=
Cython
CodeStepper
.
finish
invoke
=
Cython
ExecutionControlCommand
.
finish
class
CyUp
(
CythonCommand
):
...
...
@@ -964,7 +966,7 @@ class CyDown(CyUp):
_command
=
'down'
class
CySelect
(
CythonCo
deStepper
):
class
CySelect
(
CythonCo
mmand
):
"""
Select a frame. Use frame numbers as listed in `cy backtrace`.
This command is useful because `cy backtrace` prints a reversed backtrace.
...
...
@@ -982,7 +984,7 @@ class CySelect(CythonCodeStepper):
while
frame
.
newer
():
frame
=
frame
.
newer
()
stackdepth
=
self
.
_
stackdepth
(
frame
)
stackdepth
=
libpython
.
stackdepth
(
frame
)
try
:
gdb
.
execute
(
'select %d'
%
(
stackdepth
-
stackno
-
1
,))
...
...
@@ -1288,5 +1290,6 @@ class CyLine(gdb.Function, CythonBase):
def
invoke
(
self
):
return
self
.
get_cython_lineno
()
cython_info
=
CythonInfo
()
cy
=
CyCy
.
register
()
cython_info
.
cy
=
cy
\ No newline at end of file
Cython/Debugger/libpython.py
View file @
117ef042
...
...
@@ -1841,46 +1841,30 @@ def get_selected_inferior():
if thread == selected_thread:
return inferior
def stackdepth(frame):
"
Tells
the
stackdepth
of
a
gdb
frame
.
"
depth = 0
while frame:
frame = frame.older()
depth += 1
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
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)
...
...
@@ -1897,7 +1881,7 @@ class GenericCodeStepper(gdb.Command):
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:
...
...
@@ -1929,7 +1913,7 @@ class GenericCodeStepper(gdb.Command):
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
...
...
@@ -1946,71 +1930,48 @@ class GenericCodeStepper(gdb.Command):
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)
if match:
print match.group(0)
def filter_output(self, result):
output = []
return ''
match_finish = re.search(r'^Value returned is
\
$
\
d+ = (.*)', result,
re.MULTILINE)
if match_finish:
output.append('Value returned: %s' % match_finish.group(1))
def _stackdepth(self, frame):
depth = 0
while frame:
frame = frame.older()
depth += 1
reflags = re.MULTILINE
regexes = [
(r'^Program received signal .*', reflags|re.DOTALL),
(r'.*[Ww]arning.*', 0),
(r'^Program exited .*', reflags),
]
return depth
for regex, flags in regexes:
match = re.search(regex, result, flags)
if match:
output.append(match.group(0))
return '
\
n
'.join(output)
def finish_executing(self, output):
def stopped(self):
return get_selected_inferior().pid == 0
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:
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)
result = self.filter_output(result)
if line is None:
print output
else:
print result
print line
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)
if self.lang_info.is_relevant_function(frame):
print self.lang_info.get_source_line(frame) or result
else:
print result.strip()
else:
print output
print result
def _finish(self):
"""
...
...
@@ -2023,11 +1984,10 @@ class GenericCodeStepper(gdb.Command):
# 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()
...
...
@@ -2037,13 +1997,18 @@ class GenericCodeStepper(gdb.Command):
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, step
into, step
over_command='next'):
"""
Do a single step or step-over. Returns the result of the last gdb
command that made execution stop.
...
...
@@ -2062,34 +2027,34 @@ class GenericCodeStepper(gdb.Command):
works properly with local trace functions, see
PyFrameObjectPtr.current_line_num and PyFrameObjectPtr.addr2line.
"""
if s
elf.s
tepinto:
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.l
ineno() as it may raise for
# irrelevant frames.
beginline = self.lineno(beginframe)
# immediately. So don't call self.l
ang_info.lineno() as it may
#
raise for
irrelevant frames.
beginline = self.l
ang_info.l
ineno(beginframe)
if not s
elf.s
tepinto:
depth = s
elf._s
tackdepth(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:
...
...
@@ -2108,9 +2073,9 @@ class GenericCodeStepper(gdb.Command):
if newframe != beginframe:
# new function
if not s
elf.s
tepinto:
if not stepinto:
# see if we returned to the caller
newdepth = s
elf._s
tackdepth(newframe)
newdepth = stackdepth(newframe)
is_relevant_function = (newdepth < depth and
is_relevant_function)
...
...
@@ -2119,11 +2084,11 @@ class GenericCodeStepper(gdb.Command):
else:
# newframe equals beginframe, check for a difference in the
# line number
lineno = self.lineno(newframe)
lineno = self.l
ang_info.l
ineno(newframe)
if lineno and lineno != beginline:
break
if s
elf.s
tepinto:
if stepinto:
self.disable_breakpoints()
self.finish_executing(result)
...
...
@@ -2135,7 +2100,50 @@ class GenericCodeStepper(gdb.Command):
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()
...
...
@@ -2164,14 +2172,13 @@ class PythonCodeStepper(GenericCodeStepper):
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
...
...
@@ -2180,45 +2187,52 @@ class PyStep(PythonCodeStepper):
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(step
over_command='py-
finish')
self.step(step
into=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(Py
thonCodeStepper
):
class PyNext(Py
Step
):
"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()
...
...
runtests.py
View file @
117ef042
...
...
@@ -644,12 +644,12 @@ class CythonUnitTestCase(CythonCompileTestCase):
except
Exception
:
pass
try
:
import
gdb
include_debugger
=
sys
.
version_info
[:
2
]
>
(
2
,
5
)
except
:
include_debugger
=
False
# Someone wrapped this in a:
# 'try: import gdb; ... except: include_debugger = False' thing, but don't do
# this, it doesn't work as gdb is a builtin module in GDB. The tests themselves
# are doing the skipping. If there's a problem with the tests, please file an
# issue.
include_debugger
=
sys
.
version_info
[:
2
]
>
(
2
,
5
)
def
collect_unittests
(
path
,
module_prefix
,
suite
,
selectors
):
def
file_matches
(
filename
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment