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
dcd7f2a7
Commit
dcd7f2a7
authored
Nov 13, 2010
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added 'cy exec' and 'py-exec' commands
parent
ad032f3f
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
289 additions
and
8 deletions
+289
-8
Cython/Debugger/libcython.py
Cython/Debugger/libcython.py
+82
-6
Cython/Debugger/libpython.py
Cython/Debugger/libpython.py
+207
-2
No files found.
Cython/Debugger/libcython.py
View file @
dcd7f2a7
...
...
@@ -346,11 +346,7 @@ class CythonBase(object):
selected_frame
.
select
()
def
get_cython_globals_dict
(
self
):
"""
Get the Cython globals dict where the remote names are turned into
local strings.
"""
def
get_remote_cython_globals_dict
(
self
):
m
=
gdb
.
parse_and_eval
(
'__pyx_m'
)
try
:
...
...
@@ -361,7 +357,16 @@ class CythonBase(object):
with debugging support (-g)?"""
))
m
=
m
.
cast
(
PyModuleObject
.
pointer
())
pyobject_dict
=
libpython
.
PyObjectPtr
.
from_pyobject_ptr
(
m
[
'md_dict'
])
return
m
[
'md_dict'
]
def
get_cython_globals_dict
(
self
):
"""
Get the Cython globals dict where the remote names are turned into
local strings.
"""
remote_dict
=
self
.
get_remote_cython_globals_dict
()
pyobject_dict
=
libpython
.
PyObjectPtr
.
from_pyobject_ptr
(
remote_dict
)
result
=
{}
seen
=
set
()
...
...
@@ -598,6 +603,8 @@ class CyCy(CythonCommand):
print_
=
CyPrint
.
register
(),
locals
=
CyLocals
.
register
(),
globals
=
CyGlobals
.
register
(),
exec_
=
libpython
.
FixGdbCommand
(
'cy exec'
,
'-cy-exec'
),
_exec
=
CyExec
.
register
(),
cy_cname
=
CyCName
(
'cy_cname'
),
cy_cvalue
=
CyCValue
(
'cy_cvalue'
),
cy_lineno
=
CyLine
(
'cy_lineno'
),
...
...
@@ -1092,6 +1099,75 @@ class CyGlobals(CyLocals):
prefix
=
' '
)
class
CyExec
(
CythonCommand
):
name
=
'-cy-exec'
command_class
=
gdb
.
COMMAND_STACK
completer_class
=
gdb
.
COMPLETE_NONE
def
_fill_locals_dict
(
self
,
executor
,
local_dict_pointer
):
"Fill a remotely allocated dict with values from the Cython C stack"
cython_func
=
self
.
get_cython_function
()
for
name
,
cyvar
in
cython_func
.
locals
.
iteritems
():
if
cyvar
.
type
==
PythonObject
:
# skip unitialized Cython variables
try
:
val
=
gdb
.
parse_and_eval
(
cyvar
.
cname
)
except
RuntimeError
:
continue
else
:
# Fortunately, Cython initializes all local (automatic)
# variables to NULL
if
libpython
.
pointervalue
(
val
)
==
0
:
continue
pystringp
=
executor
.
alloc_pystring
(
name
)
code
=
'''
PyDict_SetItem(
(PyObject *) %d,
(PyObject *) %d,
(PyObject *) %s)
'''
%
(
local_dict_pointer
,
pystringp
,
cyvar
.
cname
)
# PyDict_SetItem doesn't steal our reference
executor
.
decref
(
pystringp
)
if
gdb
.
parse_and_eval
(
code
)
<
0
:
gdb
.
parse_and_eval
(
'PyErr_Print()'
)
raise
gdb
.
GdbError
(
"Unable to execute Python code."
)
def
_find_first_cython_or_python_frame
(
self
):
frame
=
gdb
.
selected_frame
()
while
frame
:
if
(
self
.
is_cython_function
(
frame
)
or
self
.
is_python_function
(
frame
)):
return
frame
frame
=
frame
.
older
()
raise
gdb
.
GdbError
(
"There is no Cython or Python frame on the stack."
)
def
invoke
(
self
,
expr
,
from_tty
):
frame
=
self
.
_find_first_cython_or_python_frame
()
if
self
.
is_python_function
(
frame
):
libpython
.
py_exec
.
invoke
(
expr
,
from_tty
)
return
executor
=
libpython
.
PythonCodeExecutor
()
# get the dict of Cython globals and construct a dict in the inferior
# with Cython locals
global_dict
=
gdb
.
parse_and_eval
(
'(PyObject *) PyModule_GetDict(__pyx_m)'
)
local_dict
=
gdb
.
parse_and_eval
(
'(PyObject *) PyDict_New()'
)
try
:
self
.
_fill_locals_dict
(
executor
,
libpython
.
pointervalue
(
local_dict
))
executor
.
evalcode
(
expr
,
global_dict
,
local_dict
)
finally
:
executor
.
decref
(
libpython
.
pointervalue
(
local_dict
))
# Functions
class
CyCName
(
gdb
.
Function
,
CythonBase
):
...
...
Cython/Debugger/libpython.py
View file @
dcd7f2a7
...
...
@@ -49,6 +49,7 @@ import os
import
re
import
sys
import
atexit
import
warnings
import
tempfile
import
itertools
...
...
@@ -1591,6 +1592,22 @@ gdb.execute = execute
_logging_state = _LoggingState()
def get_selected_inferior():
"""
Return the selected inferior in gdb.
"""
# Woooh, another bug in gdb! Is there an end in sight?
# http://sourceware.org/bugzilla/show_bug.cgi?id=12212
return gdb.inferiors()[0]
selected_thread = gdb.selected_thread()
for inferior in gdb.inferiors():
for thread in inferior.threads():
if thread == selected_thread:
return inferior
class GenericCodeStepper(gdb.Command):
"""
Superclass for code stepping. Subclasses must implement the following
...
...
@@ -1687,7 +1704,7 @@ class GenericCodeStepper(gdb.Command):
"""
def stopped(self):
return g
db.inferiors()[0]
.pid == 0
return g
et_selected_inferior()
.pid == 0
def _stackdepth(self, frame):
depth = 0
...
...
@@ -1881,4 +1898,192 @@ py_finish = PyFinish('py-finish')
py_run = PyRun('py-run')
py_cont = PyCont('py-cont')
py_step.init_breakpoints()
\ No newline at end of file
py_step.init_breakpoints()
Py_single_input = 256
Py_eval_input = 258
def pointervalue(gdbval):
"""
Return
the
value
of
the
pionter
as
a
Python
int
.
gdbval
.
type
must
be
a
pointer
type
"""
# don't convert with int() as it will raise a RuntimeError
if gdbval.address is not None:
return long(gdbval.address)
else:
# the address attribute is None sometimes, in which case we can
# still convert the pointer to an int
return long(gdbval)
class PythonCodeExecutor(object):
def malloc(self, size):
chunk = (gdb.parse_and_eval("(void *) malloc(%d)" % size))
pointer = pointervalue(chunk)
if pointer == 0:
err("No memory could be allocated in the inferior.")
return pointer
def alloc_string(self, string):
pointer = self.malloc(len(string))
get_selected_inferior().write_memory(pointer, string)
return pointer
def alloc_pystring(self, string):
stringp = self.alloc_string(string)
try:
result = gdb.parse_and_eval(
'PyString_FromStringAndSize((char *) %d, (size_t) %d)' %
(stringp, len(string)))
finally:
self.free(stringp)
pointer = pointervalue(result)
if pointer == 0:
err("Unable to allocate Python string in "
"the inferior.")
return pointer
def free(self, pointer):
gdb.parse_and_eval("free((void *) %d)" % pointer)
def decref(self, pointer):
"Decrement the reference count of a Python object in the inferior."
# Py_DecRef is like Py_XDECREF, but a function. So we don't have
# to check for NULL. This should also decref all our allocated
# Python strings.
gdb.parse_and_eval('Py_DecRef((PyObject *) %d)' % pointer)
def evalcode(self, code, global_dict=None, local_dict=None):
"""
Evaluate
python
code
`code`
given
as
a
string
in
the
inferior
and
return
the
result
as
a
gdb
.
Value
.
Returns
a
new
reference
in
the
inferior
.
Of
course
,
executing
any
code
in
the
inferior
may
be
dangerous
and
may
leave
the
debuggee
in
an
unsafe
state
or
terminate
it
alltogether
.
"""
if '
\
0
' in code:
err("String contains NUL byte.")
code += '
\
0
'
pointer = self.alloc_string(code)
globalsp = pointervalue(global_dict)
localsp = pointervalue(local_dict)
if globalp == 0 or localp == 0:
raise gdb.GdbError("Unable to obtain or create locals or globals.")
code = """
PyRun_String
(
(
PyObject
*
)
%
(
code
)
d
,
(
int
)
%
(
start
)
d
,
(
PyObject
*
)
%
(
globals
)
s
,
(
PyObject
*
)
%
(
locals
)
d
)
""" % dict(code=pointer, start=Py_single_input,
globals=globalsp, locals=localsp)
with FetchAndRestoreError():
try:
self.decref(gdb.parse_and_eval(code))
finally:
self.free(pointer)
class FetchAndRestoreError(PythonCodeExecutor):
"""
Context
manager
that
fetches
the
error
indicator
in
the
inferior
and
restores
it
on
exit
.
"""
def __init__(self):
self.sizeof_PyObjectPtr = gdb.lookup_type('PyObject').pointer().sizeof
self.pointer = self.malloc(self.sizeof_PyObjectPtr * 3)
type = self.pointer
value = self.pointer + self.sizeof_PyObjectPtr
traceback = self.pointer + self.sizeof_PyObjectPtr * 2
self.errstate = type, value, traceback
def __enter__(self):
gdb.parse_and_eval("PyErr_Fetch(%d, %d, %d)" % self.errstate)
def __exit__(self, *args):
if gdb.parse_and_eval("(int) PyErr_Occurred()"):
gdb.parse_and_eval("PyErr_Print()")
pyerr_restore = ("PyErr_Restore("
"(PyObject *) *%d,"
"(PyObject *) *%d,"
"(PyObject *) *%d)")
try:
gdb.parse_and_eval(pyerr_restore % self.errstate)
finally:
self.free(self.pointer)
class FixGdbCommand(gdb.Command):
def __init__(self, command, actual_command):
super(FixGdbCommand, self).__init__(command, gdb.COMMAND_DATA,
gdb.COMPLETE_NONE)
self.actual_command = actual_command
def fix_gdb(self):
"""
So
,
you
must
be
wondering
what
the
story
is
this
time
!
Yeeees
,
indeed
,
I
have
quite
the
story
for
you
!
It
seems
that
invoking
either
'cy exec'
and
'py-exec'
work
perfectly
fine
,
but
after
this
gdb
's python API is
entirely broken. Some unset exception value is still set?
sys.exc_clear() didn'
t
help
.
A
demonstration
:
(
gdb
)
cy
exec
'hello'
'hello'
(
gdb
)
python
gdb
.
execute
(
'cont'
)
RuntimeError
:
Cannot
convert
value
to
int
.
Error
while
executing
Python
code
.
(
gdb
)
python
gdb
.
execute
(
'cont'
)
[
15148
refs
]
Program
exited
normally
.
"""
warnings.filterwarnings('ignore', r'.*', RuntimeWarning, re.escape(__name__))
try:
long(gdb.parse_and_eval("(void *) 0")) == 0
except RuntimeError:
pass
# warnings.resetwarnings()
def invoke(self, args, from_tty):
self.fix_gdb()
gdb.execute('%s %s' % (self.actual_command, args))
self.fix_gdb()
class PyExec(gdb.Command):
def invoke(self, expr, from_tty):
executor = PythonCodeExecutor()
global_dict = gdb.parse_and_eval('PyEval_GetGlobals()')
local_dict = gdb.parse_and_eval('PyEval_GetLocals()')
if pointervalue(global_dict) == 0 or pointervalue(local_dict) == 0:
raise gdb.GdbError("Unable to find the locals or globals of the "
"most recent Python function (relative to the "
"selected frame).")
executor.evalcode(expr, global_dict, local_dict)
py_exec = FixGdbCommand('py-exec', '-py-exec')
_py_exec = PyExec("-py-exec", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
\ 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