2010-10-08 Doug Evans 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 8 Oct 2010 23:03:28 -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 8 Oct 2010 23:03:28 -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.767 diff -u -p -r1.767 gdb.texinfo --- doc/gdb.texinfo 28 Sep 2010 21:40:23 -0000 1.767 +++ doc/gdb.texinfo 8 Oct 2010 23:03:28 -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 @@ -20789,7 +20790,7 @@ this value, thus it is not available for @defivar Value type The type of this @code{gdb.Value}. The value of this attribute is a -@code{gdb.Type} object. +@code{gdb.Type} object (@pxref{Types In Python}). @end defivar @defivar Value dynamic_type @@ -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,37 @@ 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. +@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 references converted to the underlying type. + +@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 dictionary made 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 8 Oct 2010 23:03:28 -0000 @@ -0,0 +1,77 @@ +# 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 . + +"""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 + are converted to the original type. + """ + + if type_.code == gdb.TYPE_CODE_REF: + type_ = type_.target() + return type_.unqualified().strip_typedefs() + + +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.""" + + 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 8 Oct 2010 23:03:28 -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 8 Oct 2010 23:03:28 -0000 @@ -0,0 +1,53 @@ +/* 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 . */ + +class class1 +{ + public: + class1 (int _x) : x (_x) {} + int x; +}; + +class1 simple_class1 (42); +const class1 const_class1 (42); +volatile class1 volatile_class1 (42); +const volatile class1 const_volatile_class1 (42); + +typedef class1 class2; + +class2 typedef_class1 (42); + +class1& ref_class1 (simple_class1); + +class subclass1 : public class1 +{ + public: + subclass1 (int _x, int _y) : class1 (_x), y (_y) {} + int y; +}; + +subclass1 simple_subclass1 (42, 43); + +enum enum1 { A, B, C }; + +enum1 simple_enum1 (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 8 Oct 2010 23:03:28 -0000 @@ -0,0 +1,123 @@ +# 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 . + +# 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 = gdb.parse_and_eval ('const_class1')" +gdb_test_no_output "python basic_type_const_class1 = gdb.types.get_basic_type (const_class1.type)" +gdb_test "python print str (const_class1.type)" "const class1" +set test "const stripping" +gdb_test_multiple "python print str (basic_type_const_class1)" $test { + -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" { + pass $test + } +} + +gdb_test "python print str (basic_type_const_class1)" "class1" + +# test get_basic_type volatile stripping +gdb_test_no_output "python volatile_class1 = gdb.parse_and_eval ('volatile_class1')" +gdb_test_no_output "python basic_type_volatile_class1 = gdb.types.get_basic_type (volatile_class1.type)" +gdb_test "python print str (volatile_class1.type)" "volatile class1" +set test "volatile stripping" +gdb_test_multiple "python print str (basic_type_volatile_class1)" $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 = gdb.parse_and_eval ('const_volatile_class1')" +gdb_test_no_output "python basic_type_const_volatile_class1 = gdb.types.get_basic_type (const_volatile_class1.type)" +gdb_test "python print str (const_volatile_class1.type)" "const volatile class1" +set test "volatile+const stripping" +gdb_test_multiple "python print str (basic_type_const_volatile_class1)" $test { + -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" { + pass $test + } +} + +# test get_basic_type typedef stripping +gdb_test_no_output "python typedef_class1 = gdb.parse_and_eval ('typedef_class1')" +gdb_test_no_output "python basic_type_typedef_class1 = gdb.types.get_basic_type (typedef_class1.type)" +gdb_test "python print str (typedef_class1.type)" "class2" +set test "typedef stripping" +gdb_test_multiple "python print str (basic_type_typedef_class1)" $test { + -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" { + pass $test + } +} + +# test get_basic_type reference stripping +gdb_test_no_output "python ref_class1 = gdb.parse_and_eval ('ref_class1')" +gdb_test_no_output "python basic_type_ref_class1 = gdb.types.get_basic_type (ref_class1.type)" +gdb_test "python print str (ref_class1.type)" "class1 &" +set test "reference stripping" +gdb_test_multiple "python print str (basic_type_ref_class1)" $test { + -re "\[\r\n\]+class1\[\r\n\]+$gdb_prompt $" { + pass $test + } +} + +# test has_field on simple class +gdb_test_no_output "python simple_class1 = gdb.parse_and_eval ('simple_class1')" +gdb_test "python print gdb.types.has_field (simple_class1.type, 'x')" "True" +gdb_test "python print gdb.types.has_field (simple_class1.type, 'nope')" "False" + +# test has_field in base class +gdb_test_no_output "python simple_subclass1 = gdb.parse_and_eval ('simple_subclass1')" +gdb_test "python print gdb.types.has_field (simple_class1.type, 'x')" "True" + +# test make_enum_dict +gdb_test_no_output "python simple_enum1 = gdb.parse_and_eval ('simple_enum1')" +gdb_test_no_output "python enum1_dict = gdb.types.make_enum_dict (simple_enum1.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 8 Oct 2010 23:03:28 -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')" + } +}