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
Kirill Smelkov
cython
Commits
c5e16a3f
Commit
c5e16a3f
authored
Jan 13, 2011
by
Robert Bradshaw
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://github.com/markflorisson88/cython
into markflorisson88-master
parents
b4c160b5
05533ff0
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1039 additions
and
748 deletions
+1039
-748
Cython/Compiler/Main.py
Cython/Compiler/Main.py
+33
-28
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+163
-84
Cython/Compiler/Tests/TestParseTreeTransforms.py
Cython/Compiler/Tests/TestParseTreeTransforms.py
+16
-15
Cython/Debugger/Tests/TestLibCython.py
Cython/Debugger/Tests/TestLibCython.py
+11
-0
Cython/Debugger/Tests/codefile
Cython/Debugger/Tests/codefile
+15
-4
Cython/Debugger/Tests/test_libcython_in_gdb.py
Cython/Debugger/Tests/test_libcython_in_gdb.py
+69
-7
Cython/Debugger/libcython.py
Cython/Debugger/libcython.py
+305
-245
Cython/Debugger/libpython.py
Cython/Debugger/libpython.py
+396
-335
runtests.py
runtests.py
+28
-28
setup.py
setup.py
+3
-2
No files found.
Cython/Compiler/Main.py
View file @
c5e16a3f
...
@@ -18,7 +18,11 @@ from time import time
...
@@ -18,7 +18,11 @@ from time import time
import
Code
import
Code
import
Errors
import
Errors
import
Parsing
# Do not import Parsing here, import it when needed, because Parsing imports
# Nodes, which globally needs debug command line options initialized to set a
# conditional metaclass. These options are processed by CmdLine called from
# main() in this file.
# import Parsing
import
Version
import
Version
from
Scanning
import
PyrexScanner
,
FileSourceDescriptor
from
Scanning
import
PyrexScanner
,
FileSourceDescriptor
from
Errors
import
PyrexError
,
CompileError
,
InternalError
,
AbortError
,
error
,
warning
from
Errors
import
PyrexError
,
CompileError
,
InternalError
,
AbortError
,
error
,
warning
...
@@ -496,6 +500,7 @@ class Context(object):
...
@@ -496,6 +500,7 @@ class Context(object):
try
:
try
:
f
=
Utils
.
open_source_file
(
source_filename
,
"rU"
)
f
=
Utils
.
open_source_file
(
source_filename
,
"rU"
)
try
:
try
:
import
Parsing
s
=
PyrexScanner
(
f
,
source_desc
,
source_encoding
=
f
.
encoding
,
s
=
PyrexScanner
(
f
,
source_desc
,
source_encoding
=
f
.
encoding
,
scope
=
scope
,
context
=
self
)
scope
=
scope
,
context
=
self
)
tree
=
Parsing
.
p_module
(
s
,
pxd
,
full_module_name
)
tree
=
Parsing
.
p_module
(
s
,
pxd
,
full_module_name
)
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
c5e16a3f
...
@@ -1591,6 +1591,10 @@ class DebugTransform(CythonTransform):
...
@@ -1591,6 +1591,10 @@ class DebugTransform(CythonTransform):
#self.c_output_file = options.output_file
#self.c_output_file = options.output_file
self
.
c_output_file
=
result
.
c_file
self
.
c_output_file
=
result
.
c_file
# Closure support, basically treat nested functions as if the AST were
# never nested
self
.
nested_funcdefs
=
[]
# tells visit_NameNode whether it should register step-into functions
# tells visit_NameNode whether it should register step-into functions
self
.
register_stepinto
=
False
self
.
register_stepinto
=
False
...
@@ -1605,7 +1609,16 @@ class DebugTransform(CythonTransform):
...
@@ -1605,7 +1609,16 @@ class DebugTransform(CythonTransform):
# serialize functions
# serialize functions
self
.
tb
.
start
(
'Functions'
)
self
.
tb
.
start
(
'Functions'
)
# First, serialize functions normally...
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
# ... then, serialize nested functions
for
nested_funcdef
in
self
.
nested_funcdefs
:
self
.
visit_FuncDefNode
(
nested_funcdef
)
self
.
register_stepinto
=
True
self
.
serialize_modulenode_as_function
(
node
)
self
.
register_stepinto
=
False
self
.
tb
.
end
(
'Functions'
)
self
.
tb
.
end
(
'Functions'
)
# 2.3 compatibility. Serialize global variables
# 2.3 compatibility. Serialize global variables
...
@@ -1627,6 +1640,14 @@ class DebugTransform(CythonTransform):
...
@@ -1627,6 +1640,14 @@ class DebugTransform(CythonTransform):
def
visit_FuncDefNode
(
self
,
node
):
def
visit_FuncDefNode
(
self
,
node
):
self
.
visited
.
add
(
node
.
local_scope
.
qualified_name
)
self
.
visited
.
add
(
node
.
local_scope
.
qualified_name
)
if
getattr
(
node
,
'is_wrapper'
,
False
):
return
node
if
self
.
register_stepinto
:
self
.
nested_funcdefs
.
append
(
node
)
return
node
# node.entry.visibility = 'extern'
# node.entry.visibility = 'extern'
if
node
.
py_func
is
None
:
if
node
.
py_func
is
None
:
pf_cname
=
''
pf_cname
=
''
...
@@ -1678,6 +1699,51 @@ class DebugTransform(CythonTransform):
...
@@ -1678,6 +1699,51 @@ class DebugTransform(CythonTransform):
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
return
node
return
node
def
serialize_modulenode_as_function
(
self
,
node
):
"""
Serialize the module-level code as a function so the debugger will know
it's a "relevant frame" and it will know where to set the breakpoint
for 'break modulename'.
"""
name
=
node
.
full_module_name
.
rpartition
(
'.'
)[
-
1
]
cname_py2
=
'init'
+
name
cname_py3
=
'PyInit_'
+
name
py2_attrs
=
dict
(
name
=
name
,
cname
=
cname_py2
,
pf_cname
=
''
,
# Ignore the qualified_name, breakpoints should be set using
# `cy break modulename:lineno` for module-level breakpoints.
qualified_name
=
''
,
lineno
=
'1'
,
is_initmodule_function
=
"True"
,
)
py3_attrs
=
dict
(
py2_attrs
,
cname
=
cname_py3
)
self
.
_serialize_modulenode_as_function
(
node
,
py2_attrs
)
self
.
_serialize_modulenode_as_function
(
node
,
py3_attrs
)
def
_serialize_modulenode_as_function
(
self
,
node
,
attrs
):
self
.
tb
.
start
(
'Function'
,
attrs
=
attrs
)
self
.
tb
.
start
(
'Locals'
)
self
.
serialize_local_variables
(
node
.
scope
.
entries
)
self
.
tb
.
end
(
'Locals'
)
self
.
tb
.
start
(
'Arguments'
)
self
.
tb
.
end
(
'Arguments'
)
self
.
tb
.
start
(
'StepIntoFunctions'
)
self
.
register_stepinto
=
True
self
.
visitchildren
(
node
)
self
.
register_stepinto
=
False
self
.
tb
.
end
(
'StepIntoFunctions'
)
self
.
tb
.
end
(
'Function'
)
def
serialize_local_variables
(
self
,
entries
):
def
serialize_local_variables
(
self
,
entries
):
for
entry
in
entries
.
values
():
for
entry
in
entries
.
values
():
if
entry
.
type
.
is_pyobject
:
if
entry
.
type
.
is_pyobject
:
...
@@ -1685,9 +1751,22 @@ class DebugTransform(CythonTransform):
...
@@ -1685,9 +1751,22 @@ class DebugTransform(CythonTransform):
else
:
else
:
vartype
=
'CObject'
vartype
=
'CObject'
if
entry
.
from_closure
:
# We're dealing with a closure where a variable from an outer
# scope is accessed, get it from the scope object.
cname
=
'%s->%s'
%
(
Naming
.
cur_scope_cname
,
entry
.
outer_entry
.
cname
)
qname
=
'%s.%s.%s'
%
(
entry
.
scope
.
outer_scope
.
qualified_name
,
entry
.
scope
.
name
,
entry
.
name
)
elif
entry
.
in_closure
:
cname
=
'%s->%s'
%
(
Naming
.
cur_scope_cname
,
entry
.
cname
)
qname
=
entry
.
qualified_name
else
:
cname
=
entry
.
cname
cname
=
entry
.
cname
# if entry.type.is_extension_type:
qname
=
entry
.
qualified_name
# cname = entry.type.typeptr_cname
if
not
entry
.
pos
:
if
not
entry
.
pos
:
# this happens for variables that are not in the user's code,
# this happens for variables that are not in the user's code,
...
@@ -1700,7 +1779,7 @@ class DebugTransform(CythonTransform):
...
@@ -1700,7 +1779,7 @@ class DebugTransform(CythonTransform):
attrs
=
dict
(
attrs
=
dict
(
name
=
entry
.
name
,
name
=
entry
.
name
,
cname
=
cname
,
cname
=
cname
,
qualified_name
=
entry
.
qualified_
name
,
qualified_name
=
q
name
,
type
=
vartype
,
type
=
vartype
,
lineno
=
lineno
)
lineno
=
lineno
)
...
...
Cython/Compiler/Tests/TestParseTreeTransforms.py
View file @
c5e16a3f
...
@@ -182,7 +182,8 @@ class TestDebugTransform(DebuggerTestCase):
...
@@ -182,7 +182,8 @@ class TestDebugTransform(DebuggerTestCase):
self
.
assertEqual
(
'PythonObject'
,
xml_globals
.
get
(
'python_var'
))
self
.
assertEqual
(
'PythonObject'
,
xml_globals
.
get
(
'python_var'
))
# test functions
# test functions
funcnames
=
'codefile.spam'
,
'codefile.ham'
,
'codefile.eggs'
funcnames
=
(
'codefile.spam'
,
'codefile.ham'
,
'codefile.eggs'
,
'codefile.closure'
,
'codefile.inner'
)
required_xml_attrs
=
'name'
,
'cname'
,
'qualified_name'
required_xml_attrs
=
'name'
,
'cname'
,
'qualified_name'
assert
all
([
f
in
xml_funcs
for
f
in
funcnames
])
assert
all
([
f
in
xml_funcs
for
f
in
funcnames
])
spam
,
ham
,
eggs
=
[
xml_funcs
[
funcname
]
for
funcname
in
funcnames
]
spam
,
ham
,
eggs
=
[
xml_funcs
[
funcname
]
for
funcname
in
funcnames
]
...
...
Cython/Debugger/Tests/TestLibCython.py
View file @
c5e16a3f
...
@@ -16,6 +16,7 @@ from distutils import ccompiler
...
@@ -16,6 +16,7 @@ from distutils import ccompiler
import
runtests
import
runtests
import
Cython.Distutils.extension
import
Cython.Distutils.extension
import
Cython.Distutils.build_ext
from
Cython.Debugger
import
Cygdb
as
cygdb
from
Cython.Debugger
import
Cygdb
as
cygdb
root
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
root
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
...
@@ -24,6 +25,10 @@ cfuncs_file = os.path.join(root, 'cfuncs.c')
...
@@ -24,6 +25,10 @@ cfuncs_file = os.path.join(root, 'cfuncs.c')
with
open
(
codefile
)
as
f
:
with
open
(
codefile
)
as
f
:
source_to_lineno
=
dict
((
line
.
strip
(),
i
+
1
)
for
i
,
line
in
enumerate
(
f
))
source_to_lineno
=
dict
((
line
.
strip
(),
i
+
1
)
for
i
,
line
in
enumerate
(
f
))
# Cython.Distutils.__init__ imports build_ext from build_ext which means we
# can't access the module anymore. Get it from sys.modules instead.
build_ext
=
sys
.
modules
[
'Cython.Distutils.build_ext'
]
class
DebuggerTestCase
(
unittest
.
TestCase
):
class
DebuggerTestCase
(
unittest
.
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
...
@@ -52,6 +57,9 @@ class DebuggerTestCase(unittest.TestCase):
...
@@ -52,6 +57,9 @@ class DebuggerTestCase(unittest.TestCase):
module
=
'codefile'
,
module
=
'codefile'
,
)
)
optimization_disabler
=
build_ext
.
Optimization
()
optimization_disabler
.
disable_optimization
()
cython_compile_testcase
=
runtests
.
CythonCompileTestCase
(
cython_compile_testcase
=
runtests
.
CythonCompileTestCase
(
workdir
=
self
.
tempdir
,
workdir
=
self
.
tempdir
,
# we clean up everything (not only compiled files)
# we clean up everything (not only compiled files)
...
@@ -77,6 +85,8 @@ class DebuggerTestCase(unittest.TestCase):
...
@@ -77,6 +85,8 @@ class DebuggerTestCase(unittest.TestCase):
**
opts
**
opts
)
)
optimization_disabler
.
restore_state
()
# ext = Cython.Distutils.extension.Extension(
# ext = Cython.Distutils.extension.Extension(
# 'codefile',
# 'codefile',
# ['codefile.pyx'],
# ['codefile.pyx'],
...
@@ -95,6 +105,7 @@ class DebuggerTestCase(unittest.TestCase):
...
@@ -95,6 +105,7 @@ class DebuggerTestCase(unittest.TestCase):
class
GdbDebuggerTestCase
(
DebuggerTestCase
):
class
GdbDebuggerTestCase
(
DebuggerTestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
GdbDebuggerTestCase
,
self
).
setUp
()
super
(
GdbDebuggerTestCase
,
self
).
setUp
()
...
...
Cython/Debugger/Tests/codefile
View file @
c5e16a3f
...
@@ -22,15 +22,26 @@ def spam(a=0):
...
@@ -22,15 +22,26 @@ def spam(a=0):
os.path.join("foo", "bar")
os.path.join("foo", "bar")
some_c_function()
some_c_function()
c
def ham
():
c
pdef eggs
():
pass
pass
c
pdef eggs
():
c
def ham
():
pass
pass
cdef class SomeClass(object):
cdef class SomeClass(object):
def spam(self):
def spam(self):
pass
pass
def outer():
cdef object a = "an object"
def inner():
b = 2
# access closed over variables
print a, b
return inner
outer()()
spam()
spam()
print "bye!"
print "bye!"
\ No newline at end of file
Cython/Debugger/Tests/test_libcython_in_gdb.py
View file @
c5e16a3f
...
@@ -14,6 +14,7 @@ import warnings
...
@@ -14,6 +14,7 @@ import warnings
import
unittest
import
unittest
import
textwrap
import
textwrap
import
tempfile
import
tempfile
import
functools
import
traceback
import
traceback
import
itertools
import
itertools
from
test
import
test_support
from
test
import
test_support
...
@@ -28,12 +29,35 @@ from Cython.Debugger.Tests import TestLibCython as test_libcython
...
@@ -28,12 +29,35 @@ from Cython.Debugger.Tests import TestLibCython as test_libcython
sys
.
argv
=
[
'gdb'
]
sys
.
argv
=
[
'gdb'
]
def
print_on_call_decorator
(
func
):
@
functools
.
wraps
(
func
)
def
wrapper
(
self
,
*
args
,
**
kwargs
):
_debug
(
type
(
self
).
__name__
,
func
.
__name__
)
try
:
return
func
(
self
,
*
args
,
**
kwargs
)
except
Exception
,
e
:
_debug
(
"An exception occurred:"
,
traceback
.
format_exc
(
e
))
raise
return
wrapper
class
TraceMethodCallMeta
(
type
):
def
__init__
(
self
,
name
,
bases
,
dict
):
for
func_name
,
func
in
dict
.
iteritems
():
if
inspect
.
isfunction
(
func
):
setattr
(
self
,
func_name
,
print_on_call_decorator
(
func
))
class
DebugTestCase
(
unittest
.
TestCase
):
class
DebugTestCase
(
unittest
.
TestCase
):
"""
"""
Base class for test cases. On teardown it kills the inferior and unsets
Base class for test cases. On teardown it kills the inferior and unsets
all breakpoints.
all breakpoints.
"""
"""
__metaclass__
=
TraceMethodCallMeta
def
__init__
(
self
,
name
):
def
__init__
(
self
,
name
):
super
(
DebugTestCase
,
self
).
__init__
(
name
)
super
(
DebugTestCase
,
self
).
__init__
(
name
)
self
.
cy
=
libcython
.
cy
self
.
cy
=
libcython
.
cy
...
@@ -58,7 +82,7 @@ class DebugTestCase(unittest.TestCase):
...
@@ -58,7 +82,7 @@ class DebugTestCase(unittest.TestCase):
if
source_line
is
not
None
:
if
source_line
is
not
None
:
lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
frame
=
gdb
.
selected_frame
()
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
):
def
break_and_run
(
self
,
source_line
):
break_lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
break_lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
...
@@ -74,9 +98,6 @@ class DebugTestCase(unittest.TestCase):
...
@@ -74,9 +98,6 @@ class DebugTestCase(unittest.TestCase):
gdb
.
execute
(
'set args -c "import codefile"'
)
gdb
.
execute
(
'set args -c "import codefile"'
)
libcython
.
cy
.
step
.
static_breakpoints
.
clear
()
libcython
.
cy
.
step
.
runtime_breakpoints
.
clear
()
libcython
.
cy
.
step
.
init_breakpoints
()
class
TestDebugInformationClasses
(
DebugTestCase
):
class
TestDebugInformationClasses
(
DebugTestCase
):
...
@@ -101,7 +122,7 @@ class TestDebugInformationClasses(DebugTestCase):
...
@@ -101,7 +122,7 @@ class TestDebugInformationClasses(DebugTestCase):
'codefile.SomeClass.spam'
)
'codefile.SomeClass.spam'
)
self
.
assertEqual
(
self
.
spam_func
.
module
,
self
.
module
)
self
.
assertEqual
(
self
.
spam_func
.
module
,
self
.
module
)
assert
self
.
eggs_func
.
pf_cname
assert
self
.
eggs_func
.
pf_cname
,
(
self
.
eggs_func
,
self
.
eggs_func
.
pf_cname
)
assert
not
self
.
ham_func
.
pf_cname
assert
not
self
.
ham_func
.
pf_cname
assert
not
self
.
spam_func
.
pf_cname
assert
not
self
.
spam_func
.
pf_cname
assert
not
self
.
spam_meth
.
pf_cname
assert
not
self
.
spam_meth
.
pf_cname
...
@@ -130,7 +151,7 @@ class TestParameters(unittest.TestCase):
...
@@ -130,7 +151,7 @@ class TestParameters(unittest.TestCase):
class
TestBreak
(
DebugTestCase
):
class
TestBreak
(
DebugTestCase
):
def
test_break
(
self
):
def
test_break
(
self
):
breakpoint_amount
=
len
(
gdb
.
breakpoints
())
breakpoint_amount
=
len
(
gdb
.
breakpoints
()
or
()
)
gdb
.
execute
(
'cy break codefile.spam'
)
gdb
.
execute
(
'cy break codefile.spam'
)
self
.
assertEqual
(
len
(
gdb
.
breakpoints
()),
breakpoint_amount
+
1
)
self
.
assertEqual
(
len
(
gdb
.
breakpoints
()),
breakpoint_amount
+
1
)
...
@@ -143,6 +164,16 @@ class TestBreak(DebugTestCase):
...
@@ -143,6 +164,16 @@ class TestBreak(DebugTestCase):
gdb
.
execute
(
'cy break -p join'
)
gdb
.
execute
(
'cy break -p join'
)
assert
'def join('
in
gdb
.
execute
(
'cy run'
,
to_string
=
True
)
assert
'def join('
in
gdb
.
execute
(
'cy run'
,
to_string
=
True
)
def
test_break_lineno
(
self
):
beginline
=
'import os'
nextline
=
'cdef int c_var = 12'
self
.
break_and_run
(
beginline
)
self
.
lineno_equals
(
beginline
)
step_result
=
gdb
.
execute
(
'cy step'
,
to_string
=
True
)
self
.
lineno_equals
(
nextline
)
assert
step_result
.
rstrip
().
endswith
(
nextline
)
class
TestKilled
(
DebugTestCase
):
class
TestKilled
(
DebugTestCase
):
...
@@ -151,6 +182,7 @@ class TestKilled(DebugTestCase):
...
@@ -151,6 +182,7 @@ class TestKilled(DebugTestCase):
output
=
gdb
.
execute
(
'cy run'
,
to_string
=
True
)
output
=
gdb
.
execute
(
'cy run'
,
to_string
=
True
)
assert
'abort'
in
output
.
lower
()
assert
'abort'
in
output
.
lower
()
class
DebugStepperTestCase
(
DebugTestCase
):
class
DebugStepperTestCase
(
DebugTestCase
):
def
step
(
self
,
varnames_and_values
,
source_line
=
None
,
lineno
=
None
):
def
step
(
self
,
varnames_and_values
,
source_line
=
None
,
lineno
=
None
):
...
@@ -262,7 +294,7 @@ class TestBacktrace(DebugTestCase):
...
@@ -262,7 +294,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
):
...
@@ -344,6 +376,36 @@ class TestExec(DebugTestCase):
...
@@ -344,6 +376,36 @@ class TestExec(DebugTestCase):
gdb
.
execute
(
'cy exec some_random_var = 14'
)
gdb
.
execute
(
'cy exec some_random_var = 14'
)
self
.
assertEqual
(
'14'
,
self
.
eval_command
(
'some_random_var'
))
self
.
assertEqual
(
'14'
,
self
.
eval_command
(
'some_random_var'
))
class
TestClosure
(
DebugTestCase
):
def
break_and_run_func
(
self
,
funcname
):
gdb
.
execute
(
'cy break '
+
funcname
)
gdb
.
execute
(
'cy run'
)
def
test_inner
(
self
):
self
.
break_and_run_func
(
'inner'
)
self
.
assertEqual
(
''
,
gdb
.
execute
(
'cy locals'
,
to_string
=
True
))
# Allow the Cython-generated code to initialize the scope variable
gdb
.
execute
(
'cy step'
)
self
.
assertEqual
(
str
(
self
.
read_var
(
'a'
)),
"'an object'"
)
print_result
=
gdb
.
execute
(
'cy print a'
,
to_string
=
True
).
strip
()
self
.
assertEqual
(
print_result
,
"a = 'an object'"
)
def
test_outer
(
self
):
self
.
break_and_run_func
(
'outer'
)
self
.
assertEqual
(
''
,
gdb
.
execute
(
'cy locals'
,
to_string
=
True
))
# Initialize scope with 'a' uninitialized
gdb
.
execute
(
'cy step'
)
self
.
assertEqual
(
''
,
gdb
.
execute
(
'cy locals'
,
to_string
=
True
))
# Initialize 'a' to 1
gdb
.
execute
(
'cy step'
)
print_result
=
gdb
.
execute
(
'cy print a'
,
to_string
=
True
).
strip
()
self
.
assertEqual
(
print_result
,
"a = 'an object'"
)
_do_debug
=
os
.
environ
.
get
(
'GDB_DEBUG'
)
_do_debug
=
os
.
environ
.
get
(
'GDB_DEBUG'
)
if
_do_debug
:
if
_do_debug
:
...
...
Cython/Debugger/libcython.py
View file @
c5e16a3f
...
@@ -154,9 +154,6 @@ class CythonModule(object):
...
@@ -154,9 +154,6 @@ class CythonModule(object):
self
.
lineno_c2cy
=
{}
self
.
lineno_c2cy
=
{}
self
.
functions
=
{}
self
.
functions
=
{}
def
qualified_name
(
self
,
varname
):
return
'.'
.
join
(
self
.
name
,
varname
)
class
CythonVariable
(
object
):
class
CythonVariable
(
object
):
def
__init__
(
self
,
name
,
cname
,
qualified_name
,
type
,
lineno
):
def
__init__
(
self
,
name
,
cname
,
qualified_name
,
type
,
lineno
):
...
@@ -174,7 +171,8 @@ class CythonFunction(CythonVariable):
...
@@ -174,7 +171,8 @@ class CythonFunction(CythonVariable):
pf_cname
,
pf_cname
,
qualified_name
,
qualified_name
,
lineno
,
lineno
,
type
=
CObject
):
type
=
CObject
,
is_initmodule_function
=
"False"
):
super
(
CythonFunction
,
self
).
__init__
(
name
,
super
(
CythonFunction
,
self
).
__init__
(
name
,
cname
,
cname
,
qualified_name
,
qualified_name
,
...
@@ -182,6 +180,7 @@ class CythonFunction(CythonVariable):
...
@@ -182,6 +180,7 @@ class CythonFunction(CythonVariable):
lineno
)
lineno
)
self
.
module
=
module
self
.
module
=
module
self
.
pf_cname
=
pf_cname
self
.
pf_cname
=
pf_cname
self
.
is_initmodule_function
=
is_initmodule_function
==
"True"
self
.
locals
=
{}
self
.
locals
=
{}
self
.
arguments
=
[]
self
.
arguments
=
[]
self
.
step_into_functions
=
set
()
self
.
step_into_functions
=
set
()
...
@@ -243,7 +242,8 @@ class CythonBase(object):
...
@@ -243,7 +242,8 @@ class CythonBase(object):
pyframeobject
=
libpython
.
Frame
(
frame
).
get_pyop
()
pyframeobject
=
libpython
.
Frame
(
frame
).
get_pyop
()
if
not
pyframeobject
:
if
not
pyframeobject
:
raise
gdb
.
GdbError
(
'Unable to read information on python frame'
)
raise
gdb
.
GdbError
(
'Unable to read information on python frame'
)
filename
=
pyframeobject
.
filename
()
filename
=
pyframeobject
.
filename
()
lineno
=
pyframeobject
.
current_line_num
()
lineno
=
pyframeobject
.
current_line_num
()
...
@@ -256,7 +256,7 @@ class CythonBase(object):
...
@@ -256,7 +256,7 @@ class CythonBase(object):
filename
=
None
filename
=
None
lineno
=
0
lineno
=
0
else
:
else
:
filename
=
symbol_and_line_obj
.
symtab
.
f
ilename
filename
=
symbol_and_line_obj
.
symtab
.
f
ullname
()
lineno
=
symbol_and_line_obj
.
line
lineno
=
symbol_and_line_obj
.
line
if
pygments
:
if
pygments
:
lexer
=
pygments
.
lexers
.
CLexer
(
stripall
=
False
)
lexer
=
pygments
.
lexers
.
CLexer
(
stripall
=
False
)
...
@@ -389,10 +389,20 @@ class CythonBase(object):
...
@@ -389,10 +389,20 @@ class CythonBase(object):
value
)
value
)
def
is_initialized
(
self
,
cython_func
,
local_name
):
def
is_initialized
(
self
,
cython_func
,
local_name
):
islocal
=
local_name
in
cython_func
.
locals
if
islocal
:
cyvar
=
cython_func
.
locals
[
local_name
]
if
'->'
in
cyvar
.
cname
:
# Closed over free variable
if
self
.
get_cython_lineno
()
>=
cython_func
.
lineno
+
1
:
if
cyvar
.
type
==
PythonObject
:
return
long
(
gdb
.
parse_and_eval
(
cyvar
.
cname
))
return
True
return
False
cur_lineno
=
self
.
get_cython_lineno
()
cur_lineno
=
self
.
get_cython_lineno
()
return
(
local_name
in
cython_func
.
arguments
or
return
(
local_name
in
cython_func
.
arguments
or
(
local_name
in
cython_func
.
locals
and
(
islocal
and
cur_lineno
>
cyvar
.
lineno
))
cur_lineno
>
cython_func
.
locals
[
local_name
].
lineno
))
class
SourceFileDescriptor
(
object
):
class
SourceFileDescriptor
(
object
):
def
__init__
(
self
,
filename
,
lexer
,
formatter
=
None
):
def
__init__
(
self
,
filename
,
lexer
,
formatter
=
None
):
...
@@ -706,8 +716,6 @@ class CyImport(CythonCommand):
...
@@ -706,8 +716,6 @@ class CyImport(CythonCommand):
for
c_lineno
in
c_linenos
:
for
c_lineno
in
c_linenos
:
cython_module
.
lineno_c2cy
[
c_lineno
]
=
cython_lineno
cython_module
.
lineno_c2cy
[
c_lineno
]
=
cython_lineno
self
.
cy
.
step
.
init_breakpoints
()
class
CyBreak
(
CythonCommand
):
class
CyBreak
(
CythonCommand
):
"""
"""
...
@@ -754,10 +762,16 @@ class CyBreak(CythonCommand):
...
@@ -754,10 +762,16 @@ class CyBreak(CythonCommand):
def
_break_funcname
(
self
,
funcname
):
def
_break_funcname
(
self
,
funcname
):
func
=
self
.
cy
.
functions_by_qualified_name
.
get
(
funcname
)
func
=
self
.
cy
.
functions_by_qualified_name
.
get
(
funcname
)
if
func
and
func
.
is_initmodule_function
:
func
=
None
break_funcs
=
[
func
]
break_funcs
=
[
func
]
if
not
func
:
if
not
func
:
funcs
=
self
.
cy
.
functions_by_name
.
get
(
funcname
)
funcs
=
self
.
cy
.
functions_by_name
.
get
(
funcname
)
or
[]
funcs
=
[
f
for
f
in
funcs
if
not
f
.
is_initmodule_function
]
if
not
funcs
:
if
not
funcs
:
gdb
.
execute
(
'break '
+
funcname
)
gdb
.
execute
(
'break '
+
funcname
)
return
return
...
@@ -813,18 +827,28 @@ class CyBreak(CythonCommand):
...
@@ -813,18 +827,28 @@ class CyBreak(CythonCommand):
@
dont_suppress_errors
@
dont_suppress_errors
def
complete
(
self
,
text
,
word
):
def
complete
(
self
,
text
,
word
):
names
=
self
.
cy
.
functions_by_qualified_name
# Filter init-module functions (breakpoints can be set using
# modulename:linenumber).
names
=
[
n
for
n
,
L
in
self
.
cy
.
functions_by_name
.
iteritems
()
if
any
(
not
f
.
is_initmodule_function
for
f
in
L
)]
qnames
=
[
n
for
n
,
f
in
self
.
cy
.
functions_by_qualified_name
.
iteritems
()
if
not
f
.
is_initmodule_function
]
if
parameters
.
complete_unqualified
:
if
parameters
.
complete_unqualified
:
names
=
itertools
.
chain
(
names
,
self
.
cy
.
functions_by_name
)
all_names
=
itertools
.
chain
(
qnames
,
names
)
else
:
all_names
=
qnames
words
=
text
.
strip
().
split
()
words
=
text
.
strip
().
split
()
if
words
and
'.'
in
words
[
-
1
]:
if
not
words
or
'.'
not
in
words
[
-
1
]:
lastword
=
words
[
-
1
]
# complete unqualified
compl
=
[
n
for
n
in
self
.
cy
.
functions_by_qualified_name
if
n
.
startswith
(
lastword
)]
else
:
seen
=
set
(
text
[:
-
len
(
word
)].
split
())
seen
=
set
(
text
[:
-
len
(
word
)].
split
())
return
[
n
for
n
in
names
if
n
.
startswith
(
word
)
and
n
not
in
seen
]
return
[
n
for
n
in
all_names
if
n
.
startswith
(
word
)
and
n
not
in
seen
]
# complete qualified name
lastword
=
words
[
-
1
]
compl
=
[
n
for
n
in
qnames
if
n
.
startswith
(
lastword
)]
if
len
(
lastword
)
>
len
(
word
):
if
len
(
lastword
)
>
len
(
word
):
# readline sees something (e.g. a '.') as a word boundary, so don't
# readline sees something (e.g. a '.') as a word boundary, so don't
...
@@ -835,10 +859,9 @@ class CyBreak(CythonCommand):
...
@@ -835,10 +859,9 @@ class CyBreak(CythonCommand):
return
compl
return
compl
class
Cython
CodeStepper
(
CythonCommand
,
libpython
.
GenericCodeStepper
):
class
Cython
Info
(
CythonBase
,
libpython
.
PythonInfo
):
"""
"""
Base class for CyStep and CyNext. It implements the interface dictated by
Implementation of the interface dictated by libpython.LanguageInfo.
libpython.GenericCodeStepper.
"""
"""
def
lineno
(
self
,
frame
):
def
lineno
(
self
,
frame
):
...
@@ -848,32 +871,49 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
...
@@ -848,32 +871,49 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
# related code. The C level should be dispatched to the 'step' command.
# related code. The C level should be dispatched to the 'step' command.
if
self
.
is_cython_function
(
frame
):
if
self
.
is_cython_function
(
frame
):
return
self
.
get_cython_lineno
(
frame
)
return
self
.
get_cython_lineno
(
frame
)
else
:
return
super
(
CythonInfo
,
self
).
lineno
(
frame
)
return
libpython
.
py_step
.
lineno
(
frame
)
def
get_source_line
(
self
,
frame
):
def
get_source_line
(
self
,
frame
):
try
:
try
:
line
=
super
(
Cython
CodeStepper
,
self
).
get_source_line
(
frame
)
line
=
super
(
Cython
Info
,
self
).
get_source_line
(
frame
)
except
gdb
.
GdbError
:
except
gdb
.
GdbError
:
return
None
return
None
else
:
else
:
return
line
.
strip
()
or
None
return
line
.
strip
()
or
None
@
classmethod
def
exc_info
(
self
,
frame
):
def
register
(
cls
)
:
if
self
.
is_python_function
:
return
cls
(
cls
.
name
,
stepinto
=
getattr
(
cls
,
'stepinto'
,
False
)
)
return
super
(
CythonInfo
,
self
).
exc_info
(
frame
)
def
runtime_break_functions
(
self
):
def
runtime_break_functions
(
self
):
if
self
.
is_cython_function
():
if
self
.
is_cython_function
():
return
self
.
get_cython_function
().
step_into_functions
return
self
.
get_cython_function
().
step_into_functions
return
()
def
static_break_functions
(
self
):
def
static_break_functions
(
self
):
result
=
[
'PyEval_EvalFrameEx'
]
result
=
[
'PyEval_EvalFrameEx'
]
result
.
extend
(
self
.
cy
.
functions_by_cname
)
result
.
extend
(
self
.
cy
.
functions_by_cname
)
return
result
return
result
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'
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
():
self
.
python_step
(
self
.
stepinto
)
elif
not
self
.
is_cython_function
():
if
self
.
stepinto
:
if
self
.
stepinto
:
command
=
'step'
command
=
'step'
else
:
else
:
...
@@ -881,24 +921,17 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
...
@@ -881,24 +921,17 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
self
.
finish_executing
(
gdb
.
execute
(
command
,
to_string
=
True
))
self
.
finish_executing
(
gdb
.
execute
(
command
,
to_string
=
True
))
else
:
else
:
self
.
step
()
self
.
step
(
stepinto
=
self
.
stepinto
)
class
CyStep
(
CythonCodeStepper
):
"Step through Cython, Python or C code."
name
=
'cy step'
stepinto
=
True
class
CyNext
(
Cy
thonCodeStepper
):
class
CyNext
(
Cy
Step
):
"Step-over
Python
code."
"Step-over
Cython, Python or C
code."
name
=
'cy next'
name
=
'cy
-
next'
stepinto
=
False
stepinto
=
False
class
CyRun
(
Cython
CodeStepper
):
class
CyRun
(
Cython
ExecutionControlCommand
):
"""
"""
Run a Cython program. This is like the 'run' command, except that it
Run a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well
displays Cython or Python source lines as well
...
@@ -906,26 +939,26 @@ class CyRun(CythonCodeStepper):
...
@@ -906,26 +939,26 @@ class CyRun(CythonCodeStepper):
name
=
'cy run'
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
Continue a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well.
displays Cython or Python source lines as well.
"""
"""
name
=
'cy cont'
name
=
'cy cont'
invoke
=
Cython
CodeStepper
.
cont
invoke
=
Cython
ExecutionControlCommand
.
cont
class
CyFinish
(
Cy
Run
):
class
CyFinish
(
Cy
thonExecutionControlCommand
):
"""
"""
Execute until the function returns.
Execute until the function returns.
"""
"""
name
=
'cy finish'
name
=
'cy finish'
invoke
=
Cython
CodeStepper
.
finish
invoke
=
Cython
ExecutionControlCommand
.
finish
class
CyUp
(
CythonCommand
):
class
CyUp
(
CythonCommand
):
...
@@ -961,7 +994,7 @@ class CyDown(CyUp):
...
@@ -961,7 +994,7 @@ class CyDown(CyUp):
_command
=
'down'
_command
=
'down'
class
CySelect
(
CythonCo
deStepper
):
class
CySelect
(
CythonCo
mmand
):
"""
"""
Select a frame. Use frame numbers as listed in `cy backtrace`.
Select a frame. Use frame numbers as listed in `cy backtrace`.
This command is useful because `cy backtrace` prints a reversed backtrace.
This command is useful because `cy backtrace` prints a reversed backtrace.
...
@@ -979,7 +1012,7 @@ class CySelect(CythonCodeStepper):
...
@@ -979,7 +1012,7 @@ class CySelect(CythonCodeStepper):
while
frame
.
newer
():
while
frame
.
newer
():
frame
=
frame
.
newer
()
frame
=
frame
.
newer
()
stackdepth
=
self
.
_
stackdepth
(
frame
)
stackdepth
=
libpython
.
stackdepth
(
frame
)
try
:
try
:
gdb
.
execute
(
'select %d'
%
(
stackdepth
-
stackno
-
1
,))
gdb
.
execute
(
'select %d'
%
(
stackdepth
-
stackno
-
1
,))
...
@@ -1033,7 +1066,7 @@ class CyList(CythonCommand):
...
@@ -1033,7 +1066,7 @@ class CyList(CythonCommand):
command_class
=
gdb
.
COMMAND_FILES
command_class
=
gdb
.
COMMAND_FILES
completer_class
=
gdb
.
COMPLETE_NONE
completer_class
=
gdb
.
COMPLETE_NONE
@
dispatch_on_frame
(
c_command
=
'list'
)
#
@dispatch_on_frame(c_command='list')
def
invoke
(
self
,
_
,
from_tty
):
def
invoke
(
self
,
_
,
from_tty
):
sd
,
lineno
=
self
.
get_source_desc
()
sd
,
lineno
=
self
.
get_source_desc
()
source
=
sd
.
get_source
(
lineno
-
5
,
lineno
+
5
,
mark_line
=
lineno
,
source
=
sd
.
get_source
(
lineno
-
5
,
lineno
+
5
,
mark_line
=
lineno
,
...
@@ -1085,7 +1118,13 @@ class CyLocals(CythonCommand):
...
@@ -1085,7 +1118,13 @@ class CyLocals(CythonCommand):
@
dispatch_on_frame
(
c_command
=
'info locals'
,
python_command
=
'py-locals'
)
@
dispatch_on_frame
(
c_command
=
'info locals'
,
python_command
=
'py-locals'
)
def
invoke
(
self
,
args
,
from_tty
):
def
invoke
(
self
,
args
,
from_tty
):
local_cython_vars
=
self
.
get_cython_function
().
locals
cython_function
=
self
.
get_cython_function
()
if
cython_function
.
is_initmodule_function
:
self
.
cy
.
globals
.
invoke
(
args
,
from_tty
)
return
local_cython_vars
=
cython_function
.
locals
max_name_length
=
len
(
max
(
local_cython_vars
,
key
=
len
))
max_name_length
=
len
(
max
(
local_cython_vars
,
key
=
len
))
for
name
,
cyvar
in
sorted
(
local_cython_vars
.
iteritems
(),
key
=
sortkey
):
for
name
,
cyvar
in
sorted
(
local_cython_vars
.
iteritems
(),
key
=
sortkey
):
if
self
.
is_initialized
(
self
.
get_cython_function
(),
cyvar
.
name
):
if
self
.
is_initialized
(
self
.
get_cython_function
(),
cyvar
.
name
):
...
@@ -1263,17 +1302,16 @@ class CyCValue(CyCName):
...
@@ -1263,17 +1302,16 @@ class CyCValue(CyCName):
@
require_cython_frame
@
require_cython_frame
@
gdb_function_value_to_unicode
@
gdb_function_value_to_unicode
def
invoke
(
self
,
cyname
,
frame
=
None
):
def
invoke
(
self
,
cyname
,
frame
=
None
):
try
:
globals_dict
=
self
.
get_cython_globals_dict
()
cython_function
=
self
.
get_cython_function
(
frame
)
if
self
.
is_initialized
(
cython_function
,
cyname
):
cname
=
super
(
CyCValue
,
self
).
invoke
(
cyname
,
frame
=
frame
)
cname
=
super
(
CyCValue
,
self
).
invoke
(
cyname
,
frame
=
frame
)
return
gdb
.
parse_and_eval
(
cname
)
return
gdb
.
parse_and_eval
(
cname
)
except
(
gdb
.
GdbError
,
RuntimeError
),
e
:
elif
cyname
in
globals_dict
:
# variable exists but may not have been initialized yet, or may be
return
globals_dict
[
cyname
].
_gdbval
# in the globals dict of the Cython module
else
:
d
=
self
.
get_cython_globals_dict
()
raise
gdb
.
GdbError
(
"Variable %s is not initialized."
%
cyname
)
if
cyname
in
d
:
return
d
[
cyname
].
_gdbval
raise
gdb
.
GdbError
(
str
(
e
))
class
CyLine
(
gdb
.
Function
,
CythonBase
):
class
CyLine
(
gdb
.
Function
,
CythonBase
):
...
@@ -1285,5 +1323,27 @@ class CyLine(gdb.Function, CythonBase):
...
@@ -1285,5 +1323,27 @@ class CyLine(gdb.Function, CythonBase):
def
invoke
(
self
):
def
invoke
(
self
):
return
self
.
get_cython_lineno
()
return
self
.
get_cython_lineno
()
cython_info
=
CythonInfo
()
cy
=
CyCy
.
register
()
cy
=
CyCy
.
register
()
cython_info
.
cy
=
cy
def
register_defines
():
libpython
.
source_gdb_script
(
textwrap
.
dedent
(
"""
\
define cy step
cy -step
end
define cy next
cy -next
end
document cy step
%s
end
document cy next
%s
end
"""
)
%
(
CyStep
.
__doc__
,
CyNext
.
__doc__
))
register_defines
()
\ No newline at end of file
Cython/Debugger/libpython.py
View file @
c5e16a3f
...
@@ -369,8 +369,8 @@ class PyObjectPtr(object):
...
@@ -369,8 +369,8 @@ class PyObjectPtr(object):
if
tp_name
in
name_map
:
if
tp_name
in
name_map
:
return
name_map
[
tp_name
]
return
name_map
[
tp_name
]
if
tp_flags
&
Py_TPFLAGS_HEAPTYPE
:
if
tp_flags
&
(
Py_TPFLAGS_HEAPTYPE
|
Py_TPFLAGS_TYPE_SUBCLASS
)
:
return
Heap
TypeObjectPtr
return
Py
TypeObjectPtr
if
tp_flags
&
Py_TPFLAGS_INT_SUBCLASS
:
if
tp_flags
&
Py_TPFLAGS_INT_SUBCLASS
:
return
PyIntObjectPtr
return
PyIntObjectPtr
...
@@ -392,8 +392,6 @@ class PyObjectPtr(object):
...
@@ -392,8 +392,6 @@ class PyObjectPtr(object):
return
PyDictObjectPtr
return
PyDictObjectPtr
if
tp_flags
&
Py_TPFLAGS_BASE_EXC_SUBCLASS
:
if
tp_flags
&
Py_TPFLAGS_BASE_EXC_SUBCLASS
:
return
PyBaseExceptionObjectPtr
return
PyBaseExceptionObjectPtr
#if tp_flags & Py_TPFLAGS_TYPE_SUBCLASS:
# return PyTypeObjectPtr
# Use the base class:
# Use the base class:
return
cls
return
cls
...
@@ -484,8 +482,8 @@ def _PyObject_VAR_SIZE(typeobj, nitems):
...
@@ -484,8 +482,8 @@ def _PyObject_VAR_SIZE(typeobj, nitems):
)
&
~
(
SIZEOF_VOID_P
-
1
)
)
&
~
(
SIZEOF_VOID_P
-
1
)
).
cast
(
gdb
.
lookup_type
(
'size_t'
))
).
cast
(
gdb
.
lookup_type
(
'size_t'
))
class
Heap
TypeObjectPtr
(
PyObjectPtr
):
class
Py
TypeObjectPtr
(
PyObjectPtr
):
_typename
=
'PyObject'
_typename
=
'Py
Type
Object'
def
get_attr_dict
(
self
):
def
get_attr_dict
(
self
):
'''
'''
...
@@ -546,9 +544,16 @@ class HeapTypeObjectPtr(PyObjectPtr):
...
@@ -546,9 +544,16 @@ class HeapTypeObjectPtr(PyObjectPtr):
return
return
visited
.
add
(
self
.
as_address
())
visited
.
add
(
self
.
as_address
())
pyop_attrdict
=
self
.
get_attr_dict
()
try
:
_write_instance_repr
(
out
,
visited
,
tp_name
=
self
.
field
(
'tp_name'
).
string
()
self
.
safe_tp_name
(),
pyop_attrdict
,
self
.
as_address
())
except
RuntimeError
:
tp_name
=
'unknown'
out
.
write
(
'<type %s at remote 0x%x>'
%
(
tp_name
,
self
.
as_address
()))
# pyop_attrdict = self.get_attr_dict()
# _write_instance_repr(out, visited,
# self.safe_tp_name(), pyop_attrdict, self.as_address())
class
ProxyException
(
Exception
):
class
ProxyException
(
Exception
):
def
__init__
(
self
,
tp_name
,
args
):
def
__init__
(
self
,
tp_name
,
args
):
...
@@ -1136,9 +1141,6 @@ class PyTupleObjectPtr(PyObjectPtr):
...
@@ -1136,9 +1141,6 @@ class PyTupleObjectPtr(PyObjectPtr):
else
:
else
:
out
.
write
(
')'
)
out
.
write
(
')'
)
class
PyTypeObjectPtr
(
PyObjectPtr
):
_typename
=
'PyTypeObject'
def
_unichr_is_printable
(
char
):
def
_unichr_is_printable
(
char
):
# Logic adapted from Python 3's Tools/unicode/makeunicodedata.py
# Logic adapted from Python 3's Tools/unicode/makeunicodedata.py
...
@@ -1715,6 +1717,8 @@ class PyNameEquals(gdb.Function):
...
@@ -1715,6 +1717,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())
...
@@ -1839,132 +1843,98 @@ def get_selected_inferior():
...
@@ -1839,132 +1843,98 @@ def get_selected_inferior():
if thread == selected_thread:
if thread == selected_thread:
return inferior
return inferior
def source_gdb_script(script_contents, to_string=False):
class GenericCodeStepper(gdb.Command):
"""
"""
Superclass for code stepping. Subclasses must implement the following
Source a gdb script with script_contents passed as a string. This is useful
methods:
to provide defines for py-step and py-next to make them repeatable (this is
not possible with gdb.execute()). See
lineno(frame)
http://sourceware.org/bugzilla/show_bug.cgi?id=12216
tells the current line number (only called for a relevant frame)
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
This class provides an '
invoke
' method that invokes a '
step
' or '
step
-
over
'
depending on the '
stepinto
' argument.
"""
"""
fd, filename = tempfile.mkstemp()
f = os.fdopen(fd, 'w')
f.write(script_contents)
f.close()
gdb.execute("
source
%
s
" % filename, to_string=to_string)
os.remove(filename)
def register_defines():
source_gdb_script(textwrap.dedent("""
\
define py-step
-py-step
end
define py-next
-py-next
end
document py-step
%s
end
document py-next
%s
end
""") % (PyStep.__doc__, PyNext.__doc__))
def stackdepth(frame):
"
Tells
the
stackdepth
of
a
gdb
frame
.
"
depth = 0
while frame:
frame = frame.older()
depth += 1
stepper = False
return depth
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 _break_func(self, funcname):
result = gdb.execute('
break
%
s
' % funcname, to_string=True)
return re.search(r'
Breakpoint
(
\
d
+
)
', result).group(1)
def init_breakpoints(self):
"""
Keep all breakpoints around and simply disable/enable them each time
we are stepping. We need this because if you set and delete a
breakpoint, gdb will not repeat your command (this is due to '
delete
').
We also can'
t
use
the
breakpoint
API
because
there
's no option to make
breakpoint setting silent.
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())
for funcname in break_funcs:
if funcname not in self.static_breakpoints:
try:
gdb.Breakpoint('', gdb.BP_BREAKPOINT, internal=True)
except (AttributeError, TypeError):
# gdb.Breakpoint does not take an '
internal
' argument, or
# gdb.Breakpoint does not exist.
breakpoint = self._break_func(funcname)
except RuntimeError:
# gdb.Breakpoint does take an '
internal
' argument, use it
# and hide output
result = gdb.execute(textwrap.dedent("""
\
python bp = gdb.Breakpoint(%r, gdb.BP_BREAKPOINT,
\
internal=True);
\
print bp.number""",
to_string=True))
breakpoint = int(result)
self.static_breakpoints[funcname] = breakpoint
for bp in set(self.static_breakpoints) - break_funcs:
gdb.execute("delete " + self.static_breakpoints[bp])
self.disable_breakpoints()
def enable_breakpoints(self):
for bp in self.static_breakpoints.itervalues():
gdb.execute('
enable
' + bp)
runtime_break_functions = self.runtime_break_functions()
if runtime_break_functions is None:
return
for funcname in runtime_break_functions:
if (funcname not in self.static_breakpoints and
funcname not in self.runtime_breakpoints):
self.runtime_breakpoints[funcname] = self._break_func(funcname)
elif funcname in self.runtime_breakpoints:
gdb.execute('
enable
' + self.runtime_breakpoints[funcname])
def disable_breakpoints(self):
chain = itertools.chain(self.static_breakpoints.itervalues(),
self.runtime_breakpoints.itervalues())
for bp in chain:
gdb.execute('
disable
' + bp)
def runtime_break_functions(self
):
class ExecutionControlCommandBase(gdb.Command
):
"""
"""
Implement this if the list of step-into functions depends on the
Superclass for language specific execution control. Language specific
context.
features should be implemented by lang_info using the LanguageInfo
interface. 'name' is the name of the command.
"""
"""
def stopped(self, result):
def __init__(self, name, lang_info):
match = re.search('
^
Program
received
signal
.
*
', result, re.MULTILINE)
super(ExecutionControlCommandBase, self).__init__(
name, gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
self.lang_info = lang_info
def install_breakpoints(self):
all_locations = itertools.chain(
self.lang_info.static_break_functions(),
self.lang_info.runtime_break_functions())
for location in all_locations:
result = gdb.execute('break %s' % location, to_string=True)
yield re.search(r'Breakpoint (
\
d+)
'
, result).group(1)
def delete_breakpoints(self, breakpoint_list):
for bp in breakpoint_list:
gdb.execute("
delete
%
s
" % bp)
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:
if match:
return match.group(0)
output.append(match.group(0))
elif get_selected_inferior().pid == 0:
return result
else:
return None
def _stackdepth(self, frame):
return '
\
n
'.join(output)
depth = 0
while frame:
frame = frame.older()
depth += 1
return depth
def stopped(self):
return get_selected_inferior().pid == 0
def finish_executing(self, result):
def finish_executing(self, result):
"""
"""
...
@@ -1972,32 +1942,19 @@ class GenericCodeStepper(gdb.Command):
...
@@ -1972,32 +1942,19 @@ class GenericCodeStepper(gdb.Command):
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(result)
result = self.filter_output(result)
if result:
if self.stopped():
print result.strip()
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:
print self.get_source_line(frame)
else:
else:
frame = gdb.selected_frame()
frame = gdb.selected_frame()
output = None
if self.lang_info.is_relevant_function(frame):
raised_exception = self.lang_info.exc_info(frame)
if self.is_relevant_function(frame):
if raised_exception:
output = self.get_source_line(frame)
print raised_exception
print self.lang_info.get_source_line(frame) or result
if output is None:
pframe = getattr(self, '
print_stackframe
', None)
if pframe:
pframe(frame, index=0)
else:
print result.strip()
else:
else:
print
outpu
t
print
resul
t
def _finish(self):
def _finish(self):
"""
"""
...
@@ -2010,11 +1967,10 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2010,11 +1967,10 @@ class GenericCodeStepper(gdb.Command):
# outermost frame, continue
# outermost frame, continue
return gdb.execute('cont', to_string=True)
return gdb.execute('cont', to_string=True)
def
finish(self, *args
):
def
_finish_frame(self
):
"""
"""
Execute until the function returns to a relevant caller.
Execute until the function returns to a relevant caller.
"""
"""
while True:
while True:
result = self._finish()
result = self._finish()
...
@@ -2024,39 +1980,64 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2024,39 +1980,64 @@ class GenericCodeStepper(gdb.Command):
break
break
hitbp = re.search(r'Breakpoint (
\
d+)
'
, result)
hitbp = re.search(r'Breakpoint (
\
d+)
'
, result)
is_rel
avant = self
.is_relevant_function(frame)
is_rel
evant = self.lang_info
.is_relevant_function(frame)
if hitbp or is_rel
avant or self.stopped(result
):
if hitbp or is_rel
evant or self.stopped(
):
break
break
return result
def finish(self, *args):
"
Implements
the
finish
command
.
"
result = self._finish_frame()
self.finish_executing(result)
self.finish_executing(result)
def
_step(self
):
def
step(self, stepinto, 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 s
elf.s
tepinto:
if stepinto:
self.enable_breakpoints(
)
breakpoint_list = list(self.install_breakpoints()
)
beginframe = gdb.selected_frame()
beginframe = gdb.selected_frame()
beginline = self.lineno(beginframe)
if not self.stepinto:
if self.lang_info.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.lang_info.lineno() as it may
# raise for irrelevant frames.
beginline = self.lang_info.lineno(beginframe)
if not stepinto:
depth = stackdepth(beginframe)
newframe = beginframe
newframe = beginframe
result = ''
while True:
while True:
if self.is_relevant_function(newframe):
if self.
lang_info.
is_relevant_function(newframe):
result = gdb.execute(
'
next
'
, to_string=True)
result = gdb.execute(
stepover_command
, to_string=True)
else:
else:
result = self._finish()
result = self._finish
_frame
()
if self.stopped(
result
):
if self.stopped():
break
break
newframe = gdb.selected_frame()
newframe = gdb.selected_frame()
is_relevant_function = self.is_relevant_function(newframe)
is_relevant_function = self.
lang_info.
is_relevant_function(newframe)
try:
try:
framename = newframe.name()
framename = newframe.name()
except RuntimeError:
except RuntimeError:
...
@@ -2064,8 +2045,7 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2064,8 +2045,7 @@ 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)
if is_relevant_function and m.group(1) in breakpoint_list:
if bp is None or (m.group(1) == bp and is_relevant_function):
# 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
...
@@ -2074,25 +2054,25 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2074,25 +2054,25 @@ class GenericCodeStepper(gdb.Command):
if newframe != beginframe:
if newframe != beginframe:
# new function
# new function
if not s
elf.s
tepinto:
if not stepinto:
# see if we returned to the caller
# 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 = (newdepth < depth and
is_relevant_function)
is_relevant_function)
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.lang_info.lineno(newframe)
if lineno and lineno != beginline:
break
break
if self.stepinto:
if stepinto:
self.disable_breakpoints()
self.delete_breakpoints(breakpoint_list)
return result
def step(self, *args):
self.finish_executing(result)
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))
...
@@ -2101,14 +2081,57 @@ class GenericCodeStepper(gdb.Command):
...
@@ -2101,14 +2081,57 @@ class GenericCodeStepper(gdb.Command):
self.finish_executing(gdb.execute('cont', to_string=True))
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.
"""
return ()
class PythonInfo(LanguageInfo):
def pyframe(self, frame):
def pyframe(self, frame):
pyframe = Frame(frame).get_pyop()
pyframe = Frame(frame).get_pyop()
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).")
...
@@ -2126,47 +2149,67 @@ class PythonCodeStepper(GenericCodeStepper):
...
@@ -2126,47 +2149,67 @@ class PythonCodeStepper(GenericCodeStepper):
except IOError, e:
except IOError, e:
return None
return None
def exc_info(self, frame):
try:
tstate = frame.read_var('
tstate
').dereference()
if gdb.parse_and_eval('
tstate
->
frame
==
f'):
# tstate local variable initialized
inf_type = tstate['
curexc_type
']
inf_value = tstate['
curexc_value
']
if inf_type:
return '
An
exception
was
raised
:
%
s
(
%
s
)
' % (inf_type,
inf_value)
except (ValueError, RuntimeError), e:
# Could not read the variable tstate or it'
s
memory
,
it
's ok
pass
def static_break_functions(self):
def static_break_functions(self):
yield '
PyEval_EvalFrameEx
'
yield '
PyEval_EvalFrameEx
'
class PyStep(PythonCodeStepper):
class PythonStepperMixin(object):
"""
Make this a mixin so CyStep can also inherit from this and use a
CythonCodeStepper at the same time.
"""
def python_step(self, stepinto):
frame = gdb.selected_frame()
framewrapper = Frame(frame)
output = gdb.execute('
watch
f
->
f_lasti
', to_string=True)
watchpoint = int(re.search(r'
[
Ww
]
atchpoint
(
\
d
+
):
', output).group(1))
self.step(stepinto=stepinto, stepover_command='
finish
')
gdb.execute('
delete
%
s
' % watchpoint)
class PyStep(ExecutionControlCommandBase, PythonStepperMixin):
"Step through Python code."
"Step through Python code."
invoke = PythonCodeStepper.step
stepinto = True
def invoke(self, args, from_tty):
self.python_step(stepinto=self.stepinto)
class PyNext(Py
thonCodeStepper
):
class PyNext(Py
Step
):
"Step-over Python code."
"Step-over Python code."
invoke = PythonCodeStepper.step
stepinto = False
class PyFinish(
PythonCodeStepper
):
class PyFinish(
ExecutionControlCommandBase
):
"Execute until function returns to a caller."
"Execute until function returns to a caller."
invoke =
PythonCodeStepper
.finish
invoke =
ExecutionControlCommandBase
.finish
class PyRun(
PythonCodeStepper
):
class PyRun(
ExecutionControlCommandBase
):
"Run the program."
"Run the program."
invoke = PythonCodeStepper.run
invoke = ExecutionControlCommandBase.run
class PyCont(PythonCodeStepper):
invoke = PythonCodeStepper.cont
class PyCont(ExecutionControlCommandBase):
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
')
gdb.execute('
set
breakpoint
pending
on
')
py_step.init_breakpoints()
Py_single_input = 256
Py_file_input = 257
Py_eval_input = 258
def _pointervalue(gdbval):
def _pointervalue(gdbval):
"""
"""
...
@@ -2210,6 +2253,10 @@ def get_inferior_unicode_postfix():
...
@@ -2210,6 +2253,10 @@ def get_inferior_unicode_postfix():
class
PythonCodeExecutor
(
object
):
class
PythonCodeExecutor
(
object
):
Py_single_input
=
256
Py_file_input
=
257
Py_eval_input
=
258
def
malloc
(
self
,
size
):
def
malloc
(
self
,
size
):
chunk
=
(
gdb
.
parse_and_eval
(
"(void *) malloc((size_t) %d)"
%
size
))
chunk
=
(
gdb
.
parse_and_eval
(
"(void *) malloc((size_t) %d)"
%
size
))
...
@@ -2382,7 +2429,7 @@ class PyExec(gdb.Command):
...
@@ -2382,7 +2429,7 @@ class PyExec(gdb.Command):
def
readcode
(
self
,
expr
):
def
readcode
(
self
,
expr
):
if
expr
:
if
expr
:
return
expr
,
Py_single_input
return
expr
,
Py
thonCodeExecutor
.
Py
_single_input
else
:
else
:
lines
=
[]
lines
=
[]
while
True
:
while
True
:
...
@@ -2412,5 +2459,19 @@ class PyExec(gdb.Command):
...
@@ -2412,5 +2459,19 @@ class PyExec(gdb.Command):
executor
.
evalcode
(
expr
,
input_type
,
global_dict
,
local_dict
)
executor
.
evalcode
(
expr
,
input_type
,
global_dict
,
local_dict
)
py_exec
=
FixGdbCommand
(
'py-exec'
,
'-py-exec'
)
_py_exec
=
PyExec
(
"-py-exec"
,
gdb
.
COMMAND_DATA
,
gdb
.
COMPLETE_NONE
)
gdb
.
execute
(
'set breakpoint pending on'
)
if
hasattr
(
gdb
,
'GdbError'
):
# Wrap py-step and py-next in gdb defines to make them repeatable.
py_step
=
PyStep
(
'-py-step'
,
PythonInfo
())
py_next
=
PyNext
(
'-py-next'
,
PythonInfo
())
register_defines
()
py_finish
=
PyFinish
(
'py-finish'
,
PythonInfo
())
py_run
=
PyRun
(
'py-run'
,
PythonInfo
())
py_cont
=
PyCont
(
'py-cont'
,
PythonInfo
())
py_exec
=
FixGdbCommand
(
'py-exec'
,
'-py-exec'
)
_py_exec
=
PyExec
(
"-py-exec"
,
gdb
.
COMMAND_DATA
,
gdb
.
COMPLETE_NONE
)
else
:
warnings
.
warn
(
"Use gdb 7.2 or higher to use the py-exec command."
)
\ No newline at end of file
runtests.py
View file @
c5e16a3f
...
@@ -675,12 +675,12 @@ class CythonPyregrTestCase(CythonRunTestCase):
...
@@ -675,12 +675,12 @@ class CythonPyregrTestCase(CythonRunTestCase):
except
(
unittest
.
SkipTest
,
support
.
ResourceDenied
):
except
(
unittest
.
SkipTest
,
support
.
ResourceDenied
):
result
.
addSkip
(
self
,
'ok'
)
result
.
addSkip
(
self
,
'ok'
)
# Someone wrapped this in a:
try
:
# 'try: import gdb; ... except: include_debugger = False' thing, but don't do
import
gdb
# this, it doesn't work as gdb is a builtin module in GDB. The tests themselves
include_debugger
=
sys
.
version_info
[:
2
]
>
(
2
,
5
)
# are doing the skipping. If there's a problem with the tests, please file an
except
:
# issue.
include_debugger
=
False
include_debugger
=
sys
.
version_info
[:
2
]
>
(
2
,
5
)
def
collect_unittests
(
path
,
module_prefix
,
suite
,
selectors
):
def
collect_unittests
(
path
,
module_prefix
,
suite
,
selectors
):
def
file_matches
(
filename
):
def
file_matches
(
filename
):
...
...
setup.py
View file @
c5e16a3f
...
@@ -100,7 +100,8 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan
...
@@ -100,7 +100,8 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan
"Cython.Compiler.Parsing"
,
"Cython.Compiler.Parsing"
,
"Cython.Compiler.Visitor"
,
"Cython.Compiler.Visitor"
,
"Cython.Compiler.Code"
,
"Cython.Compiler.Code"
,
"Cython.Runtime.refnanny"
]
"Cython.Runtime.refnanny"
,
"Cython.Debugger.do_repeat"
,]
if
compile_more
:
if
compile_more
:
compiled_modules
.
extend
([
compiled_modules
.
extend
([
"Cython.Compiler.ParseTreeTransforms"
,
"Cython.Compiler.ParseTreeTransforms"
,
...
...
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