From 0e3509dbced8d51a4293ab311eac20a2aee8cfdf Mon Sep 17 00:00:00 2001 From: Doug Evans Date: Wed, 13 Oct 2010 20:08:46 +0000 Subject: [PATCH] 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. --- gdb/ChangeLog | 5 + gdb/NEWS | 4 + gdb/data-directory/Makefile.in | 3 +- gdb/doc/ChangeLog | 6 ++ gdb/doc/gdb.texinfo | 54 ++++++++++ gdb/python/lib/gdb/types.py | 91 ++++++++++++++++ gdb/testsuite/ChangeLog | 7 ++ gdb/testsuite/gdb.python/Makefile.in | 2 +- gdb/testsuite/gdb.python/lib-types.cc | 61 +++++++++++ gdb/testsuite/gdb.python/lib-types.exp | 143 +++++++++++++++++++++++++ gdb/testsuite/lib/gdb-python.exp | 18 ++++ 11 files changed, 392 insertions(+), 2 deletions(-) create mode 100644 gdb/python/lib/gdb/types.py create mode 100644 gdb/testsuite/gdb.python/lib-types.cc create mode 100644 gdb/testsuite/gdb.python/lib-types.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index de94202d85d..b5886ddf250 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -4,6 +4,11 @@ 2010-10-13 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. + * c-typeprint.c: Whitespace cleanup. (cp_type_print_method_args): Remove unnecessary forward decl. (cp_type_print_derivation_info): Ditto. diff --git a/gdb/NEWS b/gdb/NEWS index 85059c617d2..033d2fe4a58 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -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 diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 311915a4b53..e60bc59d497 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -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 diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index e2b435e6cc0..c2e45ae7002 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2010-10-13 Doug Evans + + * gdb.texinfo (Python): Add "Python modules". + (Types in Python): Add reference to gdb.types section. + (Python modules): New node. + 2010-10-11 Doug Evans * gdb.texinfo (Values From Inferior): Add reference to "Types in diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index c73e2f3f640..944693256a2 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -20401,6 +20401,7 @@ the Python interpreter to locate all scripts installed at this location. * 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}. This is the type used to represent 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 the scripts in the 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@t{++} references converted to the underlying type. + +C@t{++} 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 diff --git a/gdb/python/lib/gdb/types.py b/gdb/python/lib/gdb/types.py new file mode 100644 index 00000000000..e45ea4c09ad --- /dev/null +++ b/gdb/python/lib/gdb/types.py @@ -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 . + +"""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 diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index bc3b2fc801f..37bf7c94694 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2010-10-13 Doug Evans + + * 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. + 2010-10-13 Jan Kratochvil * gdb.python/py-error.exp: New file. diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in index aaa771a6779..ed941900516 100644 --- a/gdb/testsuite/gdb.python/Makefile.in +++ b/gdb/testsuite/gdb.python/Makefile.in @@ -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 diff --git a/gdb/testsuite/gdb.python/lib-types.cc b/gdb/testsuite/gdb.python/lib-types.cc new file mode 100644 index 00000000000..fff19faa08b --- /dev/null +++ b/gdb/testsuite/gdb.python/lib-types.cc @@ -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 . */ + +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; +} diff --git a/gdb/testsuite/gdb.python/lib-types.exp b/gdb/testsuite/gdb.python/lib-types.exp new file mode 100644 index 00000000000..0599b0da235 --- /dev/null +++ b/gdb/testsuite/gdb.python/lib-types.exp @@ -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 . + +# 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\)\]} diff --git a/gdb/testsuite/lib/gdb-python.exp b/gdb/testsuite/lib/gdb-python.exp index 4a15d7a71a7..58deadc2cfe 100644 --- a/gdb/testsuite/lib/gdb-python.exp +++ b/gdb/testsuite/lib/gdb-python.exp @@ -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')" + } +} -- 2.30.2