Commit f7d7598f authored by Stefan Behnel's avatar Stefan Behnel

merge

parents 09d92f6f 27961df2
This diff is collapsed.
......@@ -64,7 +64,6 @@ class SkipDeclarations(object):
def visit_CStructOrUnionDefNode(self, node):
return node
class NormalizeTree(CythonTransform):
"""
This transform fixes up a few things after parsing
......
......@@ -378,6 +378,32 @@ class TestExec(DebugTestCase):
gdb.execute('cy exec some_random_var = 14')
self.assertEqual('14', self.eval_command('some_random_var'))
class CySet(DebugTestCase):
def test_cyset(self):
self.break_and_run('os.path.join("foo", "bar")')
gdb.execute('cy set a = $cy_eval("{None: []}")')
stringvalue = self.read_var("a", cast_to=str)
self.assertEqual(stringvalue, "{None: []}")
class TestCyEval(DebugTestCase):
"Test the $cy_eval() gdb function."
def test_cy_eval(self):
# This function leaks a few objects in the GDB python process. This
# is no biggie
self.break_and_run('os.path.join("foo", "bar")')
result = gdb.execute('print $cy_eval("None")', to_string=True)
assert re.match(r'\$\d+ = None\n', result), result
result = gdb.execute('print $cy_eval("[a]")', to_string=True)
assert re.match(r'\$\d+ = \[0\]', result), result
class TestClosure(DebugTestCase):
def break_and_run_func(self, funcname):
......
......@@ -112,6 +112,4 @@ class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase):
def test_frame_type(self):
frame = self.pyobject_fromcode('PyEval_GetFrame()')
self.assertEqual(type(frame), libpython.PyFrameObjectPtr)
self.assertEqual(type(frame), libpython.PyFrameObjectPtr)
\ No newline at end of file
......@@ -592,6 +592,7 @@ class CyCy(CythonCommand):
cy bt / cy backtrace
cy list
cy print
cy set
cy locals
cy globals
cy exec
......@@ -607,6 +608,7 @@ class CyCy(CythonCommand):
completer_class, prefix=True)
commands = dict(
# GDB commands
import_ = CyImport.register(),
break_ = CyBreak.register(),
step = CyStep.register(),
......@@ -624,9 +626,13 @@ class CyCy(CythonCommand):
globals = CyGlobals.register(),
exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'),
_exec = CyExec.register(),
set = CySet.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():
......@@ -1169,15 +1175,14 @@ class CyGlobals(CyLocals):
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()
......@@ -1208,28 +1213,22 @@ class CyExec(CythonCommand, libpython.PyExec):
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
......@@ -1240,9 +1239,65 @@ class CyExec(CythonCommand, libpython.PyExec):
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(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 libpython._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))
class CySet(CythonCommand):
"""
Set a Cython variable to a certain value
cy set my_cython_c_variable = 10
cy set my_cython_py_variable = $cy_eval("{'doner': 'kebab'}")
This is equivalent to
set $cy_value("my_cython_variable") = 10
"""
name = 'cy set'
command_class = gdb.COMMAND_DATA
completer_class = gdb.COMPLETE_NONE
@require_cython_frame
def invoke(self, expr, from_tty):
name_and_expr = expr.split('=', 1)
if len(name_and_expr) != 2:
raise gdb.GdbError("Invalid expression. Use 'cy set var = expr'.")
varname, expr = name_and_expr
cname = self.cy.cy_cname.invoke(varname.strip())
gdb.execute("set %s = %s" % (cname, expr))
# Functions
......@@ -1312,6 +1367,18 @@ class CyLine(gdb.Function, CythonBase):
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
......
......@@ -1949,26 +1949,40 @@ class ExecutionControlCommandBase(gdb.Command):
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 = [
output_on_halt = [
(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:
output.append(match.group(0))
output_always = [
# output when halting on a watchpoint
(r'^(Old|New) value = .*', reflags),
# output from the 'display' command
(r'^\d+: \w+ = .*', reflags),
]
def filter_output(regexes):
output = []
for regex, flags in regexes:
for match in re.finditer(regex, result, flags):
output.append(match.group(0))
return '\n'.join(output)
# Filter the return value output of the 'finish' command
match_finish = re.search(r'^Value returned is \$\d+ = (.*)', result,
re.MULTILINE)
if match_finish:
finish_output = 'Value returned: %s\n' % match_finish.group(1)
else:
finish_output = ''
return (filter_output(output_on_halt),
finish_output + filter_output(output_always))
return '\n'.join(output)
def stopped(self):
return get_selected_inferior().pid == 0
......@@ -1979,17 +1993,23 @@ class ExecutionControlCommandBase(gdb.Command):
of source code or the result of the last executed gdb command (passed
in as the `result` argument).
"""
result = self.filter_output(result)
output_on_halt, output_always = self.filter_output(result)
if self.stopped():
print result.strip()
print output_always
print output_on_halt
else:
frame = gdb.selected_frame()
source_line = self.lang_info.get_source_line(frame)
if self.lang_info.is_relevant_function(frame):
raised_exception = self.lang_info.exc_info(frame)
if raised_exception:
print raised_exception
print self.lang_info.get_source_line(frame) or result
if source_line:
if output_always.rstrip():
print output_always.rstrip()
print source_line
else:
print result
......@@ -2190,12 +2210,12 @@ class PythonInfo(LanguageInfo):
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' % (inf_value,)
except (ValueError, RuntimeError), e:
# Could not read the variable tstate or it's memory, it's ok
pass
......@@ -2342,7 +2362,7 @@ class PythonCodeExecutor(object):
"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
......@@ -2382,10 +2402,11 @@ class PythonCodeExecutor(object):
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):
"""
......@@ -2462,6 +2483,20 @@ class FixGdbCommand(gdb.Command):
self.fix_gdb()
def _evalcode_python(executor, code, input_type):
"""
Execute Python code in the most recent stack frame.
"""
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).")
return executor.evalcode(code, input_type, global_dict, local_dict)
class PyExec(gdb.Command):
def readcode(self, expr):
......@@ -2484,17 +2519,9 @@ class PyExec(gdb.Command):
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)
executor.xdecref(_evalcode_python(executor, input_type, global_dict,
local_dict))
gdb.execute('set breakpoint pending on')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment