Commit b0f6eba4 authored by Mark Florisson's avatar Mark Florisson

Debugger: Fix a few libpython.py bugs

parent ae137ef7
...@@ -4,10 +4,8 @@ GDB extension that adds Cython support. ...@@ -4,10 +4,8 @@ GDB extension that adds Cython support.
from __future__ import with_statement from __future__ import with_statement
import os
import sys import sys
import textwrap import textwrap
import operator
import traceback import traceback
import functools import functools
import itertools import itertools
...@@ -279,7 +277,7 @@ class CythonBase(object): ...@@ -279,7 +277,7 @@ class CythonBase(object):
if self.is_cython_function(frame) or self.is_python_function(frame): if self.is_cython_function(frame) or self.is_python_function(frame):
return True return True
elif older_frame and self.is_cython_function(older_frame): elif older_frame and self.is_cython_function(older_frame):
# direct C function call from a Cython function # check for direct C function call from a Cython function
cython_func = self.get_cython_function(older_frame) cython_func = self.get_cython_function(older_frame)
return name in cython_func.step_into_functions return name in cython_func.step_into_functions
...@@ -756,7 +754,7 @@ class CyBreak(CythonCommand): ...@@ -756,7 +754,7 @@ class CyBreak(CythonCommand):
breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno) breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno)
gdb.execute('break ' + breakpoint) gdb.execute('break ' + breakpoint)
else: else:
raise GdbError("Not a valid line number. " raise gdb.GdbError("Not a valid line number. "
"Does it contain actual code?") "Does it contain actual code?")
def _break_funcname(self, funcname): def _break_funcname(self, funcname):
...@@ -1030,7 +1028,7 @@ class CyBacktrace(CythonCommand): ...@@ -1030,7 +1028,7 @@ class CyBacktrace(CythonCommand):
@require_running_program @require_running_program
def invoke(self, args, from_tty): def invoke(self, args, from_tty):
# get the first frame # get the first frame
selected_frame = frame = gdb.selected_frame() frame = gdb.selected_frame()
while frame.older(): while frame.older():
frame = frame.older() frame = frame.older()
...@@ -1038,13 +1036,10 @@ class CyBacktrace(CythonCommand): ...@@ -1038,13 +1036,10 @@ class CyBacktrace(CythonCommand):
index = 0 index = 0
while frame: while frame:
is_c = False
is_relevant = False
try: try:
is_relevant = self.is_relevant_function(frame) is_relevant = self.is_relevant_function(frame)
except CyGDBError: except CyGDBError:
pass is_relevant = False
if print_all or is_relevant: if print_all or is_relevant:
self.print_stackframe(frame, index) self.print_stackframe(frame, index)
...@@ -1052,8 +1047,6 @@ class CyBacktrace(CythonCommand): ...@@ -1052,8 +1047,6 @@ class CyBacktrace(CythonCommand):
index += 1 index += 1
frame = frame.newer() frame = frame.newer()
selected_frame.select()
class CyList(CythonCommand): class CyList(CythonCommand):
""" """
...@@ -1188,7 +1181,6 @@ class CyExec(CythonCommand, libpython.PyExec): ...@@ -1188,7 +1181,6 @@ class CyExec(CythonCommand, libpython.PyExec):
def _fill_locals_dict(self, executor, local_dict_pointer): def _fill_locals_dict(self, executor, local_dict_pointer):
"Fill a remotely allocated dict with values from the Cython C stack" "Fill a remotely allocated dict with values from the Cython C stack"
cython_func = self.get_cython_function() cython_func = self.get_cython_function()
current_lineno = self.get_cython_lineno()
for name, cyvar in cython_func.locals.iteritems(): for name, cyvar in cython_func.locals.iteritems():
if (cyvar.type == PythonObject and if (cyvar.type == PythonObject and
...@@ -1245,8 +1237,6 @@ class CyExec(CythonCommand, libpython.PyExec): ...@@ -1245,8 +1237,6 @@ class CyExec(CythonCommand, libpython.PyExec):
'(PyObject *) PyModule_GetDict(__pyx_m)') '(PyObject *) PyModule_GetDict(__pyx_m)')
local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()') local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()')
cython_function = self.get_cython_function()
try: try:
self._fill_locals_dict(executor, self._fill_locals_dict(executor,
libpython.pointervalue(local_dict)) libpython.pointervalue(local_dict))
......
...@@ -1322,6 +1322,16 @@ class PyUnicodeObjectPtr(PyObjectPtr): ...@@ -1322,6 +1322,16 @@ class PyUnicodeObjectPtr(PyObjectPtr):
out.write(quote) out.write(quote)
def __unicode__(self):
return self.proxyval(set())
def __str__(self):
# In Python 3, everything is unicode (including attributes of e.g.
# code objects, such as function names). The Python 2 debugger code
# uses PyUnicodePtr objects to format strings etc, whereas with a
# Python 2 debuggee we'd get PyStringObjectPtr instances with __str__.
# Be compatible with that.
return unicode(self).encode('UTF-8')
def int_from_int(gdbval): def int_from_int(gdbval):
return int(str(gdbval)) return int(str(gdbval))
...@@ -1452,12 +1462,39 @@ class Frame(object): ...@@ -1452,12 +1462,39 @@ class Frame(object):
return False return False
def get_pyop(self): def read_var(self, varname):
"""
read_var with respect to code blocks (gdbframe.read_var works with
respect to the most recent block)
Apparently this function doesn't work, though, as it seems to read
variables in other frames also sometimes.
"""
block = self._gdbframe.block()
var = None
while block and var is None:
try: try:
f = self._gdbframe.read_var('f') var = self._gdbframe.read_var(varname, block)
return PyFrameObjectPtr.from_pyobject_ptr(f)
except ValueError: except ValueError:
pass
block = block.superblock
return var
def get_pyop(self):
try:
# self.read_var does not always work properly, so select our frame
# and restore the previously selected frame
selected_frame = gdb.selected_frame()
self._gdbframe.select()
f = gdb.parse_and_eval('f')
selected_frame.select()
except RuntimeError:
return None return None
else:
return PyFrameObjectPtr.from_pyobject_ptr(f)
@classmethod @classmethod
def get_selected_frame(cls): def get_selected_frame(cls):
...@@ -2174,9 +2211,10 @@ class PythonStepperMixin(object): ...@@ -2174,9 +2211,10 @@ class PythonStepperMixin(object):
""" """
def python_step(self, stepinto): def python_step(self, stepinto):
frame = gdb.selected_frame() """
framewrapper = Frame(frame) Set a watchpoint on the Python bytecode instruction pointer and try
to finish the frame
"""
output = gdb.execute('watch f->f_lasti', to_string=True) output = gdb.execute('watch f->f_lasti', to_string=True)
watchpoint = int(re.search(r'[Ww]atchpoint (\d+):', output).group(1)) watchpoint = int(re.search(r'[Ww]atchpoint (\d+):', output).group(1))
self.step(stepinto=stepinto, stepover_command='finish') self.step(stepinto=stepinto, stepover_command='finish')
...@@ -2392,10 +2430,9 @@ class FixGdbCommand(gdb.Command): ...@@ -2392,10 +2430,9 @@ class FixGdbCommand(gdb.Command):
def fix_gdb(self): def fix_gdb(self):
""" """
So, you must be wondering what the story is this time! Yeeees, indeed, It seems that invoking either 'cy exec' and 'py-exec' work perfectly
I have quite the story for you! It seems that invoking either 'cy exec' fine, but after this gdb's python API is entirely broken.
and 'py-exec' work perfectly fine, but after this gdb's python API is Maybe some uncleared exception value is still set?
entirely broken. Some unset exception value is still set?
sys.exc_clear() didn't help. A demonstration: sys.exc_clear() didn't help. A demonstration:
(gdb) cy exec 'hello' (gdb) cy exec 'hello'
...@@ -2443,7 +2480,7 @@ class PyExec(gdb.Command): ...@@ -2443,7 +2480,7 @@ class PyExec(gdb.Command):
lines.append(line) lines.append(line)
return '\n'.join(lines), Py_file_input return '\n'.join(lines), PythonCodeExecutor.Py_file_input
def invoke(self, expr, from_tty): def invoke(self, expr, from_tty):
expr, input_type = self.readcode(expr) expr, input_type = self.readcode(expr)
......
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