from Cython.Debugger import libpython
from Cython.Debugger.Tests import TestLibCython as test_libcython
-
# for some reason sys.argv is missing in gdb
sys.argv = ['gdb']
self.assertEqual(str(pyframe.co_name), 'join')
assert re.match(r'\d+ def join\(', result), result
+
class TestNext(DebugStepperTestCase):
def test_cython_next(self):
self.assertEqual('14', self.eval_command('some_random_var'))
-_do_debug = os.environ.get('CYTHON_GDB_DEBUG')
+_do_debug = os.environ.get('GDB_DEBUG')
if _do_debug:
_debug_file = open('/dev/tty', 'w')
def _debug(*messages):
if _do_debug:
- messages = itertools.chain([sys._getframe(1).f_code.co_name],
+ messages = itertools.chain([sys._getframe(1).f_code.co_name, ':'],
messages)
_debug_file.write(' '.join(str(msg) for msg in messages) + '\n')
-def _main():
+
+def run_unittest_in_module(modulename):
try:
gdb.lookup_type('PyModuleObject')
except RuntimeError:
warnings.warn(msg)
os._exit(1)
else:
- m = __import__(__name__, fromlist=[''])
+ m = __import__(modulename, fromlist=[''])
tests = inspect.getmembers(m, inspect.isclass)
# test_support.run_unittest(tests)
[test_loader.loadTestsFromTestCase(cls) for name, cls in tests])
result = unittest.TextTestRunner(verbosity=1).run(suite)
- if not result.wasSuccessful():
- os._exit(1)
+ return result.wasSuccessful()
-def main(trace_code=False):
+def runtests():
+ """
+ Run the libcython and libpython tests. Ensure that an appropriate status is
+ returned to the parent test process.
+ """
+ from Cython.Debugger.Tests import test_libpython_in_gdb
+
+ success_libcython = run_unittest_in_module(__name__)
+ success_libpython = run_unittest_in_module(test_libpython_in_gdb.__name__)
+
+ if not success_libcython or not success_libpython:
+ sys.exit(1)
+
+def main(version, trace_code=False):
+ global inferior_python_version
+
+ inferior_python_version = version
+
if trace_code:
tracer = trace.Trace(count=False, trace=True, outfile=sys.stderr,
ignoredirs=[sys.prefix, sys.exec_prefix])
- tracer.runfunc(_main)
+ tracer.runfunc(runtests)
else:
- _main()
-
-main()
\ No newline at end of file
+ runtests()
\ No newline at end of file
import atexit
import warnings
import tempfile
+import textwrap
import itertools
import gdb
# Look up the gdb.Type for some standard types:
_type_char_ptr = gdb.lookup_type('char').pointer() # char*
-_type_unsigned_char_ptr = gdb.lookup_type('unsigned char').pointer() # unsigned char*
+_type_unsigned_char_ptr = gdb.lookup_type('unsigned char').pointer()
_type_void_ptr = gdb.lookup_type('void').pointer() # void*
SIZEOF_VOID_P = _type_void_ptr.sizeof
-
Py_TPFLAGS_HEAPTYPE = (1L << 9)
Py_TPFLAGS_INT_SUBCLASS = (1L << 23)
Py_TPFLAGS_BASE_EXC_SUBCLASS = (1L << 30)
Py_TPFLAGS_TYPE_SUBCLASS = (1L << 31)
-
-MAX_OUTPUT_LEN=1024
+MAX_OUTPUT_LEN = 1024
hexdigits = "0123456789abcdef"
def getvalue(self):
return self._val
+
+# pretty printer lookup
+all_pretty_typenames = set()
+
+class PrettyPrinterTrackerMeta(type):
+
+ def __init__(self, name, bases, dict):
+ super(PrettyPrinterTrackerMeta, self).__init__(name, bases, dict)
+ all_pretty_typenames.add(self._typename)
+
+
class PyObjectPtr(object):
"""
Class wrapping a gdb.Value that's a either a (PyObject*) within the
Note that at every stage the underlying pointer could be NULL, point
to corrupt data, etc; this is the debugger, after all.
"""
+
+ __metaclass__ = PrettyPrinterTrackerMeta
+
_typename = 'PyObject'
-
+
def __init__(self, gdbval, cast_to=None):
if cast_to:
self._gdbval = gdbval.cast(cast_to)
}
if tp_name in name_map:
return name_map[tp_name]
-
+
if tp_flags & Py_TPFLAGS_HEAPTYPE:
return HeapTypeObjectPtr
def as_address(self):
return long(self._gdbval)
+if not isinstance(PyObjectPtr, PrettyPrinterTrackerMeta):
+ # Python 3, ensure metaclass
+ PyObjectPtr = PrettyPrinterTrackerMeta(
+ PyObjectPtr.__name__, PyObjectPtr.__bases__, vars(PyObjectPtr))
+
class PyVarObjectPtr(PyObjectPtr):
_typename = 'PyVarObject'
class HeapTypeObjectPtr(PyObjectPtr):
_typename = 'PyObject'
-
+
def get_attr_dict(self):
'''
Get the PyDictObject ptr representing the attribute dictionary
within the process being debugged.
"""
_typename = 'PyBaseExceptionObject'
-
+
def proxyval(self, visited):
# Guard against infinite loops:
if self.as_address() in visited:
class PyInstanceObjectPtr(PyObjectPtr):
_typename = 'PyInstanceObject'
-
+
def proxyval(self, visited):
# Guard against infinite loops:
if self.as_address() in visited:
class PyListObjectPtr(PyObjectPtr):
_typename = 'PyListObject'
-
+
def __getitem__(self, i):
# Get the gdb.Value for the (PyObject*) with the given index:
field_ob_item = self.field('ob_item')
class PyLongObjectPtr(PyObjectPtr):
_typename = 'PyLongObject'
-
+
def proxyval(self, visited):
'''
Python's Include/longobjrep.h has this declaration:
where SHIFT can be either:
#define PyLong_SHIFT 30
#define PyLong_SHIFT 15
- '''
+ '''
ob_size = long(self.field('ob_size'))
if ob_size == 0:
return 0L
Class wrapping a gdb.Value that's a PyBoolObject* i.e. one of the two
<bool> instances (Py_True/Py_False) within the process being debugged.
"""
+ _typename = 'PyBoolObject'
def proxyval(self, visited):
- return bool(PyLongObjectPtr.proxyval(self, visited))
+ castto = gdb.lookup_type('PyLongObject').pointer()
+ self._gdbval = self._gdbval.cast(castto)
+ return bool(PyLongObjectPtr(self._gdbval).proxyval(visited))
class PyNoneStructPtr(PyObjectPtr):
class PyBytesObjectPtr(PyObjectPtr):
_typename = 'PyBytesObject'
-
+
def __str__(self):
field_ob_size = self.field('ob_size')
field_ob_sval = self.field('ob_sval')
def proxyval(self, visited):
return str(self)
- def write_repr(self, out, visited):
+ def write_repr(self, out, visited, py3=True):
# Write this out as a Python 3 bytes literal, i.e. with a "b" prefix
# Get a PyStringObject* within the Python 2 gdb process:
quote = "'"
if "'" in proxy and not '"' in proxy:
quote = '"'
- out.write('b')
+
+ if py3:
+ out.write('b')
+
out.write(quote)
for byte in proxy:
if byte == quote or byte == '\\':
class PyStringObjectPtr(PyBytesObjectPtr):
_typename = 'PyStringObject'
+ def write_repr(self, out, visited):
+ return super(PyStringObjectPtr, self).write_repr(out, visited, py3=False)
class PyTupleObjectPtr(PyObjectPtr):
_typename = 'PyTupleObject'
return result
def write_repr(self, out, visited):
- # Write this out as a Python 3 str literal, i.e. without a "u" prefix
-
# Get a PyUnicodeObject* within the Python 2 gdb process:
proxy = self.proxyval(visited)
# Transliteration of Python 3's Object/unicodeobject.c:unicode_repr
# to Python 2:
+ try:
+ gdb.parse_and_eval('PyString_Type')
+ except RuntimeError:
+ # Python 3, don't write 'u' as prefix
+ pass
+ else:
+ # Python 2, write the 'u'
+ out.write('u')
+
if "'" in proxy and '"' not in proxy:
quote = '"'
else:
out.write(quote)
-
-
def int_from_int(gdbval):
return int(str(gdbval))
proxyval = pyop.proxyval(set())
return stringify(proxyval)
-
def pretty_printer_lookup(gdbval):
type = gdbval.type.unqualified()
if type.code == gdb.TYPE_CODE_PTR:
type = type.target().unqualified()
- # do this every time to allow new subclasses to "register"
- # alternatively, we could use a metaclass to register all the typenames
- classes = [PyObjectPtr]
- classes.extend(PyObjectPtr.__subclasses__())
- if str(type) in [cls._typename for cls in classes]:
+ if str(type) in all_pretty_typenames:
return PyObjectPtrPrinter(gdbval)
"""
register (gdb.current_objfile ())
-
-
# Unfortunately, the exact API exposed by the gdb module varies somewhat
# from build to build
# See http://bugs.python.org/issue8279?#msg102276
Keep all breakpoints around and simply disable/enable them each time
we are stepping. We need this because if you set and delete a
breakpoint, gdb will not repeat your command (this is due to 'delete').
- Why? I'm buggered if I know. To further annoy us, we can't use the
- breakpoint API because there's no option to make breakpoint setting
- silent.
- So now! We may have an insane amount of breakpoints to list when the
- user does 'info breakpoints' :(
+ We also can't use the breakpoint API because there's no option to make
+ breakpoint setting silent.
This method must be called whenever the list of functions we should
step into changes. It can be called on any GenericCodeStepper instance.
except RuntimeError:
# gdb.Breakpoint does take an 'internal' argument, use it
# and hide output
- result = gdb.execute(
- "python bp = gdb.Breakpoint(%r, gdb.BP_BREAKPOINT, internal=True); "
- "print bp.number",
- to_string=True)
+ result = gdb.execute(textwrap.dedent("""\
+ python bp = gdb.Breakpoint(%r, gdb.BP_BREAKPOINT, \
+ internal=True); \
+ print bp.number""",
+ to_string=True))
breakpoint = int(result)
return pointer
+def get_inferior_unicode_postfix():
+ try:
+ gdb.parse_and_eval('PyUnicode_FromEncodedObject')
+ except RuntimeError:
+ try:
+ gdb.parse_and_eval('PyUnicodeUCS2_FromEncodedObject')
+ except RuntimeError:
+ return 'UCS4'
+ else:
+ return 'UCS2'
+ else:
+ return ''
+
class PythonCodeExecutor(object):
def malloc(self, size):
def alloc_pystring(self, string):
stringp = self.alloc_string(string)
PyString_FromStringAndSize = 'PyString_FromStringAndSize'
+
try:
gdb.parse_and_eval(PyString_FromStringAndSize)
except RuntimeError:
- try:
- gdb.parse_and_eval('PyUnicode_FromStringAndSize')
- except RuntimeError:
- PyString_FromStringAndSize = 'PyUnicodeUCS2_FromStringAndSize'
- else:
- PyString_FromStringAndSize = 'PyUnicode_FromStringAndSize'
-
+ # Python 3
+ PyString_FromStringAndSize = ('PyUnicode%s_FromStringAndSize' %
+ (get_inferior_unicode_postfix,))
+
try:
result = gdb.parse_and_eval(
'(PyObject *) %s((char *) %d, (size_t) %d)' % (
code = """
PyRun_String(
- (PyObject *) %(code)d,
+ (char *) %(code)d,
(int) %(start)d,
(PyObject *) %(globals)s,
(PyObject *) %(locals)d)
executor.evalcode(expr, input_type, global_dict, local_dict)
py_exec = FixGdbCommand('py-exec', '-py-exec')
-_py_exec = PyExec("-py-exec", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
\ No newline at end of file
+_py_exec = PyExec("-py-exec", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)