[RFA] New python module gdb.types

Doug Evans dje@google.com
Wed Oct 13 18:42:00 GMT 2010


On Sat, Oct 9, 2010 at 2:14 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>> Date: Fri, 8 Oct 2010 15:18:45 -0700
>> From: Doug Evans <dje@google.com>
>> Cc: gdb-patches@sourceware.org
>>
>> Return @var{type} with const and volatile qualifiers stripped,
>> and with typedefs and references converted to the underlying type.
>
> If the conversion of typedefs is recursive, then I think "to the
> underlying primitive data type" is more accurate.
>
> This takes care of typedefs.  I'm still unclear regarding the
> "references" part.  What does it mean? removing the "pointer to" part,
> like in "int *" -> "int"?
>
> I still maintain that examples go a long way towards explaining such
> abstract descriptions.
>
>> >> +@item make_enum_dict (@var{enum_type})
>> >> +Return a dictionary made from @var{enum_type}.
>> >
>> > Same here: at the very least we should say what is a "dictionary" in
>> > this context.
>>
>> The context is python and in the python context "dictionary" is quite
>> specific, it's a builtin type.
>
> Well, you used it as a simple word, which makes it hard to guess to
> someone like me, for whom Python is a read-only language.
>
>> What if it said the following instead?
>>
>> Return a python dictionary made from @var{enum_type}.
>
> How about
>
>  Return a Python @code{dictionary} type produced from @var{enum_type}.

How about this?

2010-10-13  Doug Evans  <dje@google.com>

        New python module gdb.types.
        * NEWS: Document it.
        * data-directory/Makefile.in (PYTHON_FILES): Add gdb/types.py.
        * python/lib/gdb/types.py: New file.

        testsuite/
        * lib/gdb-python.exp (gdb_check_python_config): New function.
        * gdb.python/Makefile.in (EXECUTABLES): Add lib-types.
        * gdb.python/lib-types.cc: New file.
        * gdb.python/lib-types.exp: New file.

        doc/
        * gdb.texinfo (Python): Add "Python modules".
        (Types in Python): Add reference to gdb.types section.
        (Python modules): New node.
-------------- next part --------------
2010-10-13  Doug Evans  <dje@google.com>

	New python module gdb.types.
	* NEWS: Document it.
	* data-directory/Makefile.in (PYTHON_FILES): Add gdb/types.py.
	* python/lib/gdb/types.py: New file.

	testsuite/
	* lib/gdb-python.exp (gdb_check_python_config): New function.
	* gdb.python/Makefile.in (EXECUTABLES): Add lib-types.
	* gdb.python/lib-types.cc: New file.
	* gdb.python/lib-types.exp: New file.

	doc/
	* gdb.texinfo (Python): Add "Python modules".
	(Types in Python): Add reference to gdb.types section.
	(Python modules): New node.

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.406
diff -u -p -r1.406 NEWS
--- NEWS	28 Sep 2010 21:40:23 -0000	1.406
+++ NEWS	13 Oct 2010 18:26:34 -0000
@@ -12,6 +12,10 @@
 
      result = some_value (10,20)
 
+  ** Module gdb.types has been added.
+     It contains a collection of utilities for working with gdb.Types objects:
+     get_basic_type, has_field, make_enum_dict.
+
 * C++ Improvements:
 
   ** GDB now puts template parameters in scope when debugging in an
Index: data-directory/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/data-directory/Makefile.in,v
retrieving revision 1.2
diff -u -p -r1.2 Makefile.in
--- data-directory/Makefile.in	7 Oct 2010 00:18:22 -0000	1.2
+++ data-directory/Makefile.in	13 Oct 2010 18:26:34 -0000
@@ -51,7 +51,8 @@ SYSCALLS_FILES = \
 PYTHON_DIR = python
 PYTHON_INSTALL_DIR = $(DESTDIR)/$(GDB_DATADIR)/$(PYTHON_DIR)
 PYTHON_FILES = \
-	gdb/__init__.py
+	gdb/__init__.py \
+	gdb/types.py
 
 .PHONY: all
 all: stamp-syscalls stamp-python
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.768
diff -u -p -r1.768 gdb.texinfo
--- doc/gdb.texinfo	11 Oct 2010 15:46:09 -0000	1.768
+++ doc/gdb.texinfo	13 Oct 2010 18:26:34 -0000
@@ -20401,6 +20401,7 @@ the Python interpreter to locate all scr
 * Python Commands::             Accessing Python from @value{GDBN}.
 * Python API::                  Accessing @value{GDBN} from Python.
 * Auto-loading::                Automatically loading Python code.
+* Python modules::              Python modules provided by @value{GDBN}.
 @end menu
 
 @node Python Commands
@@ -21245,6 +21246,9 @@ A function internal to @value{GDBN}.  Th
 convenience functions.
 @end table
 
+Further support for types is provided in the @code{gdb.types}
+Python module (@pxref{gdb.types}).
+
 @node Pretty Printing API
 @subsubsection Pretty Printing API
 
@@ -22911,6 +22915,56 @@ cumbersome.  It may be easier to specify
 top of the source tree to the source search path.
 @end itemize
 
+@node Python modules
+@subsection Python modules
+@cindex python modules
+
+@c It is assumed that at least one more module will be added before
+@c the next release of gdb.  Thus we use a menu here.
+@value{GDBN} comes with a module to assist writing Python code.
+
+@menu
+* gdb.types::          Utilities for working with types.
+@end menu
+
+@node gdb.types
+@subsubsection gdb.types
+
+This module provides a collection of utilities for working with
+@code{gdb.Types} objects.
+
+@table @code
+@item get_basic_type (@var{type})
+Return @var{type} with const and volatile qualifiers stripped,
+and with typedefs and C++ references converted to the underlying type.
+
+C++ example:
+
+@smallexample
+typedef const int const_int;
+const_int foo (3);
+const_int& foo_ref (foo);
+int main () { return 0; }
+@end smallexample
+
+Then in gdb:
+
+@smallexample
+(gdb) start
+(gdb) python import gdb.types
+(gdb) python foo_ref = gdb.parse_and_eval("foo_ref")
+(gdb) python print gdb.types.get_basic_type(foo_ref.type)
+int
+@end smallexample
+
+@item has_field (@var{type}, @var{field})
+Return @code{True} if @var{type}, assumed to be a type with fields
+(e.g., a structure or union), has field @var{field}.
+
+@item make_enum_dict (@var{enum_type})
+Return a Python @code{dictionary} type produced from @var{enum_type}.
+@end table
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
Index: python/lib/gdb/types.py
===================================================================
RCS file: python/lib/gdb/types.py
diff -N python/lib/gdb/types.py
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ python/lib/gdb/types.py	13 Oct 2010 18:40:00 -0000
@@ -0,0 +1,91 @@
+# Type utilities.
+# Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Utilities for working with gdb.Types."""
+
+import gdb
+
+
+def get_basic_type(type_):
+    """Return the "basic" type of a type.
+
+    Arguments:
+        type_: The type to reduce to its basic type.
+
+    Returns:
+        type_ with const/volatile is stripped away,
+        and typedefs/references converted to the underlying type.
+    """
+
+    while (type_.code == gdb.TYPE_CODE_REF or
+           type_.code == gdb.TYPE_CODE_TYPEDEF):
+        if type_.code == gdb.TYPE_CODE_REF:
+            type_ = type_.target()
+        else:
+            type_ = type_.strip_typedefs()
+    return type_.unqualified()
+
+
+def has_field(type_, field):
+    """Return True if a type has the specified field.
+
+    Arguments:
+        type_: The type to examine.
+            It must be one of gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION.
+        field: The name of the field to look up.
+
+    Returns:
+        True if the field is present either in type_ or any baseclass.
+
+    Raises:
+        TypeError: The type is not a struct or union.
+    """
+
+    type_ = get_basic_type(type_)
+    if (type_.code != gdb.TYPE_CODE_STRUCT and
+        type_.code != gdb.TYPE_CODE_UNION):
+        raise TypeError("not a struct or union")
+    for f in type_.fields():
+        if f.is_base_class:
+            if has_field(f.type, field):
+                return True
+        else:
+            # NOTE: f.name could be None
+            if f.name == field:
+                return True
+    return False
+
+
+def make_enum_dict(enum_type):
+    """Return a dictionary from a program's enum type.
+
+    Arguments:
+        enum_type: The enum to compute the dictionary for.
+
+    Returns:
+        The dictionary of the enum.
+
+    Raises:
+        TypeError: The type is not an enum.
+    """
+
+    if enum_type.code != gdb.TYPE_CODE_ENUM:
+        raise TypeError("not an enum type")
+    enum_dict = {}
+    for field in enum_type.fields():
+        # The enum's value is stored in "bitpos".
+        enum_dict[field.name] = field.bitpos
+    return enum_dict
Index: testsuite/gdb.python/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/Makefile.in,v
retrieving revision 1.8
diff -u -p -r1.8 Makefile.in
--- testsuite/gdb.python/Makefile.in	19 Aug 2010 17:00:57 -0000	1.8
+++ testsuite/gdb.python/Makefile.in	13 Oct 2010 18:26:35 -0000
@@ -3,7 +3,7 @@ srcdir = @srcdir@
 
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
 		py-symbol py-mi py-breakpoint py-inferior py-infthread \
-		py-shared python
+		py-shared python lib-types
 
 MISCELLANEOUS = py-shared-sl.sl
 
Index: testsuite/gdb.python/lib-types.cc
===================================================================
RCS file: testsuite/gdb.python/lib-types.cc
diff -N testsuite/gdb.python/lib-types.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.python/lib-types.cc	13 Oct 2010 18:26:35 -0000
@@ -0,0 +1,61 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2010 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+class class1
+{
+ public:
+  class1 (int _x) : x (_x) {}
+  int x;
+};
+
+class1 class1_obj (42);
+const class1 const_class1_obj (42);
+volatile class1 volatile_class1_obj (42);
+const volatile class1 const_volatile_class1_obj (42);
+
+typedef class1 typedef_class1;
+
+typedef_class1 typedef_class1_obj (42);
+
+class1& class1_ref_obj (class1_obj);
+
+typedef const typedef_class1 typedef_const_typedef_class1;
+
+typedef_const_typedef_class1 typedef_const_typedef_class1_obj (42);
+
+typedef typedef_const_typedef_class1& typedef_const_typedef_class1_ref;
+
+typedef_const_typedef_class1_ref typedef_const_typedef_class1_ref_obj (typedef_const_typedef_class1_obj);
+
+class subclass1 : public class1
+{
+ public:
+  subclass1 (int _x, int _y) : class1 (_x), y (_y) {}
+  int y;
+};
+
+subclass1 subclass1_obj (42, 43);
+
+enum enum1 { A, B, C };
+
+enum1 enum1_obj (A);
+
+int
+main ()
+{
+  return 0;
+}
Index: testsuite/gdb.python/lib-types.exp
===================================================================
RCS file: testsuite/gdb.python/lib-types.exp
diff -N testsuite/gdb.python/lib-types.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.python/lib-types.exp	13 Oct 2010 18:26:35 -0000
@@ -0,0 +1,143 @@
+# Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.
+# It tests the types.py module.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "lib-types"
+set srcfile ${testfile}.cc
+set binfile ${objdir}/${subdir}/${testfile}
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+# Ensure sys.path, et.al. are initialized properly.
+gdb_check_python_config
+
+gdb_test_no_output "python import gdb.types"
+
+# test get_basic_type const stripping
+gdb_test_no_output "python const_class1_obj = gdb.parse_and_eval ('const_class1_obj')"
+gdb_test_no_output "python basic_type_const_class1_obj = gdb.types.get_basic_type (const_class1_obj.type)"
+gdb_test "python print str (const_class1_obj.type)" "const class1"
+set test "const stripping"
+gdb_test_multiple "python print str (basic_type_const_class1_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test get_basic_type volatile stripping
+gdb_test_no_output "python volatile_class1_obj = gdb.parse_and_eval ('volatile_class1_obj')"
+gdb_test_no_output "python basic_type_volatile_class1_obj = gdb.types.get_basic_type (volatile_class1_obj.type)"
+gdb_test "python print str (volatile_class1_obj.type)" "volatile class1"
+set test "volatile stripping"
+gdb_test_multiple "python print str (basic_type_volatile_class1_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test get_basic_type volatile+const stripping
+gdb_test_no_output "python const_volatile_class1_obj = gdb.parse_and_eval ('const_volatile_class1_obj')"
+gdb_test_no_output "python basic_type_const_volatile_class1_obj = gdb.types.get_basic_type (const_volatile_class1_obj.type)"
+gdb_test "python print str (const_volatile_class1_obj.type)" "const volatile class1"
+set test "volatile+const stripping"
+gdb_test_multiple "python print str (basic_type_const_volatile_class1_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test get_basic_type typedef stripping
+gdb_test_no_output "python typedef_class1_obj = gdb.parse_and_eval ('typedef_class1_obj')"
+gdb_test_no_output "python basic_type_typedef_class1_obj = gdb.types.get_basic_type (typedef_class1_obj.type)"
+gdb_test "python print str (typedef_class1_obj.type)" "typedef_class1"
+set test "typedef stripping"
+gdb_test_multiple "python print str (basic_type_typedef_class1_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test get_basic_type reference stripping
+gdb_test_no_output "python class1_ref_obj = gdb.parse_and_eval ('class1_ref_obj')"
+gdb_test_no_output "python basic_type_class1_ref_obj = gdb.types.get_basic_type (class1_ref_obj.type)"
+gdb_test "python print str (class1_ref_obj.type)" "class1 &"
+set test "reference stripping"
+gdb_test_multiple "python print str (basic_type_class1_ref_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test nested typedef stripping
+gdb_test_no_output "python typedef_const_typedef_class1_obj = gdb.parse_and_eval ('typedef_const_typedef_class1_obj')"
+gdb_test_no_output "python basic_type_typedef_const_typedef_class1_obj = gdb.types.get_basic_type (typedef_const_typedef_class1_obj.type)"
+gdb_test "python print str (typedef_class1_obj.type)" "typedef_class1"
+set test "nested typedef stripping"
+gdb_test_multiple "python print str (basic_type_typedef_const_typedef_class1_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test nested typedef/reference stripping
+gdb_test_no_output "python typedef_const_typedef_class1_ref_obj = gdb.parse_and_eval ('typedef_const_typedef_class1_ref_obj')"
+gdb_test_no_output "python basic_type_typedef_const_typedef_class1_ref_obj = gdb.types.get_basic_type (typedef_const_typedef_class1_ref_obj.type)"
+gdb_test "python print str (typedef_const_typedef_class1_ref_obj.type)" "const typedef_const_typedef_class1_ref"
+set test "nested typedef/ref stripping"
+gdb_test_multiple "python print str (basic_type_typedef_const_typedef_class1_ref_obj)" $test {
+    -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# test has_field on simple class
+gdb_test_no_output "python class1_obj = gdb.parse_and_eval ('class1_obj')"
+gdb_test "python print gdb.types.has_field (class1_obj.type, 'x')" "True"
+gdb_test "python print gdb.types.has_field (class1_obj.type, 'nope')" "False"
+
+# test has_field in base class
+gdb_test_no_output "python subclass1_obj = gdb.parse_and_eval ('subclass1_obj')"
+gdb_test "python print gdb.types.has_field (subclass1_obj.type, 'x')" "True"
+
+# test make_enum_dict
+gdb_test_no_output "python enum1_obj = gdb.parse_and_eval ('enum1_obj')"
+gdb_test_no_output "python enum1_dict = gdb.types.make_enum_dict (enum1_obj.type)"
+gdb_test_no_output "python enum1_list = enum1_dict.items ()"
+gdb_test_no_output "python enum1_list.sort ()"
+gdb_test "python print enum1_list" {\[\('A', 0L\), \('B', 1L\), \('C', 2L\)\]}
Index: testsuite/lib/gdb-python.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/lib/gdb-python.exp,v
retrieving revision 1.1
diff -u -p -r1.1 gdb-python.exp
--- testsuite/lib/gdb-python.exp	1 Oct 2010 17:03:50 -0000	1.1
+++ testsuite/lib/gdb-python.exp	13 Oct 2010 18:26:35 -0000
@@ -45,3 +45,21 @@ proc gdb_py_test_multiple { name args } 
     }
     return 0
 }
+
+# Establish various python configuration parameters if necessary.
+# E.g. sys.path.
+
+proc gdb_check_python_config { } {
+    global USE_INSTALLED_TREE
+    # If we're running an installed version of gdb, and we want to test the
+    # installed versions of the python support scripts, then we don't want
+    # to point data-directory at the build tree.
+    if { [info exists USE_INSTALLED_TREE] && "$USE_INSTALLED_TREE" == "yes" } {
+	verbose -log "Assuming system config already installed."
+    } else {
+	verbose -log "Installing system config from build tree."
+	set gdb_data_dir "[pwd]/../data-directory"
+	gdb_test_no_output "set data-directory $gdb_data_dir"
+	gdb_test_no_output "python GdbSetPythonDirectory ('$gdb_data_dir/python')"
+    }
+}


More information about the Gdb-patches mailing list