summaryrefslogtreecommitdiffstats
path: root/test_gdb.py
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2010-02-23 10:48:08 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2010-02-23 10:48:08 -0500
commit49a93802d2013a79f1c63f948c5c781be04fc928 (patch)
treeddd96cce4ea8dd9294ca17dbf39ce6b171b84946 /test_gdb.py
parentde5644a7091197366da12eaafa71f739a20926a9 (diff)
downloadlibpython-49a93802d2013a79f1c63f948c5c781be04fc928.zip
libpython-49a93802d2013a79f1c63f948c5c781be04fc928.tar.gz
libpython-49a93802d2013a79f1c63f948c5c781be04fc928.tar.xz
Start an automated test suite for the gdb pretty-printers
Diffstat (limited to 'test_gdb.py')
-rw-r--r--test_gdb.py143
1 files changed, 143 insertions, 0 deletions
diff --git a/test_gdb.py b/test_gdb.py
new file mode 100644
index 0000000..ee60461
--- /dev/null
+++ b/test_gdb.py
@@ -0,0 +1,143 @@
+# Verify that gdb can pretty-print the various PyObject* types
+#
+# The code for testing gdb was adapted from similar work in Unladen Swallow's
+# Lib/test/test_jit_gdb.py
+
+import os
+import re
+import subprocess
+import sys
+import unittest
+
+from test.test_support import run_unittest, TestSkipped
+
+try:
+ gdb_version, _ = subprocess.Popen(["gdb", "--version"],
+ stdout=subprocess.PIPE).communicate()
+except OSError:
+ # This is what "no gdb" looks like. There may, however, be other
+ # errors that manifest this way too.
+ raise TestSkipped("Couldn't find gdb on the path")
+gdb_version_number = re.search(r"^GNU gdb [^\d]*(\d+)\.", gdb_version)
+if int(gdb_version_number.group(1)) < 7:
+ raise TestSkipped("gdb versions before 7.0 didn't support python embedding."
+ " Saw:\n" + gdb_version)
+
+# TODO: handle gdb 7 configured without python support
+
+class DebuggerTests(unittest.TestCase):
+
+ """Test that the debugger can debug Python."""
+
+ def run_gdb(self, *args):
+ """Runs gdb with the command line given by *args. Returns its stdout.
+
+ Forwards stderr to the current process's stderr.
+ """
+ # err winds up empty.
+ out, err = subprocess.Popen(
+ args, stdout=subprocess.PIPE,
+ stderr=None, # Forward stderr to the current process's stderr.
+ ).communicate()
+ return out
+
+ def get_stack_trace(self, source, breakpoint='PyObject_Print'):
+ '''
+ Run 'python -c SOURCE' under gdb with a breakpoint.
+
+ Returns the stdout from gdb
+ '''
+ # We use "set breakpoint pending yes" to avoid blocking with a:
+ # Function "foo" not defined.
+ # Make breakpoint pending on future shared library load? (y or [n])
+ # error, which typically happens python is dynamically linked (the
+ # breakpoints of interest are to be found in the shared library)
+ # When this happens, we still get:
+ # Function "PyObject_Print" not defined.
+ # emitted to stderr each time, alas.
+
+ # Initially I had "--eval-command=continue" here, but removed it to
+ # avoid repeated print breakpoints when traversing hierarchical data
+ # structures
+ gdb_output = self.run_gdb("gdb", "--batch",
+ "--eval-command=set breakpoint pending yes",
+ "--eval-command=break %s" % breakpoint,
+
+ "--eval-command=run",
+ "--eval-command=backtrace",
+ "--args",
+ sys.executable, "-S", "-c", source)
+ return gdb_output
+
+ def get_gdb_repr(self, in_repr):
+ # Given an input python source representation of data,
+ # run "python -c'print DATA'" under gdb with a breakpoint on
+ # PyObject_Print and scrape out gdb's representation of the "op"
+ # parameter, and verify that the gdb displays the same string
+ #
+ # For a nested structure, the first time we hit the breakpoint will
+ # give us the top-level structure
+ gdb_output = self.get_stack_trace('print ' + in_repr)
+ m = re.match('.*Breakpoint 1, PyObject_Print \(op\=(.*?), fp=.*\).*', gdb_output, re.DOTALL)
+ # print m.groups()
+ return m.group(1), gdb_output
+
+ def test_getting_backtrace(self):
+ gdb_output = self.get_stack_trace('print 42')
+ self.assertTrue('PyObject_Print' in gdb_output)
+
+ def assertGdbRepr(self, val):
+ # Ensure that gdb's rendering of the value in a debugged process
+ # matches repr(value) in this process:
+ gdb_repr, gdb_output = self.get_gdb_repr(repr(val))
+ self.assertEquals(gdb_repr, repr(val), gdb_output)
+
+ def test_int(self):
+ self.assertGdbRepr(42)
+ self.assertGdbRepr(0)
+ self.assertGdbRepr(-7)
+ self.assertGdbRepr(sys.maxint)
+ self.assertGdbRepr(-sys.maxint)
+
+ def test_long(self):
+ self.assertGdbRepr(0L)
+ self.assertGdbRepr(1000000000000L)
+ self.assertGdbRepr(-1L)
+ self.assertGdbRepr(-1000000000000000L)
+
+ def test_singletons(self):
+ self.assertGdbRepr(True)
+ self.assertGdbRepr(False)
+ self.assertGdbRepr(None)
+
+ def test_dicts(self):
+ self.assertGdbRepr({})
+ self.assertGdbRepr({'foo':'bar'})
+
+ def test_lists(self):
+ self.assertGdbRepr([])
+ self.assertGdbRepr(range(5))
+
+ def test_strings(self):
+ self.assertGdbRepr('')
+ self.assertGdbRepr('And now for something hopefully the same')
+
+ def test_tuples(self):
+ self.assertGdbRepr( tuple() )
+ self.assertGdbRepr( (1,) )
+
+ def test_unicode(self):
+ self.assertGdbRepr( u'hello world', )
+ self.assertGdbRepr( u'\u2620')
+
+ # TODO:
+ # old-style classes
+ # new-style classes
+ # frames
+
+def test_main():
+ run_unittest(DebuggerTests)
+
+
+if __name__ == "__main__":
+ test_main()