completer_class, prefix=True)
commands = dict(
+ # GDB commands
import_ = CyImport.register(),
break_ = CyBreak.register(),
step = CyStep.register(),
globals = CyGlobals.register(),
exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'),
_exec = CyExec.register(),
+
+ # GDB functions
cy_cname = CyCName('cy_cname'),
cy_cvalue = CyCValue('cy_cvalue'),
cy_lineno = CyLine('cy_lineno'),
+ cy_eval = CyEval('cy_eval'),
)
for command_name, command in commands.iteritems():
max_name_length, ' ')
-class CyExec(CythonCommand, libpython.PyExec):
+
+class EvaluateOrExecuteCodeMixin(object):
"""
- Execute Python code in the nearest Python or Cython frame.
+ Evaluate or execute Python code in a Cython or Python frame. The 'evalcode'
+ method evaluations Python code, prints a traceback if an exception went
+ uncaught, and returns any return value as a gdb.Value (NULL on exception).
"""
- 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()
raise gdb.GdbError("Unable to execute Python code.")
finally:
# PyDict_SetItem doesn't steal our reference
- executor.decref(pystringp)
+ executor.xdecref(pystringp)
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)):
+ frame.select()
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
-
- expr, input_type = self.readcode(expr)
- executor = libpython.PythonCodeExecutor()
+ def _evalcode_cython(self, executor, code, input_type):
with libpython.FetchAndRestoreError():
# get the dict of Cython globals and construct a dict in the
# inferior with Cython locals
try:
self._fill_locals_dict(executor,
libpython.pointervalue(local_dict))
- executor.evalcode(expr, input_type, global_dict, local_dict)
+ result = executor.evalcode(code, input_type, global_dict,
+ local_dict)
finally:
- executor.decref(libpython.pointervalue(local_dict))
+ executor.xdecref(libpython.pointervalue(local_dict))
+
+ return result
+
+
+ def _evalcode_python(self, executor, code, input_type):
+ global_dict = gdb.parse_and_eval('PyEval_GetGlobals()')
+ local_dict = gdb.parse_and_eval('PyEval_GetLocals()')
+
+ if (libpython.pointervalue(global_dict) == 0 or
+ libpython.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).")
+
+ return executor.evalcode(code, input_type, global_dict, local_dict)
+
+ def evalcode(self, code, input_type):
+ """
+ Evaluate `code` in a Python or Cython stack frame using the given
+ `input_type`.
+ """
+ frame = self._find_first_cython_or_python_frame()
+ executor = libpython.PythonCodeExecutor()
+ if self.is_python_function(frame):
+ return self._evalcode_python(executor, code, input_type)
+ return self._evalcode_cython(executor, code, input_type)
+
+
+class CyExec(CythonCommand, libpython.PyExec, EvaluateOrExecuteCodeMixin):
+ """
+ Execute Python code in the nearest Python or Cython frame.
+ """
+
+ name = '-cy-exec'
+ command_class = gdb.COMMAND_STACK
+ completer_class = gdb.COMPLETE_NONE
+
+ def invoke(self, expr, from_tty):
+ expr, input_type = self.readcode(expr)
+ executor = libpython.PythonCodeExecutor()
+ executor.xdecref(self.evalcode(expr, executor.Py_single_input))
# Functions
def invoke(self):
return self.get_cython_lineno()
+
+class CyEval(gdb.Function, CythonBase, EvaluateOrExecuteCodeMixin):
+ """
+ Evaluate Python code in the nearest Python or Cython frame and return
+ """
+
+ @gdb_function_value_to_unicode
+ def invoke(self, python_expression):
+ input_type = libpython.PythonCodeExecutor.Py_eval_input
+ return self.evalcode(python_expression, input_type)
+
+
cython_info = CythonInfo()
cy = CyCy.register()
cython_info.cy = cy
try:
tstate = frame.read_var('tstate').dereference()
if gdb.parse_and_eval('tstate->frame == f'):
- # tstate local variable initialized
+ # tstate local variable initialized, check for an exception
inf_type = tstate['curexc_type']
inf_value = tstate['curexc_value']
+
if inf_type:
- return 'An exception was raised: %s(%s)' % (inf_type,
- inf_value)
+ return 'An exception was raised: %s(%s)' % (inf_value,)
except (ValueError, RuntimeError), e:
# Could not read the variable tstate or it's memory, it's ok
pass
"Increment the reference count of a Python object in the inferior."
gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer)
- def decref(self, pointer):
+ def xdecref(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
with FetchAndRestoreError():
try:
- self.decref(gdb.parse_and_eval(code))
+ pyobject_return_value = gdb.parse_and_eval(code)
finally:
self.free(pointer)
+ return pyobject_return_value
class FetchAndRestoreError(PythonCodeExecutor):
"""
def invoke(self, expr, from_tty):
expr, input_type = self.readcode(expr)
-
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, input_type, global_dict, local_dict)
+ result = executor.evalcode(expr, input_type, global_dict, local_dict)
+ executor.xdecref(result)
gdb.execute('set breakpoint pending on')