[RFC] A new command 'explore'

Siva Chandra sivachandra@google.com
Tue Dec 27 06:29:00 GMT 2011


Hello again,

I have updated the patch based on feedback from Tom and Joel. The new
version is attached. As I have said earlier, I will add documentation
to gdb/doc/gdb.texinfo once the basic functionality and feature set is
approved.

2011-12-27 Siva Chandra <sivachandra@google.com>

        New command 'explore' which helps explore values in scope.
        * data-directory/Makefile.in: Add gdb/command/explore.py
        * python/lib/gdb/command/explore.py: Implemention of the 'explore'
        command using the GDB Python API.
        * testsuite/gdb.python/Makefile.in: Add py-explore to EXECUTABLES
        * testsuite/gdb.python/py-explore.c: C program used for testing
        the new 'explore' command.
        * testsuite/gdb-python/py-explore.exp: Tests for the new 'explore'
        command.

Thanks,
Siva Chandra

On Tue, Dec 20, 2011 at 12:30 AM, Siva Chandra <sivachandra@google.com> wrote:
> Hello,
>
> Attached is a patch which implements a new command 'explore' using the
> GDB Python API. The idea behind this 'explore' command is as follows:
> Often, when exploring a new code base which has complicated data
> structures, we would like to explore the data structure values
> top-down. For example, if a struct is part of a struct/union is part
> of another struct/union, then we typically explore the top-level
> struct/union in the first pass. In subsequent passes, we might want to
> go deeper by exploring the structs/unions embedded in the top level
> struct. We might infact want to explore all the way up to the leaf
> values. The ‘explore’ command enables a user to do such a top-down
> exploration of data structures interactively.
>
> I will add the documentation to gdb/doc/gdb.texinfo once the basics of
> this patch are approved.
>
> 2011-12-19 Siva Chandra <sivachandra@google.com>
>
>        New command 'explore' which helps explore values in scope.
>        * data-directory/Makefile.in: Add gdb/command/explore.py
>        * python/lib/gdb/command/explore.py: Implemention of the 'explore'
>        command using the GDB Python API.
>        * testsuite/gdb.python/Makefile.in: Add py-explore to EXECUTABLES
>        * testsuite/gdb.python/py-explore.c: C program used for testing
>        the new 'explore' command.
>        * testsuite/gdb-python/py-explore.exp: Tests for the new 'explore'
>        command.
>
> Thanks,
> Siva Chandra
-------------- next part --------------
diff -rupN src/gdb/data-directory/Makefile.in src_local/gdb/data-directory/Makefile.in
--- src/gdb/data-directory/Makefile.in	2011-08-17 16:11:20.000000000 +0530
+++ src_local/gdb/data-directory/Makefile.in	2011-11-12 12:07:34.802506703 +0530
@@ -58,7 +58,8 @@ PYTHON_FILES = \
 	gdb/prompt.py \
 	gdb/command/__init__.py \
 	gdb/command/pretty_printers.py \
-	gdb/command/prompt.py
+	gdb/command/prompt.py \
+	gdb/command/explore.py
 
 FLAGS_TO_PASS = \
 	"prefix=$(prefix)" \
diff -rupN src/gdb/python/lib/gdb/command/explore.py src_local/gdb/python/lib/gdb/command/explore.py
--- src/gdb/python/lib/gdb/command/explore.py	1970-01-01 05:30:00.000000000 +0530
+++ src_local/gdb/python/lib/gdb/command/explore.py	2011-12-27 10:45:00.090174863 +0530
@@ -0,0 +1,693 @@
+# GDB 'explore' command.
+# Copyright (C) 2011 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/>.
+
+"""Implementation of the GDB 'explore' command using the GDB Python API."""
+
+import gdb
+
+class Explorer(object):
+    """Internal class which invokes other explorers."""
+
+    # This map is filled by the Explorer.init_env() function
+    type_code_to_explorer_map = { }
+
+    _SCALAR_TYPE_LIST = (
+        gdb.TYPE_CODE_CHAR,
+        gdb.TYPE_CODE_INT,
+        gdb.TYPE_CODE_BOOL,
+        gdb.TYPE_CODE_FLT,
+        gdb.TYPE_CODE_VOID,
+        gdb.TYPE_CODE_ENUM,
+    )
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        """Main function to explore an expression value.
+
+        Arguments:
+            expr: The expression string that is being explored.
+            value: The gdb.Value value of the expression.
+            is_child: Boolean value to indicate if the expression is a child.
+                      An expression is a child if it is derived from the main
+                      expression entered by the user. For example, if the user
+                      entered an expression which evaluates to a struct, then
+                      when exploring the fields of the struct, is_child is set
+                      to True internally.
+
+        Returns:
+            No return value.
+        """
+        type_code = value.type.code
+        if type_code in Explorer.type_code_to_explorer_map:
+            if expr[0] == '*':
+                expr = "(%s)" % expr
+            explorer_class = Explorer.type_code_to_explorer_map[type_code]
+            while explorer_class.explore_expr(expr, value, is_child):
+                pass
+        else:
+            print ("Explorer for type '%s' not yet available.\n" %
+                   str(value.type))
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        """Main function to explore a data type.
+
+        Arguments:
+            name: The string representing the path to the data type being
+                  explored.
+            datatype: The gdb.Type value of the data type being explored.
+            is_child: Boolean value to indicate if the name is a child.
+                      A name is a child if it is derived from the main name
+                      entered by the user. For example, if the user entered the
+                      name of struct type, then when exploring the fields of the
+                      struct, is_child is set to True internally.
+
+        Returns:
+            No return value.
+        """
+        type_code = datatype.code
+        if type_code in Explorer.type_code_to_explorer_map:
+            explorer_class = Explorer.type_code_to_explorer_map[type_code]
+            while explorer_class.explore_type(name, datatype, is_child):
+                pass
+        else:
+            print ("Explorer for type '%s' not yet available.\n" %
+                   str(datatype))
+
+    @staticmethod
+    def init_env():
+        """Initializes the Explorer environment.
+        This function should be invoked before starting any exploration. If
+        invoked before an exploration, it need not be invoked for subsequent
+        explorations.
+        """
+        Explorer.type_code_to_explorer_map = {
+            gdb.TYPE_CODE_CHAR : ScalarExplorer,
+            gdb.TYPE_CODE_INT : ScalarExplorer,
+            gdb.TYPE_CODE_BOOL : ScalarExplorer,
+            gdb.TYPE_CODE_FLT : ScalarExplorer,
+            gdb.TYPE_CODE_VOID : ScalarExplorer,
+            gdb.TYPE_CODE_ENUM : ScalarExplorer,
+            gdb.TYPE_CODE_STRUCT : CompoundExplorer,
+            gdb.TYPE_CODE_UNION : CompoundExplorer,
+            gdb.TYPE_CODE_PTR : PointerExplorer,
+            gdb.TYPE_CODE_TYPEDEF : TypedefExplorer,
+            gdb.TYPE_CODE_ARRAY : ArrayExplorer
+        }
+
+    @staticmethod
+    def is_scalar_type(type):
+        """Checks whether a type is a scalar type.
+        A type is a scalar type of its type is
+            gdb.TYPE_CODE_CHAR or
+            gdb.TYPE_CODE_INT or
+            gdb.TYPE_CODE_BOOL or
+            gdb.TYPE_CODE_FLT or
+            gdb.TYPE_CODE_VOID or
+            gdb.TYPE_CODE_ENUM.
+
+        Arguments:
+            type: The type to be checked.
+
+        Returns:
+            'True' if 'type' is a scalar type. 'False' otherwise.
+        """
+        return type.code in Explorer._SCALAR_TYPE_LIST
+
+    @staticmethod
+    def returning_to_parent_value_message():
+        """A utility function which prints that the current exploration session
+        is returning to the parent value. Useful when exploring values.
+        """
+        print "\nReturning to parent value...\n"
+        
+    @staticmethod
+    def return_to_parent_value_prompt():
+        """A utility function which prompts the user to press the 'enter' key so
+        that the exploration session can shift back to the parent value. Useful
+        when exploring values.
+        """
+        raw_input("\nPress enter to return to parent value...")
+        Explorer.returning_to_parent_value_message()
+        
+    @staticmethod
+    def returning_to_enclosing_type_message():
+        """A utility function which prints that the current exploration session
+        is returning to the enclosing type. Useful when exploring types.
+        """
+        print "\nReturning to enclosing type...\n"
+        
+    @staticmethod
+    def return_to_enclosing_type_prompt():
+        """A utility function which prompts the user to press the 'enter' key so
+        that the exploration session can shift back to the enclosing type.
+        Useful when exploring types.
+        """
+        raw_input("\nPress enter to return to enclosing type...")
+        Explorer.returning_to_enclosing_type_message()
+
+
+class ScalarExplorer(object):
+    """Internal class used to explore scalar values."""
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        """Function to explore scalar values.
+        See Explorer.explore_expr and Explorer.is_scalar_type for more
+        information.
+        """
+        print ("'%s' is a scalar value of type '%s'.\n" %
+               (expr, value.type))
+        print "  %s = %s" % (expr, str(value))
+
+        if is_child:
+            Explorer.return_to_parent_value_prompt()
+
+        return False
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        """Function to explore scalar types.
+        See Explorer.explore_type and Explorer.is_scalar_type for more
+        information.
+        """
+        if datatype.code == gdb.TYPE_CODE_ENUM:
+            if is_child:
+                print ("%s is of an enumerated type '%s'." %
+                       (name, str(datatype)))
+            else:
+                print "%s is an enumerated type." % name
+        else:
+            if is_child:
+                print ("%s is of a primitive scalar type '%s'." %
+                       (name, str(datatype)))
+            else:
+                print "%s is a primitive scalar type." % name
+
+        if is_child:
+            Explorer.return_to_enclosing_type_prompt()
+
+        return False
+
+
+class PointerExplorer(object):
+    """Internal class used to explore pointer values."""
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        """Function to explore pointer values.
+        See Explorer.explore_expr for more information.
+        """
+        if str(value) == "0x0":
+            print "\n  %s = NULL" % expr
+            if is_child:
+                Explorer.return_to_parent_value_prompt()
+            return False
+
+        print ("\n'%s' is a pointer to a value of type '%s'" %
+               (expr, str(value.type.target())))
+        option  = raw_input("\nEnter 's' to continue exploring as a pointer "
+                            "to a single value, 'a' to explore as an array: ")
+        if option == "s":
+            deref_value = None
+            try:
+                deref_value = value.dereference()
+                str(deref_value)
+            except gdb.MemoryError:
+                print ("'%s' a pointer pointing to an invalid memory "
+                       "location." % expr)
+                if is_child:
+                    Explorer.return_to_parent_value_prompt()
+                return False
+            Explorer.explore_expr("*(%s)" % expr, deref_value, is_child)
+        if option == "a":
+            while True:
+                index = 0
+                try:
+                    index = int(raw_input("Enter the index to explore: "))
+                except ValueError:
+                    break
+                element_expr = "%s[%d]" % (expr, index)
+                element = gdb.parse_and_eval(element_expr)
+                try:
+                    str(element)
+                except gdb.MemoryError:
+                    print "Cannot read value at index %d." % index
+                    continue
+                Explorer.explore_expr(element_expr, element, True)
+        if option != "s" and option != "a":    
+            if is_child:
+                Explorer.returning_to_parent_value_message()
+
+        return False
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        """Function to explore pointer types.
+        See Explorer.explore_type for more information.
+        """
+        target_type = datatype.target()
+        print ("\n'%s' is of a pointer type with target type '%s'." %
+               (name, str(target_type)))
+
+        Explorer.explore_type("the value pointed to by %s" % name,
+                              target_type,
+                              is_child)
+        return False
+
+
+class ArrayExplorer(object):
+    """Internal class used to explore arrays."""
+
+    @staticmethod
+    def _print_array_info(name, target_type):
+        """Internal function which prints the information about the array type.
+        """
+        print ("\n%s is an array of elements of type '%s'." %
+               (name, target_type))
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        """Function to explore array values.
+        See Explorer.explore_expr for more information.
+        """
+        ArrayExplorer._print_array_info("'%s'" % expr, value.type.target())
+        index = 0
+        try:
+            index = int(raw_input("Enter the index to explore: "))
+        except ValueError:
+            if is_child:
+                Explorer.returning_to_parent_value_message()
+            return False
+
+        element = None
+        try:
+            element = value[index]
+            str(element)
+        except gdb.MemoryError:
+            print "Cannot read value at index %d." % index
+            raw_input("Press enter to continue... ")
+            return True
+            
+        Explorer.explore_expr("%s[%s]" % (expr, index), element, True)
+        return True
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        """Function to explore array types.
+        See Explorer.explore_type for more information.
+        """
+        target_type = datatype.target()
+        ArrayExplorer._print_array_info(name, target_type)
+
+        entry = raw_input("Enter 's' to step in to exploring the "
+                          "type of the elements: ")
+        if entry == "s":
+            Explorer.explore_type("an array element of %s" % name,
+                                  target_type,
+                                  True)
+        else:
+            if is_child:
+                Explorer.returning_to_enclosing_type_message()
+            return False
+
+        return True
+
+
+class CompoundExplorer(object):
+    """Internal class used to explore struct, classes and unions."""
+
+    _VALUES = 0
+    _TYPES  = 1
+
+    @staticmethod
+    def _print_fields(print_list, print_type):
+        """Internal function which prints the fields of a struct/class/union."""
+        max_type_name_length = 0
+        max_field_name_length = 0
+        for triple in print_list:
+            if max_type_name_length < len(triple[0]):
+                max_type_name_length = len(triple[0])
+            if max_field_name_length < len(triple[1]):
+                max_field_name_length = len(triple[1])
+
+        format_str = ""
+        if print_type == CompoundExplorer._VALUES:
+            format_str = ("  {0:>%d} {1:<%d} = {2}" %
+                          (max_type_name_length, max_field_name_length))
+        if print_type == CompoundExplorer._TYPES:
+            format_str = ("  {0:>%d} {1:<%d} // {2}" %
+                          (max_type_name_length, max_field_name_length))
+
+        for triple in print_list:
+            print format_str.format(triple[0], triple[1], triple[2])
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        """Function to explore structs/classes and union values.
+        See Explorer.explore_expr for more information.
+        """
+        type_code = value.type.code
+        if type_code == gdb.TYPE_CODE_STRUCT:
+            print ("\n'%s' is a struct/class of type '%s' "
+                   "with the following fields:\n" %
+                   (expr, str(value.type)))
+        else:
+            print ("\n'%s' is a union of type '%s' with the following "
+                   "fields:\n" %
+                   (expr, str(value.type)))
+
+        fields = value.type.fields()
+        has_explorable_fields = False
+        current_choice = 0
+        choice_to_compound_field_map = { }
+        print_list = [ ]
+        for field in fields:
+            field_full_name = expr + "." + field.name
+            field_value = value[field.name]
+            literal_value = ""
+            is_compound_field = False
+            if type_code == gdb.TYPE_CODE_UNION:
+                literal_value = ("<Enter %s to explore this field>" %
+                                 str(current_choice))
+                is_compound_field = True
+            else:
+                if Explorer.is_scalar_type(field.type):
+                    literal_value = str(field_value)
+                else:
+                    literal_value = ("<Enter %s to explore this field>" %
+                                     str(current_choice))
+                    is_compound_field = True
+
+            if is_compound_field:
+                choice_to_compound_field_map[str(current_choice)] = (
+                    field_full_name, field_value)
+                current_choice = current_choice + 1
+
+            print_list.append((str(field.type), field.name, literal_value))
+
+        CompoundExplorer._print_fields(print_list, CompoundExplorer._VALUES)
+        print ""
+
+        if len(choice_to_compound_field_map) > 0:
+            choice = raw_input("Enter the number of the field you "
+                               "want to explore in '%s': " % expr)
+            if choice in choice_to_compound_field_map:
+                Explorer.explore_expr(choice_to_compound_field_map[choice][0],
+                                      choice_to_compound_field_map[choice][1],
+                                      True)
+                return True
+            else:
+                if is_child:
+                    Explorer.returning_to_parent_value_message()
+        else:
+            if is_child:
+                Explorer.return_to_parent_value_prompt()
+
+        return False
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        """Function to explore struct/class and union types.
+        See Explorer.explore_type for more information.
+        """
+        type_code = datatype.code
+        if type_code == gdb.TYPE_CODE_STRUCT:
+            if is_child:
+                print ("\n%s is a struct/class of type '%s' "
+                       "with the following fields:\n" %
+                       (name, str(datatype)))
+            else:
+                print ("\n%s is a struct/class with the following "
+                       "fields:\n" %
+                       name)
+        else:
+            if is_child:
+                print ("\n%s is a union of type '%s' "
+                       "with the following fields:\n" %
+                       (name, str(datatype)))
+            else:
+                print ("\n%s is a union with the following fields:\n" %
+                       name)
+
+        fields = datatype.fields()
+        has_explorable_fields = False
+        current_choice = 0
+        choice_to_compound_field_map = { }
+        print_list = [ ]
+        for field in fields:
+            if Explorer.is_scalar_type(field.type):
+                print_list.append((str(field.type), field.name, "Scalar Value"))
+            else:
+                print_list.append(
+                    (str(field.type),
+                     field.name,
+                     "<Enter %d to explore this field>" % current_choice))
+                choice_to_compound_field_map[str(current_choice)] = (
+                    field.name, field.type)
+                current_choice = current_choice + 1
+
+        CompoundExplorer._print_fields(print_list, CompoundExplorer._TYPES)
+        print ""
+
+        if len(choice_to_compound_field_map) > 0:
+            choice = raw_input("Enter the number of the field you want to "
+                               "explore : ")
+            if choice in choice_to_compound_field_map:
+                Explorer.explore_type(
+                    ("field '%s' of '%s'" %
+                     (choice_to_compound_field_map[choice][0], name)),
+                    choice_to_compound_field_map[choice][1],
+                    True)
+                return True
+            else:
+                if is_child:
+                    Explorer.returning_to_enclosing_type_message()
+        else:
+            if is_child:
+                Explorer.return_to_enclosing_type_prompt()
+
+        return False
+            
+
+class TypedefExplorer(object):
+    """Internal class used to explore values whose type is a typedef."""
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        """Function to explore typedef values.
+        See Explorer.explore_expr for more information.
+        """
+        actual_type = value.type.strip_typedefs()
+        print ("\n'%s' is a value of type '%s' "
+               "which is a typedef of type '%s'" %
+               (expr, str(value.type), str(actual_type)))
+
+        Explorer.explore_expr(expr, value.cast(actual_type), is_child)
+        return False
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        """Function to explore typedef types.
+        See Explorer.explore_type for more information.
+        """
+        actual_type = datatype.strip_typedefs()
+        print("\n%s is of a type which is a typedef of type '%s'." %
+              (name, str(actual_type)))
+        Explorer.explore_type(name, actual_type, is_child)
+        return False
+
+
+class ExploreUtils(object):
+    """Internal class which provides utilities for the main command classes."""
+
+    @staticmethod
+    def check_args(name, arg_str):
+        """Utility to check if adequate number of arguments are passed to an
+        explore command.
+
+        Arguments:
+            name: The name of the explore command.
+            arg_str: The argument string passed to the explore command.
+
+        Returns:
+            True if adequate arguments are passed, false otherwise.
+
+        Raises:
+            gdb.GdbError if adequate arguments are not passed.
+        """
+        if len(arg_str) < 1:
+            raise gdb.GdbError("ERROR: '%s' command requires an argument."
+                               % name)
+            return False
+        else:
+            return True
+
+    @staticmethod
+    def get_type_from_str(type_str):
+        """A utility function to deduce the gdb.Type value from a string
+        representing the type.
+
+        Arguments:
+            type_str: The type string from which the gdb.Type value should be
+                      deduced.
+
+        Returns:
+            The deduced gdb.Type value if possible, None otherwise.
+        """
+        try:
+            # Assume the current language to be C/C++ and make a try.
+            return gdb.parse_and_eval("(%s *)0" % type_str).type.target()
+        except RuntimeError:
+            # If assumption of current language to be C/C++ was wrong, then
+            # lookup the type using the API.
+            try:
+                return gdb.lookup_type(type_str)
+            except RuntimeError:
+                return None
+
+    @staticmethod
+    def get_value_from_str(value_str):
+        """A utility function to deduce the gdb.Value value from a string
+        representing the value.
+
+        Arguments:
+            value_str: The value string from which the gdb.Value value should be
+                       deduced.
+
+        Returns:
+            The deduced gdb.Value value if possible, None otherwise.
+        """
+        try:
+            return gdb.parse_and_eval(value_str)
+        except RuntimeError:
+            return None
+
+
+class ExploreCommand(gdb.Command):
+    """GDB command to explore expression values and types top down.
+
+    Usage: explore ARG
+
+    ARG is either an expression or a type visible in the current scope. 
+
+    If ARG is an expression which evaluates to a scalar value or struct/class
+    with only scalar fields, then the value is printed. If it evaluates to a
+    union, or if it is a class/struct with class/struct fields, or if is as
+    array, the user can interactively explore the value. Exploration here means
+    to observe, starting from the fields/elements of the VALUE, right down to
+    the leaf scalar values embedded in these fields.
+
+    If ARG is a type visible in the current scope, then the user can explore
+    the type top down.
+    """
+
+    def __init__(self):
+        super(ExploreCommand, self).__init__(name = "explore",
+                                             command_class = gdb.COMMAND_DATA,
+                                             prefix = True)
+
+    def invoke(self, arg_str, from_tty):
+        if ExploreUtils.check_args("explore", arg_str) == False:
+            return
+
+        # Check if it is a value
+        value = ExploreUtils.get_value_from_str(arg_str)
+        if value is not None:
+            Explorer.explore_expr(arg_str, value, False)
+            return
+
+        # If it is not a value, check if it is a type
+        datatype = ExploreUtils.get_type_from_str(arg_str)
+        if datatype is not None:
+            Explorer.explore_type(arg_str, datatype, False)
+            return
+
+        # If it is neither a value nor a type, raise an error.
+        raise gdb.GdbError(
+            ("'%s' neither evaluates to a value nor is a type "
+             "in the current context." %
+             arg_str))
+
+
+class ExploreValueCommand(gdb.Command):
+    """GDB command to explore expression values top down.
+
+    Usage: explore value ARG
+
+    ARG is an expression visible in the current scope. 
+
+    If ARG is an expression which evaluates to a scalar value or struct/class
+    with only scalar fields, then the value is printed. If it evaluates to a
+    union, or if it is a class/struct with class/struct fields, or if is as
+    array, the user can interactively explore the value. Exploration here means
+    to observe, starting from the fields/elements of the VALUE, right down to
+    the leaf scalar values embedded in these fields.
+    """
+ 
+    def __init__(self):
+        super(ExploreValueCommand, self).__init__(
+            name = "explore value", command_class = gdb.COMMAND_DATA)
+
+    def invoke(self, arg_str, from_tty):
+        if ExploreUtils.check_args("explore value", arg_str) == False:
+            return
+
+        value = ExploreUtils.get_value_from_str(arg_str)
+        if value is None:
+            raise gdb.GdbError(
+                (" '%s' does not evaluate to a value in the current "
+                 "context." %
+                 arg_str))
+            return
+
+        Explorer.explore_expr(arg_str, value, False)
+
+
+class ExploreTypeCommand(gdb.Command):            
+    """GDB command to explore types top down.
+
+    Usage: explore type ARG
+
+    ARG is a type visible in the current scope. 
+
+    If ARG is a type visible in the current scope, then the user can explore
+    the type top down.
+    """
+
+    def __init__(self):
+        super(ExploreTypeCommand, self).__init__(
+            name = "explore type", command_class = gdb.COMMAND_DATA)
+
+    def invoke(self, arg_str, from_tty):
+        if ExploreUtils.check_args("explore type", arg_str) == False:
+            return
+
+        datatype = ExploreUtils.get_type_from_str(arg_str)
+        if datatype is None:
+            raise gdb.GdbError(
+                ("'%s' is not a type in the current context." %
+                 arg_str))
+            return
+
+        Explorer.explore_type(arg_str, datatype, False)
+
+
+Explorer.init_env()
+
+ExploreCommand()
+ExploreValueCommand()
+ExploreTypeCommand()
diff -rupN src/gdb/testsuite/gdb.python/Makefile.in src_local/gdb/testsuite/gdb.python/Makefile.in
--- src/gdb/testsuite/gdb.python/Makefile.in	2011-05-16 19:26:40.000000000 +0530
+++ src_local/gdb/testsuite/gdb.python/Makefile.in	2011-11-12 11:48:44.872558912 +0530
@@ -4,7 +4,8 @@ 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 lib-types py-events py-evthreads py-frame \
-	py-mi py-pp-maint py-progspace py-section-script py-objfile
+	py-mi py-pp-maint py-progspace py-section-script py-objfile \
+	py-explore
 
 MISCELLANEOUS = py-shared-sl.sl
 
diff -rupN src/gdb/testsuite/gdb.python/py-explore.c src_local/gdb/testsuite/gdb.python/py-explore.c
--- src/gdb/testsuite/gdb.python/py-explore.c	1970-01-01 05:30:00.000000000 +0530
+++ src_local/gdb/testsuite/gdb.python/py-explore.c	2011-12-25 23:46:14.641927363 +0530
@@ -0,0 +1,83 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 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/>.
+*/
+
+#define ARRAY_SIZE 10
+
+struct SimpleStruct
+{
+  int a;
+  double d;
+};
+
+union SimpleUnion
+{
+  int i;
+  char c;
+  float f;
+  double d;
+};
+
+typedef struct SimpleStruct SS;
+
+struct ComplexStruct
+{
+  struct SimpleStruct s;
+  union SimpleUnion u;
+  SS sa[ARRAY_SIZE];
+};
+
+union ComplexUnion
+{
+  SS s;
+  struct SimpleStruct sa[ARRAY_SIZE];
+};
+
+int
+main (void)
+{
+  int* invalid_ptr = (int *)10;
+  struct SimpleStruct ss;
+  struct SimpleStruct* ss_ptr = &ss;
+  SS ss_t;
+
+  union SimpleUnion su;
+  struct ComplexStruct cs;
+  struct ComplexStruct* cs_ptr = &cs;
+  union ComplexUnion cu;
+  int i;
+  double darray[5] = {0.1, 0.2, 0.3, 0.4, 0.5};
+  double *darray_ref = darray;
+
+  ss.a = 10;
+  ss.d = 100.01;
+  ss_t = ss;
+  
+  su.d = 100.1;
+
+  cs.s = ss;
+  cs.u = su;
+  for (i = 0; i < ARRAY_SIZE; i++)
+    {
+      cs.sa[i].a = i;
+      cs.sa[i].d = 10.10 + i;
+      cu.sa[i].a = i;
+      cu.sa[i].d = 100.10 + i;
+    }
+
+  return 0; /* Break here. */
+}
diff -rupN src/gdb/testsuite/gdb.python/py-explore.exp src_local/gdb/testsuite/gdb.python/py-explore.exp
--- src/gdb/testsuite/gdb.python/py-explore.exp	1970-01-01 05:30:00.000000000 +0530
+++ src_local/gdb/testsuite/gdb.python/py-explore.exp	2011-12-27 01:21:50.302487431 +0530
@@ -0,0 +1,358 @@
+# Copyright (C) 2011 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 command 'explore'
+# which is implemented using the GDB-Python API.
+
+load_lib gdb-python.exp
+
+set testfile "py-explore"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+set SS "struct SimpleStruct"
+set SU "union SimpleUnion"
+set CS "struct ComplexStruct"
+set CU "union ComplexUnion"
+set return_to_parent_prompt {Press enter to return to parent value...}
+set array_index_prompt {Enter the index to explore: }
+
+proc enter_field_number_prompt { value_name } {
+    return "Enter the number of the field you want to explore in '$value_name': "
+}
+
+proc compound_description { value_name type_kind type_name } {
+    return "'$value_name' is a $type_kind of type '$type_name' with the following fields:\[\r\n\]+"
+}
+
+proc typedef_description { value_name typedef_name type_name } {
+    return "'$value_name' is a value of type '$typedef_name' which is a typedef of type '$type_name'\[\r\n\]+"
+}
+
+proc scalar_description { value_name type } {
+    return "'$value_name' is a scalar value of type '$type'\.\[\r\n\]+"
+}
+
+proc array_description { value_name type } {
+    return "'$value_name' is an array of elements of type '$type'.\[\r\n\]+"
+}
+
+proc pointer_description { value_name type_name } {
+    set type_description "'$value_name' is a pointer to a value of type '$type_name'\[\r\n\]+"
+    set prompt "Enter 's' to continue exploring as a pointer to a single value, 'a' to explore as an array: " 
+    return "$type_description$prompt"
+}
+
+proc field_values { args } {
+    set result ""
+    foreach field $args {
+        set result "$result\[ \]*$field\[\r\n\]+"
+    }
+    return $result
+}
+
+proc field_choices { args } {
+    set result ""
+    set field_num 0
+    foreach field $args {
+        set result "$result$field\[ \]+=\[ \]+<Enter $field_num .*"
+        incr field_num
+    }
+    return $result
+}
+
+proc scalar_value { value_name value } {
+    return "$value_name = $value\[r\n\]+"
+}
+
+set SS_fields [field_values {int a = 10} {double d = 100[.].*}]
+
+if ![runto_main] {
+   return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here."]
+gdb_continue_to_breakpoint "Break here" ".*Break here.*"
+
+#########################
+# Value exploration tests
+#########################
+
+gdb_test "explore i" "[scalar_description {i} {int}].*i = .*"
+gdb_test "explore ss" "[compound_description {ss} {struct/class} $SS].*$SS_fields"
+gdb_test "explore *ss_ptr" "[compound_description {\(\*ss_ptr\)} {struct/class} $SS].*$SS_fields"
+gdb_test "explore ss_t" "[typedef_description {ss_t} {SS} $SS].*[compound_description {ss_t} {struct/class} $SS].*$SS_fields"
+
+gdb_test_multiple "explore invalid_ptr" "" {
+    -re "[pointer_description {invalid_ptr} {int}].*" {
+        pass "explore invalid_ptr"
+        gdb_test_multiple "s" "explore_invalid_ptr_as_single_value_pointer" {
+            -re ".*'invalid_ptr' a pointer pointing to an invalid memory location\..*" {
+                pass "explore_invalid_ptr_as_single_value_pointer"
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore ss_ptr" "" {
+    -re "[pointer_description {ss_ptr} $SS].*" {
+        pass "explore ss_ptr"
+        gdb_test_multiple "s" "explore_as_single_value_pointer" {
+            -re "$SS_fields" {
+                pass "explore su as single value pointer"
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore darray_ref" "" {
+    -re "[pointer_description {darray_ref} {double}].*" {
+        pass "explore darray_ref"
+        gdb_test_multiple "a" "explore_as_array" {
+            -re ".*Enter the index to explore:.*"  {
+                pass "explore_as_array"
+                gdb_test_multiple "2" "explore_as_array_index_2" {
+                    -re ".*darray_ref\\\[2\\\] = 0.*" {
+                        pass "explore_as_array_index_2"
+                        gdb_test_multiple " " "end explore_as_array_index_2" {
+                            -re "Returning to parent value.*Enter the index to explore: " {
+                                pass "end explore_as_array_index_2"
+                                gdb_test_multiple " " "end explore_as_array" {
+                                    -re "\[\n\r\]+" {
+                                        pass "end explore_as_array"
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } 
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore su" "" {
+    -re "[compound_description {su} union {union SimpleUnion}].*[field_choices {int i} {char c} {float f} {double d}][enter_field_number_prompt {su}]" {
+        pass "explore su"
+        gdb_test_multiple "3" "explore su.d" {
+            -re "[scalar_description {su.d} {double}].*[scalar_value {su.d} {100[.].*}].*$return_to_parent_prompt" {
+                pass "explore su.d"
+                gdb_test_multiple " " "end su.d exploration" {
+                    -re "[enter_field_number_prompt {su}]" {
+                        pass "end su.d exploration"
+                        gdb_test_multiple " " "end su exploration" {
+                            -re "$gdb_prompt" {
+                                pass "end su exploration"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore cs" "" {
+    -re "[compound_description {cs} {struct/class} {struct ComplexStruct}].*[field_choices {struct SimpleStruct s} {union SimpleUnion u} {SS .10. sa}][enter_field_number_prompt {cs}]" {
+        pass "explore cs"
+        gdb_test_multiple "0" "explore cs.s" {
+            -re "[compound_description {cs.s} {struct/class} {struct SimpleStruct}].*[field_values {int a = 10} {double d = 100[.].*}].*$return_to_parent_prompt" {
+                pass "explore cs.s"
+                gdb_test_multiple " " "end cs.s exploration" {
+                    -re "[enter_field_number_prompt {cs}]" {
+                        pass "end cs.s exploration"
+                        gdb_test_multiple " " "end cs exploration" {
+                            -re "$gdb_prompt" {
+                                pass "end cs exploration"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore cu" "" {
+    -re "[compound_description {cu} union {union ComplexUnion}].*[field_choices {SS s} {struct SimpleStruct .10. sa}][enter_field_number_prompt {cu}]" {
+        pass "explore cu"
+        gdb_test_multiple "1" "explore cu.sa" {
+            -re "[array_description {cu.sa} $SS]$array_index_prompt" {
+                pass "explore cu.sa"
+                gdb_test_multiple "0" "explore cu.sa\[0\]" {
+                    -re "[compound_description {cu.sa\[0\]} {struct/class} {struct SimpleStruct}].*[field_values {int a = 0} {double d = .*[.].*}].*$return_to_parent_prompt" {
+                        pass "explore cu.sa\[0\]"
+                        gdb_test_multiple " " "end cu.sa\[0\] exploration" {
+                            -re "[array_description {cu.sa} $SS]$array_index_prompt" {
+                                pass "end cu.sa\[0\] exploration"
+                            }
+                        }
+                    }
+                }
+                gdb_test_multiple " " "end cu.sa exploration" {
+                    -re "[enter_field_number_prompt {cu}]" {
+                        pass "end cu.sa exploration"
+                        gdb_test_multiple " " "end cu exploration" {
+                            -re "$gdb_prompt" {
+                                pass "end cu exploration"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+########################
+# Type exploration tests
+########################
+
+proc scalar_primitive_type_decsription {type} {
+    return "$type is a primitive scalar type"
+}
+
+proc compound_type_description { type_name type_kind } {
+    return "$type_name is a $type_kind with the following fields:"
+}
+
+proc compound_field_type_description { field_name type_kind type_name} {
+    return "$field_name is a $type_kind of type '$type_name' with the following fields:"
+}
+
+proc array_type_description { array_name type_name } {
+    return "$array_name is an array of elements of type '$type_name'"
+}
+
+proc typedef_type_description { type_name target_name } {
+    return "$type_name is of a type which is a typedef of type '$target_name'"
+}
+
+set CS_field_0 "field 's' of 'struct ComplexStruct'"
+set CS_field_1 "field 'u' of 'struct ComplexStruct'"
+set CS_field_2 "field 'sa' of 'struct ComplexStruct'"
+set CS_field_2_array_element "an array element of $CS_field_2"
+
+set CU_field_0 "field 's' of 'union ComplexUnion'"
+set CU_field_1 "field 'sa' of 'union ComplexUnion'"
+set CU_field_1_array_element "an array element of $CU_field_1"
+
+set SS_fields_types [field_values {int a // Scalar Value} {double d // Scalar Value}]
+set SU_fields_types [field_values {int i // Scalar Value} {char c // Scalar Value} {float f // Scalar Value} {double d // Scalar Value}]
+set CS_fields_types [field_values {struct SimpleStruct s  // <Enter 0 to explore this field>} {union SimpleUnion u  // <Enter 1 to explore this field>} {SS \[10\] sa // <Enter 2 to explore this field>}]
+set CU_fields_types [field_values {SS s  // <Enter 0 to explore this field>} {struct SimpleStruct \[10\] sa // <Enter 1 to explore this field>}]
+
+gdb_test "explore int" ".*[scalar_primitive_type_decsription {int}].*"
+gdb_test "explore struct SimpleStruct" ".*[compound_type_description $SS {struct/class}].*$SS_fields_types.*"
+gdb_test "explore union SimpleUnion" ".*[compound_type_description $SU {union}].*$SU_fields_types"
+gdb_test "explore SS" ".*[typedef_type_description {SS} $SS].*[compound_type_description {SS} {struct/class}].*$SS_fields_types.*"
+
+gdb_test_multiple "explore type struct ComplexStruct" "" {
+    -re ".*[compound_type_description $CS {struct/class}].*$CS_fields_types.*" {
+        pass "explore type struct ComplexStruct"
+        gdb_test_multiple "0" "explore type struct ComplexStruct field 0" {
+            -re ".*[compound_field_type_description $CS_field_0 {struct/class} $SS].*$SS_fields_types.*" {
+                pass "explore type struct ComplexStruct field 0"
+                gdb_test_multiple " " "return to ComplexStruct from field 0" {
+                    -re ".*[compound_type_description $CS {struct/class}].*$CS_fields_types.*" {
+                        pass "return to ComplexStruct from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type struct ComplexStruct field 1" {
+            -re ".*[compound_field_type_description $CS_field_1 {union} $SU].*$SU_fields_types.*" {
+                pass "explore type struct ComplexStruct field 1"
+                gdb_test_multiple " " "return to ComplexStruct from field 1" {
+                    -re ".*[compound_type_description $CS {struct/class}].*$CS_fields_types.*" {
+                        pass "return to ComplexStruct from field 1"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "2" "explore type struct ComplexStruct field 2" {
+            -re ".*[array_type_description $CS_field_2 {SS}].*" {
+                pass "explore type struct ComplexStruct field 2"
+                gdb_test_multiple "s" "array element of field 2 of ComplexStruct" {
+                    -re ".*[compound_field_type_description $CS_field_2_array_element {struct/class} $SS].*$SS_fields_types.*" {
+                        pass "array element of field 2 of ComplexStruct"
+                        gdb_test_multiple " " "return to ComplexStruct array" {
+                            -re ".*[array_type_description $CS_field_2 {SS}].*" {
+                                pass "return to ComplexStruct array"
+                                gdb_test_multiple " " "return to ComplexStruct from field 2" {
+                                    -re ".*[compound_type_description $CS {struct/class}].*$CS_fields_types.*" {
+                                        pass "return to ComplexStruct from field 2"
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        gdb_test_multiple " " "return to GDB prompt from ComplexStruct type exploration" {
+            -re "$gdb_prompt" {
+                pass "return to GDB prompt from ComplexStruct type exploration"
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore type union ComplexUnion" "" {
+    -re ".*[compound_type_description $CU {union}].*$CU_fields_types.*" {
+        pass "explore type union ComplexUnion"
+        gdb_test_multiple "0" "explore type union ComplexStruct field 0" {
+            -re ".*[compound_field_type_description $CU_field_0 {struct/class} $SS].*$SS_fields_types.*" {
+                pass "explore type union ComplexUnion field 0"
+                gdb_test_multiple " " "return to ComplexUnion from field 0" {
+                    -re ".*[compound_type_description $CU {union}].*$CU_fields_types.*" {
+                        pass "return to ComplexUnion from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type union ComplexUnion field 1" {
+            -re ".*[array_type_description $CU_field_1 $SS].*" {
+                pass "explore type union ComplexUnion field 1"
+                gdb_test_multiple "s" "array element of field 1 of ComplexUnion" {
+                    -re ".*[compound_field_type_description $CU_field_1_array_element {struct/class} $SS].*$SS_fields_types.*" {
+                        pass "array element of field 1 of ComplexUnion"
+                        gdb_test_multiple " " "return to ComplexUnion array" {
+                            -re ".*[array_type_description $CU_field_1 $SS].*" {
+                                pass "return to ComplexUnion array"
+                                gdb_test_multiple " " "return to ComplexUnion from field 1" {
+                                    -re ".*[compound_type_description $CU {union}].*$CU_fields_types.*" {
+                                        pass "return to ComplexUnion from field 1"
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        gdb_test_multiple " " "return to GDB prompt from ComplexUnion type exploration" {
+            -re "$gdb_prompt" {
+                pass "return to GDB prompt from ComplexUnion type exploration"
+            }
+        }
+    }
+}


More information about the Gdb-patches mailing list