Commit d2d8e141 authored by Stefan Behnel's avatar Stefan Behnel

merge

parents a94909c1 fe17af96
...@@ -167,14 +167,15 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -167,14 +167,15 @@ class GdbDebuggerTestCase(DebuggerTestCase):
p.stdout.close() p.stdout.close()
if have_gdb: if have_gdb:
python_version_script = tempfile.NamedTemporaryFile() python_version_script = tempfile.NamedTemporaryFile(mode='w+')
python_version_script.write('python import sys; print sys.version_info\n') python_version_script.write(
'python import sys; print("%s %s" % sys.version_info[:2])')
python_version_script.flush() python_version_script.flush()
p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name], p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
python_version = p.stdout.read().decode('ascii') python_version = p.stdout.read().decode('ascii')
p.wait() p.wait()
python_version_number = [int(a.strip()) for a in python_version.strip('()').split(',')[:3]] python_version_number = [int(a) for a in python_version.split()]
if have_gdb: if have_gdb:
# Based on Lib/test/test_gdb.py # Based on Lib/test/test_gdb.py
...@@ -184,9 +185,10 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -184,9 +185,10 @@ class GdbDebuggerTestCase(DebuggerTestCase):
# Be Python 3 compatible # Be Python 3 compatible
if (not have_gdb if (not have_gdb
or list(map(int, gdb_version_number)) < [7, 2] or list(map(int, gdb_version_number)) < [7, 2]
or python_version_number < [2, 5]): or python_version_number < [2, 6]):
self.p = None self.p = None
warnings.warn('Skipping gdb tests, need gdb >= 7.2 with Python >= 2.5') warnings.warn(
'Skipping gdb tests, need gdb >= 7.2 with Python >= 2.6')
else: else:
self.p = subprocess.Popen( self.p = subprocess.Popen(
args, args,
......
...@@ -232,7 +232,10 @@ class TestStep(DebugStepperTestCase): ...@@ -232,7 +232,10 @@ class TestStep(DebugStepperTestCase):
self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx') self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
pyframe = libpython.Frame(curframe).get_pyop() pyframe = libpython.Frame(curframe).get_pyop()
self.assertEqual(str(pyframe.co_name), 'join') # With Python 3 inferiors, pyframe.co_name will return a PyUnicodePtr,
# be compatible
frame_name = pyframe.co_name.proxyval(set())
self.assertEqual(frame_name, 'join')
assert re.match(r'\d+ def join\(', result), result assert re.match(r'\d+ def join\(', result), result
...@@ -280,21 +283,20 @@ class TestBacktrace(DebugTestCase): ...@@ -280,21 +283,20 @@ class TestBacktrace(DebugTestCase):
libcython.parameters.colorize_code.value = False libcython.parameters.colorize_code.value = False
self.break_and_run('os.path.join("foo", "bar")') self.break_and_run('os.path.join("foo", "bar")')
result = gdb.execute('cy bt', to_string=True)
_debug(libpython.execute, libpython._execute, gdb.execute)
_debug(gdb.execute('cy list', to_string=True))
_debug(repr(result))
assert re.search(r'\#\d+ *0x.* in spam\(\) at .*codefile\.pyx:22', def match_backtrace_output(result):
result), result assert re.search(r'\#\d+ *0x.* in spam\(\) at .*codefile\.pyx:22',
assert 'os.path.join("foo", "bar")' in result, result result), result
assert 'os.path.join("foo", "bar")' in result, result
gdb.execute("cy step") result = gdb.execute('cy bt', to_string=True)
match_backtrace_output(result)
gdb.execute('cy bt')
result = gdb.execute('cy bt -a', to_string=True) result = gdb.execute('cy bt -a', to_string=True)
assert re.search(r'\#0 *0x.* in main\(\)', result), result match_backtrace_output(result)
# Apparently not everyone has main()
# assert re.search(r'\#0 *0x.* in main\(\)', result), result
class TestFunctions(DebugTestCase): class TestFunctions(DebugTestCase):
......
...@@ -52,14 +52,14 @@ class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase): ...@@ -52,14 +52,14 @@ class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase):
def alloc_bytestring(self, string, gdbvar=None): def alloc_bytestring(self, string, gdbvar=None):
if inferior_python_version < (3, 0): if inferior_python_version < (3, 0):
funcname = 'PyString_FromString' funcname = 'PyString_FromStringAndSize'
else: else:
funcname = 'PyBytes_FromString' funcname = 'PyBytes_FromStringAndSize'
assert '"' not in string assert '"' not in string
# ensure double quotes # ensure double quotes
code = '(PyObject *) %s("%s")' % (funcname, string) code = '(PyObject *) %s("%s", %d)' % (funcname, string, len(string))
return self.pyobject_fromcode(code, gdbvar=gdbvar) return self.pyobject_fromcode(code, gdbvar=gdbvar)
def alloc_unicodestring(self, string, gdbvar=None): def alloc_unicodestring(self, string, gdbvar=None):
......
...@@ -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
...@@ -16,8 +14,8 @@ import collections ...@@ -16,8 +14,8 @@ import collections
import gdb import gdb
try: try:
from lxml import etree from lxml import etree
have_lxml = True have_lxml = True
except ImportError: except ImportError:
have_lxml = False have_lxml = False
try: try:
...@@ -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,8 +754,8 @@ class CyBreak(CythonCommand): ...@@ -756,8 +754,8 @@ 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):
func = self.cy.functions_by_qualified_name.get(funcname) func = self.cy.functions_by_qualified_name.get(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 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:
var = self._gdbframe.read_var(varname, block)
except ValueError:
pass
block = block.superblock
return var
def get_pyop(self): def get_pyop(self):
try: try:
f = self._gdbframe.read_var('f') # self.read_var does not always work properly, so select our frame
return PyFrameObjectPtr.from_pyobject_ptr(f) # and restore the previously selected frame
except ValueError: 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')
...@@ -2281,7 +2319,7 @@ class PythonCodeExecutor(object): ...@@ -2281,7 +2319,7 @@ class PythonCodeExecutor(object):
except RuntimeError: except RuntimeError:
# Python 3 # Python 3
PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' % PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' %
(get_inferior_unicode_postfix,)) (get_inferior_unicode_postfix(),))
try: try:
result = gdb.parse_and_eval( result = gdb.parse_and_eval(
...@@ -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