Commit d2d8e141 authored by Stefan Behnel's avatar Stefan Behnel

merge

parents a94909c1 fe17af96
......@@ -167,14 +167,15 @@ class GdbDebuggerTestCase(DebuggerTestCase):
p.stdout.close()
if have_gdb:
python_version_script = tempfile.NamedTemporaryFile()
python_version_script.write('python import sys; print sys.version_info\n')
python_version_script = tempfile.NamedTemporaryFile(mode='w+')
python_version_script.write(
'python import sys; print("%s %s" % sys.version_info[:2])')
python_version_script.flush()
p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name],
stdout=subprocess.PIPE)
python_version = p.stdout.read().decode('ascii')
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:
# Based on Lib/test/test_gdb.py
......@@ -184,9 +185,10 @@ class GdbDebuggerTestCase(DebuggerTestCase):
# Be Python 3 compatible
if (not have_gdb
or list(map(int, gdb_version_number)) < [7, 2]
or python_version_number < [2, 5]):
or python_version_number < [2, 6]):
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:
self.p = subprocess.Popen(
args,
......
......@@ -232,7 +232,10 @@ class TestStep(DebugStepperTestCase):
self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
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
......@@ -280,21 +283,20 @@ class TestBacktrace(DebugTestCase):
libcython.parameters.colorize_code.value = False
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',
result), result
assert 'os.path.join("foo", "bar")' in result, result
def match_backtrace_output(result):
assert re.search(r'\#\d+ *0x.* in spam\(\) at .*codefile\.pyx:22',
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)
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):
......
......@@ -52,14 +52,14 @@ class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase):
def alloc_bytestring(self, string, gdbvar=None):
if inferior_python_version < (3, 0):
funcname = 'PyString_FromString'
funcname = 'PyString_FromStringAndSize'
else:
funcname = 'PyBytes_FromString'
funcname = 'PyBytes_FromStringAndSize'
assert '"' not in string
# 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)
def alloc_unicodestring(self, string, gdbvar=None):
......
......@@ -4,10 +4,8 @@ GDB extension that adds Cython support.
from __future__ import with_statement
import os
import sys
import textwrap
import operator
import traceback
import functools
import itertools
......@@ -16,8 +14,8 @@ import collections
import gdb
try:
from lxml import etree
have_lxml = True
from lxml import etree
have_lxml = True
except ImportError:
have_lxml = False
try:
......@@ -279,7 +277,7 @@ class CythonBase(object):
if self.is_cython_function(frame) or self.is_python_function(frame):
return True
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)
return name in cython_func.step_into_functions
......@@ -756,8 +754,8 @@ class CyBreak(CythonCommand):
breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno)
gdb.execute('break ' + breakpoint)
else:
raise GdbError("Not a valid line number. "
"Does it contain actual code?")
raise gdb.GdbError("Not a valid line number. "
"Does it contain actual code?")
def _break_funcname(self, funcname):
func = self.cy.functions_by_qualified_name.get(funcname)
......@@ -1030,7 +1028,7 @@ class CyBacktrace(CythonCommand):
@require_running_program
def invoke(self, args, from_tty):
# get the first frame
selected_frame = frame = gdb.selected_frame()
frame = gdb.selected_frame()
while frame.older():
frame = frame.older()
......@@ -1038,13 +1036,10 @@ class CyBacktrace(CythonCommand):
index = 0
while frame:
is_c = False
is_relevant = False
try:
is_relevant = self.is_relevant_function(frame)
except CyGDBError:
pass
is_relevant = False
if print_all or is_relevant:
self.print_stackframe(frame, index)
......@@ -1052,8 +1047,6 @@ class CyBacktrace(CythonCommand):
index += 1
frame = frame.newer()
selected_frame.select()
class CyList(CythonCommand):
"""
......@@ -1188,7 +1181,6 @@ class CyExec(CythonCommand, libpython.PyExec):
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()
current_lineno = self.get_cython_lineno()
for name, cyvar in cython_func.locals.iteritems():
if (cyvar.type == PythonObject and
......@@ -1245,8 +1237,6 @@ class CyExec(CythonCommand, libpython.PyExec):
'(PyObject *) PyModule_GetDict(__pyx_m)')
local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()')
cython_function = self.get_cython_function()
try:
self._fill_locals_dict(executor,
libpython.pointervalue(local_dict))
......
......@@ -1322,6 +1322,16 @@ class PyUnicodeObjectPtr(PyObjectPtr):
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):
return int(str(gdbval))
......@@ -1452,12 +1462,39 @@ class Frame(object):
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):
try:
f = self._gdbframe.read_var('f')
return PyFrameObjectPtr.from_pyobject_ptr(f)
except ValueError:
# 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
else:
return PyFrameObjectPtr.from_pyobject_ptr(f)
@classmethod
def get_selected_frame(cls):
......@@ -2174,9 +2211,10 @@ class PythonStepperMixin(object):
"""
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)
watchpoint = int(re.search(r'[Ww]atchpoint (\d+):', output).group(1))
self.step(stepinto=stepinto, stepover_command='finish')
......@@ -2281,7 +2319,7 @@ class PythonCodeExecutor(object):
except RuntimeError:
# Python 3
PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' %
(get_inferior_unicode_postfix,))
(get_inferior_unicode_postfix(),))
try:
result = gdb.parse_and_eval(
......@@ -2392,10 +2430,9 @@ class FixGdbCommand(gdb.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?
It seems that invoking either 'cy exec' and 'py-exec' work perfectly
fine, but after this gdb's python API is entirely broken.
Maybe some uncleared exception value is still set?
sys.exc_clear() didn't help. A demonstration:
(gdb) cy exec 'hello'
......@@ -2443,7 +2480,7 @@ class PyExec(gdb.Command):
lines.append(line)
return '\n'.join(lines), Py_file_input
return '\n'.join(lines), PythonCodeExecutor.Py_file_input
def invoke(self, expr, from_tty):
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