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
ef747e9a
Commit
ef747e9a
authored
Nov 01, 2010
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reentrant gdb.execute()
cy locals, cy globals
parent
d07e7567
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
162 additions
and
103 deletions
+162
-103
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+7
-3
Cython/Debugger/Tests/codefile
Cython/Debugger/Tests/codefile
+2
-2
Cython/Debugger/Tests/test_libcython_in_gdb.py
Cython/Debugger/Tests/test_libcython_in_gdb.py
+41
-17
Cython/Debugger/libcython.py
Cython/Debugger/libcython.py
+50
-50
Cython/Debugger/libpython.py
Cython/Debugger/libpython.py
+62
-31
No files found.
Cython/Compiler/ParseTreeTransforms.py
View file @
ef747e9a
...
...
@@ -7,6 +7,7 @@ from Cython.Compiler.UtilNodes import *
from
Cython.Compiler.TreeFragment
import
TreeFragment
,
TemplateTransform
from
Cython.Compiler.StringEncoding
import
EncodedString
from
Cython.Compiler.Errors
import
error
,
CompileError
from
Cython.Compiler
import
PyrexTypes
try
:
set
...
...
@@ -1469,11 +1470,14 @@ class DebugTransform(CythonTransform):
# 2.3 compatibility. Serialize global variables
self
.
tb
.
start
(
'Globals'
)
entries
=
{}
for
k
,
v
in
node
.
scope
.
entries
.
iteritems
():
if
(
v
.
qualified_name
not
in
self
.
visited
and
not
v
.
name
.
startswith
(
'__pyx_'
)):
if
(
v
.
qualified_name
not
in
self
.
visited
and
not
v
.
name
.
startswith
(
'__pyx_'
)
and
not
v
.
type
.
is_cfunction
and
not
v
.
type
.
is_extension_type
):
entries
[
k
]
=
v
self
.
serialize_local_variables
(
entries
)
self
.
tb
.
end
(
'Globals'
)
# self.tb.end('Module') # end Module after the line number mapping in
...
...
Cython/Debugger/Tests/codefile
View file @
ef747e9a
...
...
@@ -6,8 +6,8 @@ cdef extern:
import os
cdef int c_var =
0
python_var =
0
cdef int c_var =
12
python_var =
13
def spam(a=0):
cdef:
...
...
Cython/Debugger/Tests/test_libcython_in_gdb.py
View file @
ef747e9a
...
...
@@ -8,6 +8,7 @@ Cython.Debugger.Cygdb.make_command_file()
import
os
import
sys
import
trace
import
inspect
import
warnings
import
unittest
import
traceback
...
...
@@ -19,11 +20,16 @@ from Cython.Debugger import libcython
from
Cython.Debugger
import
libpython
from
Cython.Debugger.Tests
import
TestLibCython
as
test_libcython
# for some reason sys.argv is missing in gdb
sys
.
argv
=
[
'gdb'
]
class
DebugTestCase
(
unittest
.
TestCase
):
"""
Base class for test cases. On teardown it kills the inferior and unsets
all breakpoints.
"""
def
__init__
(
self
,
name
):
super
(
DebugTestCase
,
self
).
__init__
(
name
)
...
...
@@ -50,25 +56,26 @@ class DebugTestCase(unittest.TestCase):
lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
frame
=
gdb
.
selected_frame
()
self
.
assertEqual
(
libcython
.
cy
.
step
.
lineno
(
frame
),
lineno
)
def
break_and_run
(
self
,
source_line
):
break_lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
gdb
.
execute
(
'cy break codefile:%d'
%
break_lineno
,
to_string
=
True
)
gdb
.
execute
(
'run'
,
to_string
=
True
)
def
tearDown
(
self
):
gdb
.
execute
(
'delete breakpoints'
,
to_string
=
True
)
try
:
gdb
.
execute
(
'kill inferior 1'
,
to_string
=
True
)
except
RuntimeError
:
pass
def
break_and_run
(
self
,
source_line
):
break_lineno
=
test_libcython
.
source_to_lineno
[
source_line
]
gdb
.
execute
(
'cy break codefile:%d'
%
break_lineno
,
to_string
=
True
)
gdb
.
execute
(
'run'
,
to_string
=
True
)
class
TestDebugInformationClasses
(
DebugTestCase
):
def
test_CythonModule
(
self
):
"test that debug information was parsed properly into data structures"
self
.
assertEqual
(
self
.
module
.
name
,
'codefile'
)
global_vars
=
(
'c_var'
,
'python_var'
,
'
SomeClass'
,
'
__name__'
,
global_vars
=
(
'c_var'
,
'python_var'
,
'__name__'
,
'__builtins__'
,
'__doc__'
,
'__file__'
)
assert
set
(
global_vars
).
issubset
(
self
.
module
.
globals
)
...
...
@@ -115,7 +122,6 @@ class TestBreak(DebugTestCase):
def
test_break
(
self
):
result
=
libpython
.
_execute
(
'cy break codefile.spam'
,
to_string
=
True
)
print
>>
sys
.
stderr
,
repr
(
result
)
assert
self
.
spam_func
.
cname
in
result
self
.
assertEqual
(
len
(
gdb
.
breakpoints
()),
1
)
...
...
@@ -140,6 +146,7 @@ class TestStep(DebugStepperTestCase):
Test stepping. Stepping happens in the code found in
Cython/Debugger/Tests/codefile.
"""
def
test_cython_step
(
self
):
gdb
.
execute
(
'cy break codefile.spam'
)
libcython
.
parameters
.
step_into_c_code
.
value
=
False
...
...
@@ -197,8 +204,28 @@ class TestNext(DebugStepperTestCase):
self
.
lineno_equals
(
line
)
class
TestLocalsGlobals
(
DebugTestCase
):
def
test_locals
(
self
):
self
.
break_and_run
(
'int(10)'
)
result
=
gdb
.
execute
(
'cy locals'
,
to_string
=
True
)
assert
'a = 0'
in
result
,
repr
(
result
)
assert
'b = 1'
in
result
,
repr
(
result
)
assert
'c = 2'
in
result
,
repr
(
result
)
def
test_globals
(
self
):
self
.
break_and_run
(
'int(10)'
)
result
=
gdb
.
execute
(
'cy globals'
,
to_string
=
True
)
assert
'__name__ ='
in
result
,
repr
(
result
)
assert
'__doc__ ='
in
result
,
repr
(
result
)
assert
'os ='
in
result
,
repr
(
result
)
assert
'c_var = 12'
in
result
,
repr
(
result
)
assert
'python_var = 13'
in
result
,
repr
(
result
)
def
_main
():
# unittest.main(module=__import__(__name__, fromlist=['']))
try
:
gdb
.
lookup_type
(
'PyModuleObject'
)
except
RuntimeError
:
...
...
@@ -207,17 +234,14 @@ def _main():
"-g or get a debug build (configure with --with-pydebug)."
)
warnings
.
warn
(
msg
)
else
:
tests
=
(
TestDebugInformationClasses
,
TestParameters
,
TestBreak
,
TestStep
,
TestNext
,
)
m
=
__import__
(
__name__
,
fromlist
=
[
''
])
tests
=
inspect
.
getmembers
(
m
,
inspect
.
isclass
)
# test_support.run_unittest(tests)
test_loader
=
unittest
.
TestLoader
()
suite
=
unittest
.
TestSuite
(
[
test_loader
.
loadTestsFromTestCase
(
cls
)
for
cls
in
tests
])
[
test_loader
.
loadTestsFromTestCase
(
cls
)
for
name
,
cls
in
tests
])
result
=
unittest
.
TextTestRunner
(
verbosity
=
1
).
run
(
suite
)
if
not
result
.
wasSuccessful
():
...
...
Cython/Debugger/libcython.py
View file @
ef747e9a
...
...
@@ -229,6 +229,7 @@ class CythonBase(object):
source_desc
,
lineno
=
self
.
get_source_desc
()
return
source_desc
.
get_source
(
lineno
)
@
default_selected_gdb_frame
()
def
is_relevant_function
(
self
,
frame
):
"""
returns whether we care about a frame on the user-level when debugging
...
...
@@ -236,7 +237,7 @@ class CythonBase(object):
"""
name
=
frame
.
name
()
older_frame
=
frame
.
older
()
# print 'is_relevant_function', name
if
self
.
is_cython_function
(
frame
)
or
self
.
is_python_function
(
frame
):
return
True
elif
(
parameters
.
step_into_c_code
and
...
...
@@ -246,7 +247,13 @@ class CythonBase(object):
return
name
in
cython_func
.
step_into_functions
return
False
def
print_cython_var_if_initialized
(
self
,
varname
):
try
:
self
.
cy
.
print_
.
invoke
(
varname
,
True
)
except
gdb
.
GdbError
:
# variable not initialized yet
pass
class
SourceFileDescriptor
(
object
):
def
__init__
(
self
,
filename
,
lexer
,
formatter
=
None
):
...
...
@@ -456,7 +463,9 @@ class CyCy(CythonCommand):
for
command_name
,
command
in
commands
.
iteritems
():
command
.
cy
=
self
setattr
(
self
,
command_name
,
command
)
self
.
cy
=
self
# Cython module namespace
self
.
cython_namespace
=
{}
...
...
@@ -507,6 +516,7 @@ class CyImport(CythonCommand):
# update the global function mappings
name
=
cython_function
.
name
qname
=
cython_function
.
qualified_name
self
.
cy
.
functions_by_name
[
name
].
append
(
cython_function
)
self
.
cy
.
functions_by_qualified_name
[
...
...
@@ -514,9 +524,7 @@ class CyImport(CythonCommand):
self
.
cy
.
functions_by_cname
[
cython_function
.
cname
]
=
cython_function
d
=
cython_module
.
functions
L
=
d
.
setdefault
(
cython_function
.
qualified_name
,
[])
L
.
append
(
cython_function
)
d
=
cython_module
.
functions
[
qname
]
=
cython_function
for
local
in
function
.
find
(
'Locals'
):
d
=
local
.
attrib
...
...
@@ -658,17 +666,17 @@ class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
def
get_source_line
(
self
,
frame
):
# We may have ended up in a Python, Cython, or C function
# In case of C, don't display any additional data (gdb already
# does this)
result
=
''
result
=
None
if
self
.
is_cython_function
(
frame
)
or
self
.
is_python_function
(
frame
):
try
:
result
=
super
(
CythonCodeStepper
,
self
).
get_source_line
(
frame
)
line
=
super
(
CythonCodeStepper
,
self
).
get_source_line
(
frame
)
except
gdb
.
GdbError
:
result
=
''
return
result
.
lstrip
()
pass
else
:
result
=
line
.
lstrip
()
return
result
@
classmethod
def
register
(
cls
):
...
...
@@ -724,8 +732,12 @@ class CyPrint(CythonCommand):
@
dispatch_on_frame
(
c_command
=
'print'
,
python_command
=
'py-print'
)
def
invoke
(
self
,
name
,
from_tty
):
gdb
.
execute
(
'print '
+
self
.
cy
.
cy_cname
.
invoke
(
name
,
string
=
True
))
cname
=
self
.
cy
.
cy_cname
.
invoke
(
name
,
string
=
True
)
try
:
print
'%s = %s'
%
(
name
,
gdb
.
parse_and_eval
(
cname
))
except
RuntimeError
,
e
:
raise
gdb
.
GdbError
(
"Variable %s is not initialized yet."
%
(
name
,))
def
complete
(
self
):
if
self
.
is_cython_function
():
f
=
self
.
get_cython_function
()
...
...
@@ -743,32 +755,11 @@ class CyLocals(CythonCommand):
command_class
=
gdb
.
COMMAND_STACK
completer_class
=
gdb
.
COMPLETE_NONE
def
ns
(
self
):
return
self
.
get_cython_function
().
locals
@
dispatch_on_frame
(
c_command
=
'info locals'
,
python_command
=
'py-locals'
)
def
invoke
(
self
,
name
,
from_tty
):
try
:
ns
=
self
.
ns
()
except
RuntimeError
,
e
:
print
e
.
args
[
0
]
return
if
ns
is
None
:
raise
gdb
.
GdbError
(
'Information of Cython locals could not be obtained. '
'Is this an actual Cython function and did you '
"'cy import' the debug information?"
)
for
var
in
ns
.
itervalues
():
val
=
gdb
.
parse_and_eval
(
var
.
cname
)
if
var
.
type
==
PythonObject
:
result
=
libpython
.
PyObjectPtr
.
from_pyobject_ptr
(
val
)
else
:
result
=
val
def
invoke
(
self
,
args
,
from_tty
):
for
varname
in
self
.
get_cython_function
().
locals
:
self
.
print_cython_var_if_initialized
(
varname
)
print
'%s = %s'
%
(
var
.
name
,
result
)
class
CyGlobals
(
CythonCommand
):
"""
...
...
@@ -779,12 +770,8 @@ class CyGlobals(CythonCommand):
command_class
=
gdb
.
COMMAND_STACK
completer_class
=
gdb
.
COMPLETE_NONE
def
ns
(
self
):
return
self
.
get_cython_function
().
globals
@
dispatch_on_frame
(
c_command
=
'info variables'
,
python_command
=
'py-globals'
)
def
invoke
(
self
,
name
,
from_tty
):
# include globals from the debug info XML file!
def
invoke
(
self
,
args
,
from_tty
):
m
=
gdb
.
parse_and_eval
(
'__pyx_m'
)
try
:
...
...
@@ -792,16 +779,29 @@ class CyGlobals(CythonCommand):
except
RuntimeError
:
raise
gdb
.
GdbError
(
textwrap
.
dedent
(
"""
Unable to lookup type PyModuleObject, did you compile python
with debugging support (-g)? If this installation is from your
package manager, install python-dbg and run the debug version
of python or compile it yourself.
with debugging support (-g)?
"""
))
m
=
m
.
cast
(
PyModuleObject
.
pointer
())
d
=
libpython
.
PyObjectPtr
.
from_pyobject_ptr
(
m
[
'md_dict'
])
print
d
.
get_truncated_repr
(
1000
)
seen
=
set
()
for
k
,
v
in
d
.
iteritems
():
# Note: k and v are values in the inferior, they are
# libpython.PyObjectPtr objects
k
=
k
.
get_truncated_repr
(
libpython
.
MAX_OUTPUT_LEN
)
# make it look like an actual name (inversion of repr())
k
=
k
[
1
:
-
1
].
decode
(
'string-escape'
)
v
=
v
.
get_truncated_repr
(
libpython
.
MAX_OUTPUT_LEN
)
seen
.
add
(
k
)
print
'%s = %s'
%
(
k
,
v
)
module_globals
=
self
.
get_cython_function
().
module
.
globals
for
varname
in
seen
.
symmetric_difference
(
module_globals
):
self
.
print_cython_var_if_initialized
(
varname
)
# Functions
class
CyCName
(
gdb
.
Function
,
CythonBase
):
...
...
Cython/Debugger/libpython.py
View file @
ef747e9a
...
...
@@ -47,6 +47,7 @@ from __future__ import with_statement
import
os
import
sys
import
atexit
import
tempfile
import
gdb
...
...
@@ -1454,34 +1455,62 @@ class PyLocals(gdb.Command):
PyLocals()
def execute(command, from_tty=False, to_string=False
):
class _LoggingState(object
):
"""
Replace gdb.execute() with this function and have it accept a '
to_string
'
argument (new in 7.2). Have it properly capture stderr also.
Unfortuntaly, this function is not reentrant.
State that helps to provide a reentrant gdb.execute() function.
"""
if not to_string:
return _execute(command, from_tty)
fd, filename = tempfile.mkstemp()
try
:
_execute("set logging file %s" % filename
)
_execute("set logging redirect on"
)
_execute("set logging
on"
)
_execute("set pagination off")
def __init__(self)
:
self.fd, self.filename = tempfile.mkstemp(
)
self.file = os.fdopen(self.fd, '
r
+
'
)
_execute("set logging
file %s" % self.filename
)
self.file_position_stack = []
_execute(command, from_tty)
finally:
data = os.fdopen(fd).read()
os.remove(filename)
_execute("set logging off")
_execute("set pagination on")
return data
atexit.register(os.close, self.fd)
atexit.register(os.remove, self.filename)
def __enter__(self):
if not self.file_position_stack:
_execute("set logging redirect on")
_execute("set logging on")
_execute("set pagination off")
self.file_position_stack.append(os.fstat(self.fd).st_size)
return self
def getoutput(self):
gdb.flush()
self.file.seek(self.file_position_stack[-1])
result = self.file.read()
return result
def __exit__(self, exc_type, exc_val, tb):
startpos = self.file_position_stack.pop()
self.file.seek(startpos)
self.file.truncate()
if not self.file_position_stack:
_execute("set logging off")
_execute("set logging redirect off")
_execute("set pagination on")
def execute(command, from_tty=True, to_string=False):
"""
Replace gdb.execute() with this function and have it accept a '
to_string
'
argument (new in 7.2). Have it properly capture stderr also. Ensure
reentrancy.
"""
if to_string:
with _logging_state as state:
_execute(command, from_tty)
return state.getoutput()
else:
_execute(command, from_tty)
_execute = gdb.execute
gdb.execute = execute
_logging_state = _LoggingState()
class GenericCodeStepper(gdb.Command):
...
...
@@ -1494,7 +1523,9 @@ class GenericCodeStepper(gdb.Command):
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)
frame). If the source code cannot be
retrieved this function should
return None
This class provides an '
invoke
' method that invokes a '
step
' or '
step
-
over
'
depending on the '
stepper
' argument.
...
...
@@ -1545,12 +1576,16 @@ class GenericCodeStepper(gdb.Command):
return not (hit_breakpoint or new_lineno or is_relevant_function)
def _end_stepping(self):
# sys.stdout.write(self.result)
if not self.stopped_running
:
if self.stopped_running:
sys.stdout.write(self.result)
else
:
frame = gdb.selected_frame()
if self.is_relevant_function(frame):
print self.get_source_line(frame)
output = self.get_source_line(frame)
if output is None:
sys.stdout.write(self.result)
else:
print output
def _stackdepth(self, frame):
depth = 0
...
...
@@ -1600,8 +1635,7 @@ class PythonCodeStepper(GenericCodeStepper):
try:
return self.pyframe(frame).current_line().rstrip()
except IOError, e:
gdb.GdbError('
Unable
to
retrieve
source
code
:
%
s
' % (e,))
return None
class PyStep(PythonCodeStepper):
"Step through Python code."
...
...
@@ -1611,6 +1645,3 @@ class PyNext(PythonCodeStepper):
py_step = PyStep('
py
-
step
', stepper=True)
py_next = PyNext('
py
-
next
', stepper=False)
class PyShowCCode(gdb.Parameter):
pass
\ No newline at end of file
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