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
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
98c5444d
Commit
98c5444d
authored
Dec 14, 2010
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Faster Python stepping using a watchpoint approach (f->f_lasti)
parent
0eb42da3
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
95 additions
and
33 deletions
+95
-33
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
+12
-9
Cython/Debugger/libpython.py
Cython/Debugger/libpython.py
+82
-23
No files found.
Cython/Debugger/Tests/test_libcython_in_gdb.py
View file @
98c5444d
...
@@ -262,7 +262,7 @@ class TestBacktrace(DebugTestCase):
...
@@ -262,7 +262,7 @@ class TestBacktrace(DebugTestCase):
gdb
.
execute
(
'cy bt'
)
gdb
.
execute
(
'cy bt'
)
result
=
gdb
.
execute
(
'cy bt -a'
,
to_string
=
True
)
result
=
gdb
.
execute
(
'cy bt -a'
,
to_string
=
True
)
assert
re
.
search
(
r'\
#
0 *0x.* in main\
(
\)
at
'
,
result
),
result
assert
re
.
search
(
r'\
#
0 *0x.* in main\
(
\)'
,
result
),
result
class
TestFunctions
(
DebugTestCase
):
class
TestFunctions
(
DebugTestCase
):
...
...
Cython/Debugger/libcython.py
View file @
98c5444d
...
@@ -872,8 +872,18 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
...
@@ -872,8 +872,18 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
result
.
extend
(
self
.
cy
.
functions_by_cname
)
result
.
extend
(
self
.
cy
.
functions_by_cname
)
return
result
return
result
class
CyStep
(
CythonCodeStepper
):
"Step through Cython, Python or C code."
name
=
'cy step'
stepinto
=
True
def
invoke
(
self
,
args
,
from_tty
):
def
invoke
(
self
,
args
,
from_tty
):
if
not
self
.
is_cython_function
()
and
not
self
.
is_python_function
():
if
self
.
is_python_function
():
libpython
.
py_step
.
get_source_line
=
self
.
get_source_line
libpython
.
py_step
.
invoke
(
args
,
from_tty
)
elif
not
self
.
is_cython_function
():
if
self
.
stepinto
:
if
self
.
stepinto
:
command
=
'step'
command
=
'step'
else
:
else
:
...
@@ -884,14 +894,7 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
...
@@ -884,14 +894,7 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
self
.
step
()
self
.
step
()
class
CyStep
(
CythonCodeStepper
):
class
CyNext
(
CyStep
):
"Step through Cython, Python or C code."
name
=
'cy step'
stepinto
=
True
class
CyNext
(
CythonCodeStepper
):
"Step-over Python code."
"Step-over Python code."
name
=
'cy next'
name
=
'cy next'
...
...
Cython/Debugger/libpython.py
View file @
98c5444d
...
@@ -1715,6 +1715,8 @@ class PyNameEquals(gdb.Function):
...
@@ -1715,6 +1715,8 @@ class PyNameEquals(gdb.Function):
if frame.is_evalframeex():
if frame.is_evalframeex():
pyframe = frame.get_pyop()
pyframe = frame.get_pyop()
if pyframe is None:
if pyframe is None:
warnings.warn("Use a Python debug build, Python breakpoints "
"won'
t
work
otherwise
.
")
return None
return None
return getattr(pyframe, attr).proxyval(set())
return getattr(pyframe, attr).proxyval(set())
...
@@ -1846,7 +1848,8 @@ class GenericCodeStepper(gdb.Command):
...
@@ -1846,7 +1848,8 @@ class GenericCodeStepper(gdb.Command):
methods:
methods:
lineno(frame)
lineno(frame)
tells the current line number (only called for a relevant 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)
is_relevant_function(frame)
tells whether we care about frame 'frame'
tells whether we care about frame 'frame'
...
@@ -1956,7 +1959,11 @@ class GenericCodeStepper(gdb.Command):
...
@@ -1956,7 +1959,11 @@ class GenericCodeStepper(gdb.Command):
elif get_selected_inferior().pid == 0:
elif get_selected_inferior().pid == 0:
return result
return result
else:
else:
return None
match = re.search('.*[Ww]arning.*', result, re.MULTILINE)
if match:
print match.group(0)
return ''
def _stackdepth(self, frame):
def _stackdepth(self, frame):
depth = 0
depth = 0
...
@@ -1966,13 +1973,13 @@ class GenericCodeStepper(gdb.Command):
...
@@ -1966,13 +1973,13 @@ class GenericCodeStepper(gdb.Command):
return depth
return depth
def finish_executing(self,
resul
t):
def finish_executing(self,
outpu
t):
"""
"""
After doing some kind of code running in the inferior, print the line
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
of source code or the result of the last executed gdb command (passed
in as the `result` argument).
in as the `result` argument).
"""
"""
result = self.stopped(
resul
t)
result = self.stopped(
outpu
t)
if result:
if result:
print result.strip()
print result.strip()
# check whether the program was killed by a signal, it should still
# check whether the program was killed by a signal, it should still
...
@@ -1982,7 +1989,13 @@ class GenericCodeStepper(gdb.Command):
...
@@ -1982,7 +1989,13 @@ class GenericCodeStepper(gdb.Command):
except RuntimeError:
except RuntimeError:
pass
pass
else:
else:
print self.get_source_line(frame)
line = self.get_source_line(frame)
if line is None:
print output
else:
print result
print line
else:
else:
frame = gdb.selected_frame()
frame = gdb.selected_frame()
output = None
output = None
...
@@ -2024,33 +2037,53 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2024,33 +2037,53 @@ class GenericCodeStepper(gdb.Command):
break
break
hitbp = re.search(r'Breakpoint (
\
d+)
'
, result)
hitbp = re.search(r'Breakpoint (
\
d+)
'
, result)
is_rel
a
vant = self.is_relevant_function(frame)
is_rel
e
vant = self.is_relevant_function(frame)
if hitbp or is_rel
a
vant or self.stopped(result):
if hitbp or is_rel
e
vant or self.stopped(result):
break
break
self.finish_executing(result)
self.finish_executing(result)
def
_step(self
):
def
step(self, stepover_command='next'
):
"""
"""
Do a single step or step-over. Returns the result of the last gdb
Do a single step or step-over. Returns the result of the last gdb
command that made execution stop.
command that made execution stop.
This implementation, for stepping, sets (conditional) breakpoints for
all functions that are deemed relevant. It then does a step over until
either something halts execution, or until the next line is reached.
If, however, stepover_command is given, it should be a string gdb
command that continues execution in some way. The idea is that the
caller has set a (conditional) breakpoint or watchpoint that can work
more efficiently than the step-over loop. For Python this means setting
a watchpoint for f->f_lasti, which means we can then subsequently
"
finish
" frames.
We want f->f_lasti instead of f->f_lineno, because the latter only
works properly with local trace functions, see
PyFrameObjectPtr.current_line_num and PyFrameObjectPtr.addr2line.
"""
"""
if self.stepinto:
if self.stepinto:
self.enable_breakpoints()
self.enable_breakpoints()
beginframe = gdb.selected_frame()
beginframe = gdb.selected_frame()
beginline = self.lineno(beginframe)
if not self.stepinto:
if self.is_relevant_function(beginframe):
depth = self._stackdepth(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)
if not self.stepinto:
depth = self._stackdepth(beginframe)
newframe = beginframe
newframe = beginframe
result = ''
while True:
while True:
if self.is_relevant_function(newframe):
if self.is_relevant_function(newframe):
result = gdb.execute(
'
next
'
, to_string=True)
result = gdb.execute(
stepover_command
, to_string=True)
else:
else:
result = self._
finish()
self.
finish()
if self.stopped(result):
if self.stopped(result):
break
break
...
@@ -2065,7 +2098,8 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2065,7 +2098,8 @@ class GenericCodeStepper(gdb.Command):
m = re.search(r'Breakpoint (
\
d+)
'
, result)
m = re.search(r'Breakpoint (
\
d+)
'
, result)
if m:
if m:
bp = self.runtime_breakpoints.get(framename)
bp = self.runtime_breakpoints.get(framename)
if bp is None or (m.group(1) == bp and is_relevant_function):
if (bp is None or
(is_relevant_function and bp == m.group(1))):
# although we hit a breakpoint, we still need to check
# although we hit a breakpoint, we still need to check
# that the function, in case hit by a runtime breakpoint,
# that the function, in case hit by a runtime breakpoint,
# is in the right context
# is in the right context
...
@@ -2083,17 +2117,17 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2083,17 +2117,17 @@ class GenericCodeStepper(gdb.Command):
if is_relevant_function:
if is_relevant_function:
break
break
else:
else:
if self.lineno(newframe) > beginline:
# newframe equals beginframe, check for a difference in the
# line number
lineno = self.lineno(newframe)
if lineno and lineno != beginline:
break
break
if self.stepinto:
if self.stepinto:
self.disable_breakpoints()
self.disable_breakpoints()
return result
self.finish_executing(result)
def step(self, *args):
return self.finish_executing(self._step())
def run(self, *args):
def run(self, *args):
self.finish_executing(gdb.execute('run', to_string=True))
self.finish_executing(gdb.execute('run', to_string=True))
...
@@ -2108,7 +2142,7 @@ class PythonCodeStepper(GenericCodeStepper):
...
@@ -2108,7 +2142,7 @@ class PythonCodeStepper(GenericCodeStepper):
if pyframe:
if pyframe:
return pyframe
return pyframe
else:
else:
raise gdb.
Gdb
Error(
raise gdb.
Runtime
Error(
"
Unable
to
find
the
Python
frame
,
run
your
code
with
a
debug
"
"
Unable
to
find
the
Python
frame
,
run
your
code
with
a
debug
"
"
build
(
configure
with
--
with
-
pydebug
or
compile
with
-
g
).
")
"
build
(
configure
with
--
with
-
pydebug
or
compile
with
-
g
).
")
...
@@ -2133,7 +2167,32 @@ class PythonCodeStepper(GenericCodeStepper):
...
@@ -2133,7 +2167,32 @@ class PythonCodeStepper(GenericCodeStepper):
class PyStep(PythonCodeStepper):
class PyStep(PythonCodeStepper):
"
Step
through
Python
code
.
"
"
Step
through
Python
code
.
"
invoke = PythonCodeStepper.step
def __init__(self, *args, **kwargs):
super(PyStep, self).__init__(*args, **kwargs)
self.lastframe = None
def invoke(self, args, from_tty):
# 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
# When the watchpoint goes out of scope it will automatically
# disappear.
newframe = gdb.selected_frame()
framewrapper = Frame(newframe)
if newframe != self.lastframe and framewrapper.is_evalframeex():
self.lastframe = newframe
output = gdb.execute('watch f->f_lasti', to_string=True)
self.step(stepover_command='py-finish')
# match = re.search(r'[Ww]atchpoint (
\
d+):
'
, output)
# if match:
# watchpoint = match.group(1)
# gdb.execute('delete %s' % watchpoint)
def invoke(self, args, from_tty):
self.step()
class PyNext(PythonCodeStepper):
class PyNext(PythonCodeStepper):
"
Step
-
over
Python
code
.
"
"
Step
-
over
Python
code
.
"
...
...
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