Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFC] A new command 'explore'
       [not found] <CAGyQ6gyAwr2zuv3NYpdk09vPyYpuFkyiErEhw-vws-WfuLDxiw@mail.gmail.com>
@ 2011-12-19 19:05 ` Siva Chandra
  2011-12-21 19:36   ` Tom Tromey
                     ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Siva Chandra @ 2011-12-19 19:05 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1453 bytes --]

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

[-- Attachment #2: explore_command_patch.txt --]
[-- Type: text/plain, Size: 37432 bytes --]

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-16 12:23:08.868195986 +0530
@@ -0,0 +1,542 @@
+# 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):
+    """Internel class which invokes other explorers."""
+
+    # This map is filled by the ExploreUtils.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):
+        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):
+        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 is_scalar_type(type):
+        if type.code in Explorer._SCALAR_TYPE_LIST:
+            return True
+        else:
+            return False
+
+    @staticmethod
+    def returning_to_parent_value_message():
+        print "\nReturning to parent value...\n"
+        
+    @staticmethod
+    def return_to_parent_value_prompt():
+        raw_input("\nPress enter to return to parent value...")
+        Explorer.returning_to_parent_value_message()
+        
+    @staticmethod
+    def returning_to_enclosing_type_message():
+        print "\nReturning to enclosing type...\n"
+        
+    @staticmethod
+    def return_to_enclosing_type_prompt():
+        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):
+        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):
+        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):
+        if str(value) == "0x0":
+            print "\n  %s = NULL" % expr
+            if is_child:
+                Explorer.return_to_parent_value_prompt()
+
+            return False
+
+        deref_value = None
+        try:
+            deref_value = value.dereference()
+        except RuntimeError:
+            print ("\'%s\' a pointer pointing to an invalid memory location." %
+                   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, deref_value.type))
+        Explorer.explore_expr("*(%s)" % expr, deref_value, is_child)
+
+        return False
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        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):
+        print ("\n%s is an array of elements of type \'%s\'." %
+               (name, target_type))
+
+    @staticmethod
+    def explore_expr(expr, value, is_child):
+        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
+
+        array_range = value.type.range()
+        if index < array_range[0] or index > array_range[1]:
+            print "Entered index out of range of the array."
+            raw_input("Press enter to continue... ")
+            return True
+
+        element = value[index]
+        Explorer.explore_expr("%s[%s]" % (expr, index), element, True)
+        return True
+
+    @staticmethod
+    def explore_type(name, datatype, is_child):
+        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):
+        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):
+        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):
+        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):
+        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):
+        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 classes."""
+
+    @staticmethod
+    def init_env():
+        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 check_args(name, arg_str):
+        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):
+        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):
+        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(
+            ("ERROR: \'%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(
+                ("ERROR: \'%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(
+                ("ERROR: \'%s\' is not a type in the current context." %
+                 arg_str))
+            return
+
+        Explorer.explore_type(arg_str, datatype, False)
+
+
+ExploreUtils.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-11-24 00:25:18.612318411 +0530
@@ -0,0 +1,80 @@
+/* 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)
+{
+  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;
+
+  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-16 12:19:09.898770963 +0530
@@ -0,0 +1,311 @@
+# Copyright (C) 2008, 2009, 2010, 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 value_description ".*$value_name.*$type_name.*"    
+    return "$type_description$value_description"
+}
+
+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" "[pointer_description {ss_ptr} $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 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"
+            }
+        }
+    }
+}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-19 19:05 ` [RFC] A new command 'explore' Siva Chandra
@ 2011-12-21 19:36   ` Tom Tromey
  2011-12-22 11:00     ` Siva Chandra
  2011-12-22  5:21   ` Joel Brobecker
  2011-12-27  6:29   ` Siva Chandra
  2 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2011-12-21 19:36 UTC (permalink / raw)
  To: Siva Chandra; +Cc: gdb-patches

>>>>> "Siva" == Siva Chandra <sivachandra@google.com> writes:

Siva> Attached is a patch which implements a new command 'explore' using the
Siva> GDB Python API.

Thanks.

It seems like a nice addition to me.

It would have helped a little if you had included a sample session in
your email.

I read the patch, though not in an extremely deep way.

Siva> +    """Internel class which invokes other explorers."""

Typo, "Internal".

Siva> +        raw_input("\nPress enter to return to parent value...")

The explorer seems to require a lot of enter-pressing.

To me it seems like this would get in the way.
I did not actually try it out, though.

Siva> +        try:
Siva> +            deref_value = value.dereference()
Siva> +        except RuntimeError:

I think that it should be possible to catch gdb.MemoryError here.
That would be more precise.

I didn't notice a way to explore a pointer that is really a decayed
array.

Siva> +        array_range = value.type.range()
Siva> +        if index < array_range[0] or index > array_range[1]:

I am not sure this will always do the right thing.
It seems possible to me for an array not to have a valid range.

Siva> +    @staticmethod
Siva> +    def get_type_from_str(type_str):
Siva> +        try:
Siva> +            # Assume the current language to be C/C++ and make a try.
Siva> +            return gdb.parse_and_eval("(%s *)0" % type_str).type.target()

This is a nice trick.

If you have time, it would be nice to extend the API so that this trick
isn't needed.  I think a type-parsing method would be useful.

Siva> +        raise gdb.GdbError(
Siva> +            ("ERROR: \'%s\' neither evaluates to a value nor is a type "

No need for "ERROR: ", I think.

Tom


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-19 19:05 ` [RFC] A new command 'explore' Siva Chandra
  2011-12-21 19:36   ` Tom Tromey
@ 2011-12-22  5:21   ` Joel Brobecker
  2011-12-22 11:48     ` Siva Chandra
  2011-12-27  6:29   ` Siva Chandra
  2 siblings, 1 reply; 12+ messages in thread
From: Joel Brobecker @ 2011-12-22  5:21 UTC (permalink / raw)
  To: Siva Chandra; +Cc: gdb-patches

Hi Siva,

> 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 for sending this. It's always exciting when you see people
send patches using the extension language. I have a few minor
comments for you... I'll try to skip the comments that Tom already
made.

> +import gdb

GDB already pre-imports module `gdb', and it's documented in the GDB
manual. So I think you can get rid of this import. Though, I wonder
if that's the recommended style or not. Tom?

> +    _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,
> +    ]

It's not very important, but since the list is constant, make that
a tuple. Generally speaking, I have noticed that people tend to use
lists all the time, and many times a tuple is what they really should
be using. I checked the performance delta between list and tuple a
while back, and it was significant.

> +    @staticmethod
> +    def explore_expr(expr, value, is_child):

I think it would be nice if each method was documented, the same
way we document our code in C.

> +            print ("Explorer for type \'%s\' not yet available.\n" %
                                        ^^^^^^^
I don't think the backslashes are necessary here. Same elsewhere...

> +    def is_scalar_type(type):
> +        if type.code in Explorer._SCALAR_TYPE_LIST:
> +            return True
> +        else:
> +            return False

Nitpick: 
        return type.code in Explorer._SCALAR_TYPE_LIST
?

> +class ScalarExplorer(object):
> +    """Internal class used to explore scalar values."""

It seems that the design is centered around these various *Explorer
classes implementing a defined interface.  If that's the case,
I think it would make it clearer if they inherited from an abstract
class where you can then clearly describe what the interface is,
and what each element is expected to do. Put an assertion in each
abstract method to raise an exception if the method is not implemented
by the deriving class.

Also, almost everything is declared @staticmethod. Is that typical
Python? I don't have enough experience with expert Python to really
say anything, but it almost seems like you're taking the problem
backwards. Let's imagine that you had one abstract Explorer class
which did all the generic stuff, and then a collection of *Explorer
classes which derived from it, you'd then only need a factory that
the commands would invoke, and then the rest would be standard python.
It would avoid all the @staticmethod, as well as the explicit class
names in the method invocations.

This is not a request to change, but just some suggestions based
on my limited understanding of the code and of Python in the general.
This code is self contained, so it does not matter as much how it
is designed. Please do what you think is best.

> +class ExploreUtils(object):
> +    """Internal class which provides utilities for the main classes."""
> +
> +    @staticmethod
> +    def init_env():
> +        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
> +        }

You wouldn't have to put that static map as a class variable
in class Explorer. This map would be all your factory would need.
I'd make that a pure function, or even a global "constant" that
you'd use from your commands.

> +    def get_type_from_str(type_str):
> +        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

We really need to think about giving access to parameter values
from python.... That would make the code above a little easier,
as you'd be able to temporarily force the language to C...


-- 
Joel


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-21 19:36   ` Tom Tromey
@ 2011-12-22 11:00     ` Siva Chandra
  0 siblings, 0 replies; 12+ messages in thread
From: Siva Chandra @ 2011-12-22 11:00 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Thanks for taking a look Tom. My comments inline.

Siva> +        raw_input("\nPress enter to return to parent value...")

Tom > The explorer seems to require a lot of enter-pressing.
Tom >
Tom > To me it seems like this would get in the way.
Tom > I did not actually try it out, though.

I didn't find any better way than this to inform the user that
returning to the parent is the only possible thing to do.

Siva> +        try:
Siva> +            deref_value = value.dereference()
Siva> +        except RuntimeError:

Tom > I think that it should be possible to catch gdb.MemoryError here.
Tom > That would be more precise.

OK. I will change to MemoryError. But I think just dereferencing does
not suffice here. There should be something else to try reading the
value: str(value.dereference()) ??

Tom > I didn't notice a way to explore a pointer that is really a decayed
Tom > array.

I will try to add something for this.

Siva> +        array_range = value.type.range()
Siva> +        if index < array_range[0] or index > array_range[1]:

Tom > I am not sure this will always do the right thing.
Tom > It seems possible to me for an array not to have a valid range.

I agree. I do know that it is not sufficient for many of the GCC data
structures. However, I couldn't think of a better compromise. How do
we convey to the user that he is going out of bounds? Is such a thing
required at all?? Let the user figure out the array bounds by some
other means?

Siva> +    @staticmethod
Siva> +    def get_type_from_str(type_str):
Siva> +        try:
Siva> +            # Assume the current language to be C/C++ and make a try.
Siva> +            return gdb.parse_and_eval("(%s *)0" % type_str).type.target()

Tom > This is a nice trick.

This trick is not mine, it was suggested to me by Doug Evans.

Tom > If you have time, it would be nice to extend the API so that this trick
Tom > isn't needed.  I think a type-parsing method would be useful.

Can I take a look at this after clearing this patch?

Thanks,
Siva Chandra


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-22  5:21   ` Joel Brobecker
@ 2011-12-22 11:48     ` Siva Chandra
  2011-12-22 12:53       ` Joel Brobecker
  0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2011-12-22 11:48 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

Thanks for taking a look Joel. My comments inline.

Siva > +import gdb

Joel > GDB already pre-imports module `gdb', and it's documented in the GDB
Joel > manual. So I think you can get rid of this import. Though, I wonder
Joel > if that's the recommended style or not. Tom?

This is the convention(?) in other files in gdb/python/lib/gdb/command.

Joel > It seems that the design is centered around these various *Explorer
Joel > classes implementing a defined interface.  If that's the case,
Joel > I think it would make it clearer if they inherited from an abstract
Joel > class where you can then clearly describe what the interface is,
Joel > and what each element is expected to do. Put an assertion in each
Joel > abstract method to raise an exception if the method is not implemented
Joel > by the deriving class.
Joel >
Joel > Also, almost everything is declared @staticmethod. Is that typical
Joel > Python? I don't have enough experience with expert Python to really
Joel > say anything, but it almost seems like you're taking the problem
Joel > backwards. Let's imagine that you had one abstract Explorer class
Joel > which did all the generic stuff, and then a collection of *Explorer
Joel > classes which derived from it, you'd then only need a factory that
Joel > the commands would invoke, and then the rest would be standard python.
Joel > It would avoid all the @staticmethod, as well as the explicit class
Joel > names in the method invocations.
Joel >
Joel > This is not a request to change, but just some suggestions based
Joel > on my limited understanding of the code and of Python in the general.
Joel > This code is self contained, so it does not matter as much how it
Joel > is designed. Please do what you think is best.

I did have inheritance in my first implementation. But soon realized
that I am essentially implementing stateless functions. Hence, I
removed the inheritance, kept the classes, and made the functions
static (to imply that they are stateless). In fact, we need not have
the classes as well. Like you have noted in your penultimate sentence
above, this is really not an API for others to use. So, isn't an
abstract base class and inheritance an overkill for a Python
implementation here?

Siva > +    def get_type_from_str(type_str):
Siva > +        try:
Siva > +            # Assume the current language to be C/C++ and make a try.
Siva > +            return gdb.parse_and_eval("(%s *)0" %
type_str).type.target()
Siva > +        except RuntimeError:
Siva > +            # If assumption of current language to be C/C++
was wrong, then
Siva > +            # lookup the type using the API.
Siva > +            try:
Siva > +                return gdb.lookup_type(type_str)
Siva > +            except RuntimeError:
Siva> +                return None

Joel > We really need to think about giving access to parameter values
Joel > from python.... That would make the code above a little easier,
Joel > as you'd be able to temporarily force the language to C...

Like I have said in my response to Tom, I am OK to take a look at this
once this patch is cleared.

Thanks,
Siva Chandra


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-22 11:48     ` Siva Chandra
@ 2011-12-22 12:53       ` Joel Brobecker
  0 siblings, 0 replies; 12+ messages in thread
From: Joel Brobecker @ 2011-12-22 12:53 UTC (permalink / raw)
  To: Siva Chandra; +Cc: gdb-patches

> I did have inheritance in my first implementation. But soon realized
> that I am essentially implementing stateless functions. Hence, I
> removed the inheritance, kept the classes, and made the functions
> static (to imply that they are stateless). In fact, we need not have
> the classes as well. Like you have noted in your penultimate sentence
> above, this is really not an API for others to use. So, isn't an
> abstract base class and inheritance an overkill for a Python
> implementation here?

It might be overkill indeed, but I find that this is the natural way
to implement in Python. You may be right in your observation about
stateless functions, but it seems to me that the net effect of this
design change is that it made the code more difficult to approach
and thus maintain.  For instance, you have to infer that all the classes,
which from a programatic point of view are indepdent, actually all need
to implement a given interface (which one and the semantics of that
interface are not documented either). That's where abstract classes
and inheritance bring some value. In a way, this reminds me of the
discussions I was having with Ada newbies a long time ago, telling them
how they did C programing in Ada... It feels a little bit the same way
here.

Just my 2 cents, though. This is your code, it's self-contained and
it does the job, so I am OK with whatever you decide.

-- 
Joel


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-19 19:05 ` [RFC] A new command 'explore' Siva Chandra
  2011-12-21 19:36   ` Tom Tromey
  2011-12-22  5:21   ` Joel Brobecker
@ 2011-12-27  6:29   ` Siva Chandra
  2012-02-08  9:34     ` Siva Chandra
  2 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2011-12-27  6:29 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 2434 bytes --]

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

[-- Attachment #2: explore_command_patch_v2.txt --]
[-- Type: text/plain, Size: 45684 bytes --]

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"
+            }
+        }
+    }
+}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2011-12-27  6:29   ` Siva Chandra
@ 2012-02-08  9:34     ` Siva Chandra
  2012-02-17  7:40       ` Doug Evans
  0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2012-02-08  9:34 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 3431 bytes --]

Hello,

I have not yet seen anything about a policy on contributions
implemented in extension languages. However, I have now implemented
the 'explore' command in C. The patch is attached. I will add
documentation after the basics of this patch are approved.

2012-02-08 Siva Chandra <sivachandra@google.com>

        New command 'explore' which helps explore values in scope.
        * explore.c: Implemention of the 'explore' command.
        * Makefile.in: Add rules for compiling and linked explore.c.
        * testsuite/gdb.base/explore.c: C program used for testing
        the new 'explore' command.
        * testsuite/gdb.base/explore.exp: Tests for the new 'explore'
        command.
        * testsuite/gdb.base/Makefile.in: Add explore to EXECUTABLES

To recollect, the idea behind the '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. Not only values, even
types can be explored in a similar fashion.

Example of value exploration:
========================
(gdb) explore cs
The value of 'cs' is of type 'struct ComplexStruct' with the following fields:
   s = <Enter 0 to explore this field of type 'struct SimpleStruct'>
   u = <Enter 1 to explore this field of type 'union SimpleUnion'>
  sa = <Enter 2 to explore this field of type 'SS [10]'>

Enter the field number of choice: 2
'cs.sa' is an array of elements of type 'SS'.
Enter the index you want to explore in 'cs.sa': 1
The value of '(cs.sa)[1]' is of type 'SS' which is a typedef of type
'struct SimpleStruct'.
The value of '(cs.sa)[1]' is of type 'struct SimpleStruct' with the
following fields:
  a = 1 .. (Value of type 'int')
  d = 11.1 .. (Value of type 'double')

Press enter to return to parent value:

Returning to parent value...

'cs.sa' is an array of elements of type 'SS'.
Enter the index you want to explore in 'cs.sa':
Returning to parent value...
The value of 'cs' is of type 'struct ComplexStruct' with the following fields:
   s = <Enter 0 to explore this field of type 'struct SimpleStruct'>
   u = <Enter 1 to explore this field of type 'union SimpleUnion'>
  sa = <Enter 2 to explore this field of type 'SS [10]'>

Enter the field number of choice:
(gdb)

Example of type exploration:
========================
(gdb) explore struct SimpleStruct
'struct SimpleStruct' has the following fields:
  a = <Enter 0 to explore this field of type 'int'>
  d = <Enter 1 to explore this field of type 'double'>

Enter the field number of choice: 1
field 'd' of 'struct SimpleStruct' is of a scalar type 'double'.
Press enter to return to enclosing type:

Returning to parent type...
'struct SimpleStruct' has the following fields:
  a = <Enter 0 to explore this field of type 'int'>
  d = <Enter 1 to explore this field of type 'double'>

Enter the field number of choice:
(gdb)

Thanks,
Siva Chandra

[-- Attachment #2: c_explore_command_patch_v1.txt --]
[-- Type: text/plain, Size: 50714 bytes --]

diff -rupN src/gdb/explore.c src_explore/src/gdb/explore.c
--- src/gdb/explore.c	1970-01-01 05:30:00.000000000 +0530
+++ src_explore/src/gdb/explore.c	2012-02-08 12:06:30.106071131 +0530
@@ -0,0 +1,1010 @@
+/* Implementation of the 'explore' command.
+
+   Copyright (C) 2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+
+/* For domain_enum */
+#include "symtab.h"
+
+#include "arch-utils.h"
+#include "command.h"
+#include "exceptions.h"
+#include "gdb_string.h"
+#include "gdbcmd.h"
+#include "gdbtypes.h"
+#include "language.h"
+#include "ui-file.h"
+#include "value.h"
+#include "valprint.h"
+
+static void explore_value (struct value *, const char *, int);
+static void explore_type (struct type *, const char *, int);
+
+/* Different types of exploration.  */
+
+enum exploration_type
+{
+  /* For value exploration.  */
+  VALUE_EXPLORATION = 0,
+
+  /* For type exploration.  */
+  TYPE_EXPLORATION = 1
+};
+
+/* Return 1 if STR is all spaces. O otherwise.  */
+
+static int
+is_all_spaces (const char *str)
+{
+  int i;
+  int len = strlen (str);
+
+  for (i = 0; i < len; ++i)
+    {
+      if (str[i] != ' ')
+        {
+          return 0;
+        }
+    }
+
+  return 1;
+}
+
+static void
+strip_leading_spaces (char **str_ptr)
+{
+  char *str = *str_ptr;
+  for (; *str != '\0'; str++)
+    {
+      if (*str != ' ')
+        {
+          *str_ptr = str;
+          return;
+        }
+    }
+}
+
+/* Prompt the user to return the state of exploration to the
+   parent value. Used in value exploration.  */
+
+static void
+return_to_parent_value_prompt (void)
+{
+  xfree (gdb_readline (_("Press enter to return to parent value: ")));
+  printf_filtered ("\n");
+}
+
+/* Prompt the user to return the state of exploration to the
+   enclosing/parent type. Used in type exploration.  */
+
+static void
+return_to_parent_type_prompt (void)
+{
+  xfree (gdb_readline (_("Press enter to return to enclosing type: ")));
+  printf_filtered ("\n");
+}
+
+/* Print a message for the user informing that the the state of
+   exploration is moving to the parent value.  */
+
+static void
+return_to_parent_value (void)
+{
+  printf_filtered (_("Returning to parent value...\n"));
+}
+
+/* Print a message for the user informing that the the state of
+   exploration is moving to the parent/enclosing type.  */
+
+static void
+return_to_parent_type (void)
+{
+  printf_filtered (_("Returning to parent type...\n"));
+}
+
+/* Retrun the string representation of VAL. The returned value
+   should be cleaned up after use.  */
+
+static char *
+get_value_string (struct value *val)
+{
+  struct ui_file *mem_file = mem_fileopen ();
+  struct value_print_options opts;
+  char *val_string;
+
+  get_user_print_options (&opts);
+  common_val_print (val, mem_file, 0, &opts, current_language);
+  val_string = ui_file_xstrdup (mem_file, NULL);
+  
+  ui_file_delete (mem_file);
+  return val_string;
+}
+
+/* Return the type name of VAL_TYPE. The returned value should be cleaned up
+   after use.  */
+
+static char *
+get_type_name (struct type *val_type)
+{
+  struct ui_file *mem_file = mem_fileopen ();
+  char *type_name;
+
+  LA_PRINT_TYPE (val_type, "", mem_file, 0, 0);
+  type_name = ui_file_xstrdup (mem_file, NULL);
+
+  ui_file_delete (mem_file);
+  return type_name;
+}
+
+/* Add parentheses to an EXP_STR if required. The added parenthesis
+   serves as a guard which ensures proper operator precedence when, for
+   example, dereferencing the value of which EXP_STR evaluates to. The
+   returned value should be cleaned up after use.  */
+
+static char *
+guard_exp_str (const char *exp_str)
+{
+  int len = strlen (exp_str);
+  int i, guard = 0;
+  char *new_exp;
+
+  if (exp_str[0] == '(' && exp_str[len - 1] == ')')
+    {
+      ;
+    }
+  else
+    {
+      for (i = 0; i < len; i++)
+        {
+          char c = exp_str[i];
+          if (c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
+              || ('0' <= c && c <= '9'))
+            {
+              ;
+            }
+          else
+            {
+              guard = 1;
+              break;
+            }
+        }
+    }
+
+  if (guard)
+    {
+      new_exp = xstrprintf (_("(%s)"), exp_str);
+    }
+  else
+    {
+      new_exp = xstrprintf (_("%s"), exp_str);
+    }
+
+  return new_exp;
+}
+
+/* Return 1 if VAL_TYPE is a scalar type, 0 otherwise.  */
+
+static int
+is_scalar_type (struct type *val_type)
+{
+  switch (TYPE_CODE (val_type))
+    {
+    case TYPE_CODE_INT:
+    case TYPE_CODE_FLT:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_BOOL:
+    case TYPE_CODE_ENUM:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Explore scalar value.  */
+
+static int
+explore_scalar_value (struct value *val, const char *exp_str, int is_child)
+{
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+
+  char *type_name = get_type_name (value_type (val));
+  char *val_str = get_value_string (val);
+  make_cleanup (xfree, type_name);
+  make_cleanup (xfree, val_str);
+
+  printf_filtered (_("'%s' is a scalar value of type '%s'.\n"), exp_str,
+                   type_name);
+  printf_filtered (_("%s = %s\n"), exp_str, val_str);
+
+  if (is_child)
+    {
+      return_to_parent_value_prompt ();
+      return_to_parent_value ();
+    }
+
+  do_cleanups (cleanup_obj);
+  return 0;
+}
+
+/* Explore scalar type.  */
+
+static int
+explore_scalar_type (struct type *type, const char *path, int is_child)
+{
+  if (is_child)
+    {
+      char *type_name = get_type_name (type);
+      printf_filtered (_("%s is of a scalar type '%s'.\n"), path, type_name);
+      return_to_parent_type_prompt ();
+      return_to_parent_type ();
+
+      xfree (type_name);
+    }
+  else
+    {
+      printf_filtered (_("'%s' is of a scalar type.\n"), path);
+    }
+
+  return 0;
+}
+
+/* Explore pointer value.  */
+
+static int
+explore_pointer_value (struct value *val, const char *exp_str, int is_child)
+{
+  struct value *deref_val = value_ind (val);
+  struct type *deref_type = value_type (deref_val);
+  char *resp1, *deref_type_name, *guarded_exp_str;
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+
+  deref_type_name = get_type_name (deref_type);
+  make_cleanup (xfree, deref_type_name);
+
+  guarded_exp_str = guard_exp_str (exp_str);
+  make_cleanup (xfree, guarded_exp_str);
+
+  printf_filtered (_("'%s' is a pointer to a value of type '%s'.\n"),
+                   exp_str, deref_type_name);
+
+  if (!can_dereference (value_type (val)))
+    {
+      printf_filtered (_("Cannot dereference '%s' for further exploration.\n"),
+                       exp_str);
+      if (is_child)
+        {
+          return_to_parent_value_prompt ();
+        }
+
+      return 0;
+    }
+
+  resp1 = gdb_readline (_("Do you want to explore it as an array or a single "
+                          "value pointer [a/s] : "));
+  make_cleanup (xfree, resp1);
+  if (strcmp (resp1, "a") == 0)
+    {
+      char *array_index_prompt = xstrprintf (_("Enter the index you want to "
+                                               "explore in '%s': "), exp_str);
+      make_cleanup (xfree, array_index_prompt);
+
+      while (1)
+        {
+          char *resp2 = gdb_readline (array_index_prompt);
+          make_cleanup (xfree, resp2);
+          strip_leading_spaces (&resp2);
+          if (resp2 && resp2[0] != '\0' && !is_all_spaces (resp2))
+            {
+              struct value *elem_val;
+              char *new_exp;
+
+              const char *endptr;
+              LONGEST index = strtoulst (resp2, &endptr, 10);
+              if (endptr == resp2)
+                {
+                  break;
+                }
+
+              elem_val = value_subscript (val, index);
+
+              new_exp = xstrprintf ("%s[%ld]", guarded_exp_str, index);
+              make_cleanup (xfree, new_exp);
+
+              explore_value (elem_val, new_exp, 1);
+            }
+          else
+            {
+              break;
+            }
+        }
+    }
+  else if (strcmp (resp1, "s") == 0)
+    {
+      char *new_exp = xstrprintf (_("*%s"), guarded_exp_str);
+      make_cleanup (xfree, new_exp);
+
+      explore_value (deref_val, new_exp, is_child);
+    }
+  else
+    {
+      if (is_child)
+        {
+          return_to_parent_value ();
+        }
+    }
+
+  do_cleanups (cleanup_obj);
+  return 0;
+}
+
+/* Explore pointer type.  */
+
+static int
+explore_pointer_type (struct type *type, const char *path, int is_child)
+{
+  struct type *target_type = TYPE_TARGET_TYPE (type);
+  char *type_name = get_type_name (target_type);
+  char *new_path = NULL;
+
+  printf_filtered (_("%s is of a pointer type pointing to a value of type "
+                     "'%s'.\n"), path, type_name);
+
+  new_path = xstrprintf (_("the pointee type of %s"), path);
+  explore_type (target_type, new_path, is_child);
+
+  xfree (type_name);
+  xfree (new_path);
+  return 0;
+}
+
+/* Explore array value.  */
+
+static int
+explore_array_value (struct value *val, const char *exp_str, int is_child)
+{
+  char *array_index_prompt, *resp, *guarded_exp_str;
+  struct type *elem_type = TYPE_TARGET_TYPE (value_type (val));
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+
+  char *elem_type_name = get_type_name (elem_type);
+  make_cleanup (xfree, elem_type_name);
+
+  guarded_exp_str = guard_exp_str (exp_str);
+  make_cleanup (xfree, guarded_exp_str);
+
+  printf_filtered ("'%s' is an array of elements of type '%s'.\n", exp_str,
+                   elem_type_name);
+
+  array_index_prompt = xstrprintf (_("Enter the index you want to explore "
+                                     "in '%s': "),
+                                   exp_str);
+  make_cleanup (xfree, array_index_prompt);
+
+  resp = gdb_readline (array_index_prompt);
+  make_cleanup (xfree, resp);
+  strip_leading_spaces (&resp);
+  if (resp && resp[0] != '\0' && !is_all_spaces(resp))
+    {
+      struct value *elem_val;
+      char *new_exp;
+
+      const char *endptr;
+      LONGEST index = strtoulst (resp, &endptr, 10);
+      if (endptr == resp)
+        {
+          do_cleanups (cleanup_obj);
+          return 0;
+        }
+
+      elem_val = value_subscript (val, index);
+
+      new_exp = xstrprintf ("%s[%ld]", guarded_exp_str, index);
+      make_cleanup (xfree, new_exp);
+
+      explore_value (elem_val, new_exp, 1);
+
+      do_cleanups (cleanup_obj);
+      return 1;
+    }  
+  else
+    {
+      if (is_child)
+        {
+          return_to_parent_value();
+        }
+
+      do_cleanups (cleanup_obj);
+      return 0;
+    }
+}
+
+/* Explore array type.  */
+
+static int
+explore_array_type (struct type *type, const char *path, int is_child)
+{
+  struct type *target_type = TYPE_TARGET_TYPE (type);
+  char *type_name = get_type_name (target_type);
+  char *new_path = NULL;
+
+  printf_filtered (_("%s is an array type with elements of type '%s'.\n"),
+                   path, type_name);
+
+  new_path = xstrprintf (_("the array element of %s"), path);
+  explore_type (target_type, new_path, is_child);
+
+  xfree (type_name);
+  xfree (new_path);
+  return 0;
+}
+
+struct field_desc
+{
+  const char *field_name;
+  struct value *field_value;
+  struct type *field_type;
+};
+
+/* Print fields of a compound type/value. FIELDS is an array of size NFIELDS.
+   PARENT_VAL_TYPE is the type_code of the compound type. It is either
+   TYPE_CODE_STRUCT or TYPE_CODE_UNION. EXPLR_TYPE indicates the exploration
+   type, VALUE_EXPLORATION or TYPE_EXPLORATION.  */
+
+static void
+print_fields (struct field_desc *fields, int nfields,
+              enum type_code parent_val_type,
+              enum exploration_type explr_type)
+{
+  int max_field_name_length = 0;
+  int i;
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+
+  for (i = 0; i < nfields; i++)
+    {
+      int field_name_length = strlen (fields[i].field_name);
+      if (field_name_length > max_field_name_length)
+        {
+          max_field_name_length = field_name_length;
+        }
+    }
+
+  for (i = 0; i < nfields; i++)
+    {
+      int spaces = max_field_name_length - strlen (fields[i].field_name);
+      struct type *field_type = fields[i].field_type;
+      char *field_type_name = get_type_name (field_type);
+      make_cleanup (xfree, field_type_name);
+
+      print_spaces_filtered (spaces + 2, gdb_stdout);
+      printf_filtered ("%s = ", fields[i].field_name);
+      
+      if (explr_type == VALUE_EXPLORATION && parent_val_type == TYPE_CODE_STRUCT
+          && is_scalar_type (field_type))
+        {
+          char *field_value_string = get_value_string (fields[i].field_value);
+          make_cleanup (xfree, field_value_string);
+
+          printf_filtered (_("%s .. (Value of type '%s')\n"),
+                           field_value_string, field_type_name);
+        }
+      else
+        {
+          printf_filtered (_("<Enter %d to explore this field of type '%s'>\n"),
+                           i, field_type_name);
+        }
+    }
+}
+
+/* Explore compound value. A compound value is a struct, class or a union
+   value.  */
+
+static int
+explore_compound_value (struct value *val, const char *exp_str, int is_child)
+{
+  struct type *val_type = value_type (val);
+  char *type_name;
+  enum type_code val_type_code = TYPE_CODE (val_type);
+  int nfields = TYPE_NFIELDS (val_type);
+  int i;
+  int further_exploration = (val_type_code == TYPE_CODE_UNION) ? 1 : 0;
+  struct field_desc *fields;
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+
+  fields = (struct field_desc *) xzalloc (nfields * sizeof (struct field_desc));
+  make_cleanup (xfree, fields);
+
+  type_name = get_type_name (val_type);
+  make_cleanup (xfree, type_name);
+
+  printf_filtered (_("The value of '%s' is of type '%s' with the following "
+                     "fields:\n"), exp_str, type_name);
+
+  for (i = 0; i < nfields; i++)
+    {
+      struct type *field_type = TYPE_FIELD_TYPE (val_type, i);
+      const char *field_name = TYPE_FIELD_NAME (val_type, i);
+
+      fields[i].field_name = field_name;
+      fields[i].field_value = value_field (val, i);
+      fields[i].field_type = field_type;
+
+      if (!is_scalar_type (field_type) && val_type_code == TYPE_CODE_STRUCT)
+        {
+          further_exploration = 1;
+        }
+    }
+
+  print_fields (fields, nfields, val_type_code, VALUE_EXPLORATION);
+
+  if (further_exploration)
+    {
+      char *choice_str = gdb_readline ("\nEnter the field number of choice: ");
+      make_cleanup (xfree, choice_str);
+      strip_leading_spaces (&choice_str);
+
+      if (choice_str && choice_str[0] != '\0' && !is_all_spaces (choice_str))
+        {
+          const char *endptr;
+          LONGEST choice = strtoulst (choice_str, &endptr, 10);
+          if (endptr != choice_str && choice >= 0 && choice < nfields)
+            {
+              char *guarded_exp_str = guard_exp_str (exp_str);
+              char *new_exp = xstrprintf ("%s.%s", guarded_exp_str,
+                                          fields[choice].field_name);
+              make_cleanup (xfree, new_exp);
+              make_cleanup (xfree, guarded_exp_str);
+
+              explore_value (fields[choice].field_value, new_exp, 1);
+
+              do_cleanups (cleanup_obj);
+              return 1;
+            }
+        }
+    }
+  else
+    {
+      if (is_child)
+        {
+          printf_filtered ("\n");
+          return_to_parent_value_prompt ();
+        }
+    }
+
+  if (is_child)
+    {
+      return_to_parent_value ();
+      printf_filtered ("\n");
+    }
+
+  do_cleanups (cleanup_obj);
+  return 0;
+}
+
+/* Explore compound type. A compound type is a struct, class or a union
+   type.  */
+
+static int
+explore_compound_type (struct type *type, const char *path, int is_child)
+{
+  char *type_name;
+  int nfields = TYPE_NFIELDS (type);
+  int i;
+  struct field_desc *fields;
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+  
+  fields = (struct field_desc *) xzalloc (nfields * sizeof (struct field_desc));
+  make_cleanup (xfree, fields);
+
+  type_name = get_type_name (type);
+  make_cleanup (xfree, type_name);
+
+  if (is_child)
+    {
+      printf_filtered (_("%s is of type '%s' with the following fields:\n"),
+                       path, type_name);
+    }
+  else
+    {
+      printf_filtered ("'%s' has the following fields:\n", path);
+    }
+
+  for (i = 0; i < nfields; i++)
+    {
+      fields[i].field_name = TYPE_FIELD_NAME (type, i);
+      fields[i].field_type = TYPE_FIELD_TYPE (type, i);
+      fields[i].field_value = NULL;
+    }
+
+  print_fields (fields, nfields, TYPE_CODE (type), TYPE_EXPLORATION);
+
+  if (nfields)
+    {
+      char *choice_str = gdb_readline (_("\nEnter the field number of "
+                                         "choice: "));
+      make_cleanup (xfree, choice_str);
+      strip_leading_spaces (&choice_str);
+
+      if (choice_str && choice_str[0] != '\0' && !is_all_spaces (choice_str))
+        {
+          const char *endptr;
+          LONGEST choice = strtoulst (choice_str, &endptr, 10);
+          if (endptr != choice_str && choice >= 0 && choice < nfields)
+            {
+              char *new_path;
+                if (is_child)
+                  {
+                    new_path  = xstrprintf (_("field '%s' of %s"),
+                                            fields[choice].field_name, path);
+                  }
+                else
+                  {
+                    new_path  = xstrprintf (_("field '%s' of '%s'"),
+                                            fields[choice].field_name, path);
+                  }
+              make_cleanup (xfree, new_path);
+
+              explore_type (fields[choice].field_type, new_path, 1);
+
+              do_cleanups (cleanup_obj);
+              return 1;
+            }
+        }
+    }
+  else
+    {
+      if (is_child)
+        {
+          printf_filtered ("\n");
+          return_to_parent_type_prompt ();
+        }
+    }
+
+  if (is_child)
+    {
+      return_to_parent_type ();
+      printf_filtered ("\n");
+    }
+
+  do_cleanups (cleanup_obj);
+  return 0;
+}
+
+/* Explore a value which of a typedef type.  */
+
+static int
+explore_typedef_value (struct value *val, const char *exp_str, int is_child)
+{
+  struct type *curr_type, *orig_type;
+  char *curr_type_name, *orig_type_name;
+  struct value *cast_value;
+  struct cleanup *cleanup_obj = make_cleanup (null_cleanup, NULL);
+
+  curr_type = value_type (val);
+  curr_type_name = get_type_name (curr_type);
+  make_cleanup (xfree, curr_type_name);
+
+  orig_type = check_typedef (curr_type);
+  orig_type_name = get_type_name (orig_type);
+  make_cleanup (xfree, orig_type_name);
+
+  printf_filtered (_("The value of '%s' is of type '%s' which is a typedef of "
+                     "type '%s'.\n"), exp_str, curr_type_name, orig_type_name);
+
+  cast_value = value_cast (orig_type, val);
+  explore_value (cast_value, exp_str, is_child);
+
+  do_cleanups (cleanup_obj);
+  return 0;
+}
+
+/* Explore a type which is a typedef of another type.  */
+
+static int
+explore_typedef_type (struct type *type, const char *path, int is_child)
+{
+  struct type *orig_type = check_typedef (type);
+  char *orig_type_name = get_type_name (orig_type);
+
+  if (is_child)
+    {
+      printf_filtered (_("The type of %s is a typedef of type '%s'.\n"), path,
+                       orig_type_name);
+    }
+  else
+    {
+      printf_filtered (_("The type '%s' is a typedef of type '%s'.\n"), path,
+                       orig_type_name);
+    }
+
+  explore_type (orig_type, path, is_child);
+  return 0;
+}
+
+/* Explore VAL of any type (or type_code). ARG_STR is the expression which
+   evaluates to VAL in the current language. IS_CHILD, in a way, denotes the
+   state of the exploration. IS_CHILD is 0 if ARG_STR is the same as the
+   expression string entered by the user, 1 otherwise. For example, the user
+   is probably exploring a variable 's' which is of a struct type with a
+   field 'a'. Then, during the exploration of s.a, the value of IS_CHILD will
+   be 1.  */
+
+static void
+explore_value (struct value *val, const char *arg_str, int is_child)
+{
+  struct type *val_type = value_type (val);
+  int repeat = 0;
+  char *type_name = NULL;
+
+  do
+    {
+      switch (TYPE_CODE (val_type))
+        {
+        case TYPE_CODE_INT:
+        case TYPE_CODE_FLT:
+        case TYPE_CODE_CHAR:
+        case TYPE_CODE_BOOL:
+        case TYPE_CODE_ENUM:
+          repeat = explore_scalar_value (val, arg_str, is_child);
+          break;
+        case TYPE_CODE_PTR:
+          repeat = explore_pointer_value (val, arg_str, is_child);
+          break;
+        case TYPE_CODE_ARRAY:
+          repeat = explore_array_value (val, arg_str, is_child);
+          break;
+        case TYPE_CODE_STRUCT:
+        case TYPE_CODE_UNION:
+          repeat = explore_compound_value (val, arg_str, is_child);
+          break;
+        case TYPE_CODE_TYPEDEF:
+          repeat = explore_typedef_value (val, arg_str, is_child);
+          break;
+        default:
+          type_name = get_type_name (val_type);
+          printf_filtered (_("Exploration of type '%s' of '%s' is not yet "
+                             "available.\n"), type_name, arg_str);
+          xfree (type_name);
+          break;
+        }
+    }
+  while (repeat);
+}
+
+/* Explore any type CURR_TYPE. PATH is the descriptive path to the entity
+   explored. IS_CHILD, in a way, denotes the state of the current exploration.
+   If CURR_TYPE is the type entered by the user, the IS_CHILD is 0, otherwise
+   it is 1. For example, when exploring the type of the field 'a' of a type
+   'struct S' (which was the actual type entered by the user), the value of
+   IS_CHILD will be 1.  */
+
+static void
+explore_type (struct type *curr_type, const char *path, int is_child)
+{
+  char *type_name = NULL;
+  int repeat = 0;
+  do
+    {
+      switch (TYPE_CODE (curr_type))
+        {
+        case TYPE_CODE_INT:
+        case TYPE_CODE_FLT:
+        case TYPE_CODE_CHAR:
+        case TYPE_CODE_BOOL:
+        case TYPE_CODE_ENUM:
+          repeat = explore_scalar_type (curr_type, path, is_child);
+          break;
+        case TYPE_CODE_PTR:
+          repeat = explore_pointer_type (curr_type, path, is_child);
+          break;
+        case TYPE_CODE_ARRAY:
+          repeat = explore_array_type (curr_type, path, is_child);
+          break;
+        case TYPE_CODE_STRUCT:
+        case TYPE_CODE_UNION:
+          repeat = explore_compound_type (curr_type, path, is_child);
+          break;
+        case TYPE_CODE_TYPEDEF:
+          repeat = explore_typedef_type (curr_type, path, is_child);
+          break;
+        default:
+          type_name = get_type_name (curr_type);
+          if (is_child)
+            {
+              printf_filtered (_("Exploration of type '%s' of '%s' is not yet "
+                                 "available.\n"), type_name, path);
+            }
+          else
+            {
+              printf_filtered (_("Exploration of type '%s' is not yet "
+                                 "available.\n"), type_name);
+            }
+          xfree (type_name);
+          break;
+        }
+    }
+  while (repeat);
+}
+
+/* Parse a string into a value in the current language. Return NULL if the
+   string STR does not evaluate to any value.  */
+
+static struct value *
+parse_as_value (char *str)
+{
+  struct gdb_exception except;
+  struct value *str_val = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ERROR)
+    {
+      str_val = parse_and_eval (str);
+    }
+  if (except.reason < 0)
+    {
+      return NULL;
+    }
+
+  return str_val;
+}
+
+/* Parse a string into a type in the current language. Return NULL if the
+   string STR does not evaluate to any type.  */
+
+static struct type *
+parse_as_type (const char *str)
+{
+  struct type *type = NULL;
+  volatile struct gdb_exception except;
+
+  if (current_language->la_language == language_c
+      || current_language->la_language == language_cplus)
+    {
+      /* This helps in exploring types like int*, int** and the like.  */
+      char *exp = xstrprintf (_("(%s *) 0"), str);
+      struct value *dummy_value = parse_as_value (exp);
+
+      xfree (exp);
+
+      if (!dummy_value)
+        {
+          return NULL;
+        }
+
+      return value_type (value_ind (dummy_value));
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!strncmp (str, "struct ", 7))
+	type = lookup_struct (str + 7, NULL);
+      else if (!strncmp (str, "union ", 6))
+	type = lookup_union (str + 6, NULL);
+      else if (!strncmp (str, "enum ", 5))
+	type = lookup_enum (str + 5, NULL);
+      else
+	type = lookup_typename (current_language, get_current_arch(),
+				str, NULL, 0);
+    }
+  if (except.reason < 0)
+    {
+      return NULL;
+    }
+
+  return type;
+}
+
+static void
+explore_command (char* arg_str, int from_tty)
+{
+  struct value *exp_val;
+  struct type *str_type;
+
+  if (!arg_str)
+    {
+      printf_filtered (_("'explore' command requires an argument.\n"));
+      return;
+    }
+
+  /* Try if ARG_STR can be parsed into a value.  */
+  exp_val = parse_as_value (arg_str);
+  if (exp_val)
+    {
+      explore_value (exp_val, arg_str, 0);
+
+      free_all_values ();
+      return;
+    }
+
+  /* Try if ARG_STR can be parsed into a type.  */
+  str_type = parse_as_type (arg_str);
+  if (str_type)
+    {
+      explore_type (str_type, arg_str, 0);
+      return;
+    }
+
+  /* If ARG_STR cannot be parsed into a value or a type, print an error
+     message.  */
+  printf_filtered (_("'%s' does not evaluate to any value or type in the "
+                     "current context.\n"), arg_str);
+}
+
+static void
+explore_value_command (char* arg_str, int from_tty)
+{
+  struct value *exp_val;
+
+  if (!arg_str)
+    {
+      printf_filtered (_("'explore value' command requires an argument.\n"));
+      return;
+    }
+
+  exp_val = parse_as_value (arg_str);
+  if (exp_val)
+    {
+      explore_value (exp_val, arg_str, 0);
+
+      free_all_values ();
+      return;
+    }
+
+  printf_filtered (_("'%s' does not evaluate to any value in the "
+                     "current context.\n"), arg_str);
+}
+
+static void
+explore_type_command (char* arg_str, int from_tty)
+{
+  struct type *str_type;
+
+  if (!arg_str)
+    {
+      printf_filtered (_("'explore type' command requires an argument.\n"));
+      return;
+    }
+
+  str_type = parse_as_type (arg_str);
+  if (str_type)
+    {
+      explore_type (str_type, arg_str, 0);
+      return;
+    }
+
+  printf_filtered (_("'%s' does not evaluate to any type in the "
+                     "current context.\n"), arg_str);
+}
+
+struct cmd_list_element *explore_list = NULL;
+
+void
+_initialize_explore (void)
+{
+  add_prefix_cmd ("explore", no_class, explore_command, _("\
+Explore values and types valid in the current context.\nUsage:\n\n\
+explore ARG\n\nARG is either a valid expression or a type name."),
+                  &explore_list, "explore ", 1, &cmdlist);
+
+  add_cmd ("value", no_class, explore_value_command, _("\
+Explore values of expressions valid in the current context.\nUsage:\n\n\
+explore value ARG\n\nARG is a valid expression."), &explore_list);
+
+  add_cmd ("type", no_class, explore_type_command, _("\
+Explore types valid in the current context.\nUsage:\n\n\
+explore type ARG\n\nARG is a valid type name."), &explore_list);
+}
diff -rupN src/gdb/Makefile.in src_explore/src/gdb/Makefile.in
--- src/gdb/Makefile.in	2012-02-07 11:51:59.459955595 +0530
+++ src_explore/src/gdb/Makefile.in	2012-02-07 11:51:49.309894099 +0530
@@ -695,7 +695,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
 	dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
 	dwarf2-frame-tailcall.c \
 	elfread.c environ.c eval.c event-loop.c event-top.c \
-	exceptions.c expprint.c \
+	exceptions.c explore.c expprint.c \
 	f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
 	findcmd.c findvar.c frame.c frame-base.c frame-unwind.c \
 	gdbarch.c arch-utils.c gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \
@@ -861,6 +861,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
 	infcmd.o infrun.o \
 	expprint.o environ.o stack.o thread.o \
 	exceptions.o \
+	explore.o \
 	filesystem.o \
 	inf-child.o \
 	interps.o \
diff -rupN src/gdb/testsuite/gdb.base/explore.c src_explore/src/gdb/testsuite/gdb.base/explore.c
--- src/gdb/testsuite/gdb.base/explore.c	1970-01-01 05:30:00.000000000 +0530
+++ src_explore/src/gdb/testsuite/gdb.base/explore.c	2012-02-04 17:29:00.055238706 +0530
@@ -0,0 +1,82 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012 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)
+{
+  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.base/explore.exp src_explore/src/gdb/testsuite/gdb.base/explore.exp
--- src/gdb/testsuite/gdb.base/explore.exp	1970-01-01 05:30:00.000000000 +0530
+++ src_explore/src/gdb/testsuite/gdb.base/explore.exp	2012-02-08 11:28:47.078627124 +0530
@@ -0,0 +1,430 @@
+# Copyright 2012 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/>.
+
+set testfile "explore"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+set SS "struct SimpleStruct"
+set SU "union SimpleUnion"
+set CS "struct ComplexStruct"
+set CU "union ComplexUnion"
+set enter_field_number_prompt  {Enter the field number of choice: }
+set return_to_parent_prompt {Press enter to return to parent value: }
+set array_index_prompt {Enter the index you want to explore in .*: }
+
+proc compound_description { value_name type_name } {
+    return "The value of '$value_name' is of type '$type_name' with the following fields:\[\r\n\]+"
+}
+
+proc typedef_description { value_name typedef_name type_name } {
+    return "The value of '$value_name' is 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 "Do you want to explore it as an array or a single value pointer \[\[\]a/s\[\]\] : " 
+    return "$type_description$prompt"
+}
+
+proc field_values { args } {
+    set result ""
+    foreach field $args {
+        set result "$result\[ \]*$field \[\.\]\[\.\] \[\(\]Value of type .*\[\)\]\[\r\n\]+"
+    }
+    return $result
+}
+
+proc field_choices { args } {
+    set result ""
+    set field_num 0
+    foreach field $args {
+        set result "$result$field\[ \]+=\[ \]+<Enter $field_num to explore this field of type .*"
+        incr field_num
+    }
+    return $result
+}
+
+proc scalar_value { value_name value } {
+    return "$value_name = $value\[r\n\]+"
+}
+
+set SS_fields [field_values {a = 10} {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} $SS].*$SS_fields"
+gdb_test "explore *ss_ptr" "[compound_description {\*ss_ptr} $SS].*$SS_fields"
+gdb_test "explore ss_t" "[typedef_description {ss_t} {SS} $SS].*[compound_description {ss_t} $SS].*$SS_fields"
+
+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 ss_ptr 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 you want to explore in 'darray_ref':.*"  {
+                pass "explore_as_array"
+                gdb_test_multiple "2" "explore_as_array_index_2" {
+                    -re ".*'darray_ref\\\[2\\\]' is a scalar value of type 'double'\..*darray_ref\\\[2\\\] = 0.*" {
+                        pass "explore_as_array_index_2"
+                        gdb_test_multiple "\0" "end explore_as_array_index_2" {
+                            -re ".*Returning to parent value.*Enter the index you want to explore in 'darray_ref':.*" {
+                                pass "end explore_as_array_index_2"
+                                gdb_test_multiple "\0" "end explore_as_array" {
+                                    -re "\[\n\r\]+" {
+                                        pass "end explore_as_array"
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } 
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore su" "" {
+    -re "[compound_description {su} {union SimpleUnion}].*[field_choices {i} {c} {f} {d}].*$enter_field_number_prompt" {
+        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 ".*[compound_description {su} {union SimpleUnion}].*[field_choices {i} {c} {f} {d}].*$enter_field_number_prompt" {
+                        pass "end su.d exploration"
+                        gdb_test_multiple "\0" "end su exploration" {
+                            -re "$gdb_prompt" {
+                                pass "end su exploration"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore cs" "" {
+    -re "[compound_description {cs} {struct ComplexStruct}].*[field_choices {s} {u} {sa}].*$enter_field_number_prompt" {
+        pass "explore cs"
+        gdb_test_multiple "0" "explore cs.s" {
+            -re "[compound_description {cs.s} {struct SimpleStruct}].*[field_values {a = 10} {d = 100[.].*}].*$return_to_parent_prompt" {
+                pass "explore cs.s"
+                gdb_test_multiple " " "end cs.s exploration" {
+                    -re ".*$enter_field_number_prompt" {
+                        pass "end cs.s exploration"
+                        gdb_test_multiple "\0" "end cs exploration" {
+                            -re "$gdb_prompt" {
+                                pass "end cs exploration"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore cu" "" {
+    -re "[compound_description {cu} {union ComplexUnion}].*[field_choices {s} {sa}].*$enter_field_number_prompt" {
+        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 SimpleStruct}].*[field_values {a = 0} {d = 100[.].*}].*$return_to_parent_prompt" {
+                        pass "explore cu.sa\[0\]"
+                        gdb_test_multiple "\0" "end cu.sa\[0\] exploration" {
+                            -re "[array_description {cu.sa} $SS]$array_index_prompt" {
+                                pass "end cu.sa\[0\] exploration"
+                            }
+                        }
+                    }
+                }
+                gdb_test_multiple "\0" "end cu.sa exploration" {
+                    -re ".*$enter_field_number_prompt" {
+                        pass "end cu.sa exploration"
+                        gdb_test_multiple "\0" "end cu exploration" {
+                            -re "$gdb_prompt" {
+                                pass "end cu exploration"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+########################
+# Type exploration tests
+########################
+
+proc scalar_type_decsription {type} {
+    return "'$type' is of a scalar type\."
+}
+
+proc child_scalar_type_description {path type} {
+    return "$path is of a scalar type '$type'\."
+}
+
+proc compound_type_description { type_name } {
+    return "'$type_name' has the following fields:"
+}
+
+proc child_compound_type_description { path type_name } {
+    return "$path is of type '$type_name' with the following fields:"
+}
+
+proc child_array_type_description { path target_type_name } {
+    return "$path is an array type with elements of type '$target_type_name'\."
+}
+
+proc typedef_type_description { type_name target_name } {
+    return "The type '$type_name' is a typedef of type '$target_name'\."
+}
+
+set SS_fields_types [field_choices {a} {d}]
+set SU_fields_types [field_choices {i} {c} {f} {d}]
+set CS_fields_types [field_choices {s} {u} {sa}]
+set CU_fields_types [field_choices {s} {sa}]
+
+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"
+
+gdb_test "explore int" ".*[scalar_type_decsription {int}].*"
+
+gdb_test_multiple "explore struct SimpleStruct" "" {
+    -re ".*[compound_type_description $SS].*$SS_fields_types.*" {
+        pass "explore struct SimpleStruct"
+        gdb_test_multiple "0" "explore type struct SimpleStruct feild 0" {
+            -re ".*[child_scalar_type_description {field 'a' of 'struct SimpleStruct'} {int}].*" {
+                pass "explore type struct SimpleStruct feild 0"
+                gdb_test_multiple "\0" "return to struct SimpleStruct from field 0" {
+                    -re ".*[compound_type_description $SS].*$SS_fields_types.*" {
+                        pass "return to struct SimpleStruct from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type struct SimpleStruct feild 1" {
+            -re ".*[child_scalar_type_description {field 'd' of 'struct SimpleStruct'} {double}].*" {
+                pass "explore type struct SimpleStruct feild 1"
+                gdb_test_multiple "\0" "return to struct SimpleStruct from field 1" {
+                    -re ".*[compound_type_description $SS].*$SS_fields_types.*" {
+                        pass "return to struct SimpleStruct from field 1"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "\0" "return to GDB prompt from struct SimpleStruct" {
+            -re "$gdb_prompt" {
+                pass "return to GDB prompt from struct SimpleStruct"
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore union SimpleUnion" "" {
+  -re ".*[compound_type_description $SU].*$SU_fields_types.*" {
+      pass "explore union SimpleUnion"
+        gdb_test_multiple "0" "explore type union SimpleUnion feild 0" {
+            -re ".*[child_scalar_type_description {field 'i' of 'union SimpleUnion'} {int}].*" {
+                pass "explore type union SimpleUnion feild 0"
+                gdb_test_multiple "\0" "return to union SimpleUnion from field 0" {
+                    -re ".*[compound_type_description $SU].*$SU_fields_types.*" {
+                        pass "return to union SimpleUnion from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type union SimpleUnion feild 1" {
+            -re ".*[child_scalar_type_description {field 'c' of 'union SimpleUnion'} {char}].*" {
+                pass "explore type union SimpleUnion feild 1"
+                gdb_test_multiple "\0" "return to union SimpleUnion from field 1" {
+                    -re ".*[compound_type_description $SU].*$SU_fields_types.*" {
+                        pass "return to union SimpleUnion from field 1"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "2" "explore type union SimpleUnion feild 2" {
+            -re ".*[child_scalar_type_description {field 'f' of 'union SimpleUnion'} {float}].*" {
+                pass "explore type union SimpleUnion feild 2"
+                gdb_test_multiple "\0" "return to union SimpleUnion from field 2" {
+                    -re ".*[compound_type_description $SU].*$SU_fields_types.*" {
+                        pass "return to union SimpleUnion from field 2"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "3" "explore type union SimpleUnion feild 3" {
+            -re ".*[child_scalar_type_description {field 'd' of 'union SimpleUnion'} {double}].*" {
+                pass "explore type union SimpleUnion feild 3"
+                gdb_test_multiple "\0" "return to union SimpleUnion from field 3" {
+                    -re ".*[compound_type_description $SU].*$SU_fields_types.*" {
+                        pass "return to union SimpleUnion from field 3"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "\0" "return to GDB prompt from union SimpleUnion" {
+            -re "$gdb_prompt" {
+                pass "return to GDB prompt from union SimpleUnion"
+            }
+        }
+  }
+}
+
+gdb_test_multiple "explore SS" "" {
+    -re ".*[typedef_type_description {SS} $SS].*[compound_type_description {SS}].*$SS_fields_types.*" {
+        pass "explore SS"
+        gdb_test_multiple "0" "explore type SS feild 0" {
+            -re ".*[child_scalar_type_description {field 'a' of 'SS'} {int}].*" {
+                pass "explore type SS feild 0"
+                gdb_test_multiple "\0" "return to SS from field 0" {
+                    -re ".*[compound_type_description {SS}].*$SS_fields_types.*" {
+                        pass "return to SS from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type SS feild 1" {
+            -re ".*[child_scalar_type_description {field 'd' of 'SS'} {double}].*" {
+                pass "explore type SS feild 1"
+                gdb_test_multiple "\0" "return to struct SimpleStruct from field 1" {
+                    -re ".*[compound_type_description {SS}].*$SS_fields_types.*" {
+                        pass "return to SS field 1"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "\0" "return to GDB prompt from SS" {
+            -re "$gdb_prompt" {
+                pass "return to GDB prompt from SS"
+            }
+        }
+    }
+}
+
+gdb_test_multiple "explore type struct ComplexStruct" "" {
+    -re ".*[compound_type_description $CS].*$CS_fields_types.*" {
+        pass "explore type struct ComplexStruct"
+        gdb_test_multiple "0" "explore type struct ComplexStruct field 0" {
+            -re ".*[child_compound_type_description $CS_field_0 $SS].*$SS_fields_types.*" {
+                pass "explore type struct ComplexStruct field 0"
+                gdb_test_multiple "\0" "return to ComplexStruct from field 0" {
+                    -re ".*[compound_type_description $CS].*$CS_fields_types.*" {
+                        pass "return to ComplexStruct from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type struct ComplexStruct field 1" {
+            -re ".*[child_compound_type_description $CS_field_1 $SU].*$SU_fields_types.*" {
+                pass "explore type struct ComplexStruct field 1"
+                gdb_test_multiple "\0" "return to ComplexStruct from field 1" {
+                    -re ".*[compound_type_description $CS].*$CS_fields_types.*" {
+                        pass "return to ComplexStruct from field 1"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "2" "explore type struct ComplexStruct field 2" {
+            -re ".*[child_array_type_description $CS_field_2 {SS}].*" {
+                pass "explore type struct ComplexStruct field 2"
+                gdb_test_multiple "\0" "return to ComplexStruct from field 2" {
+                    -re ".*[compound_type_description $CS].*$CS_fields_types.*" {
+                        pass "return to ComplexStruct from field 2"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "\0" "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].*$CU_fields_types.*" {
+        pass "explore type union ComplexUnion"
+        gdb_test_multiple "0" "explore type union ComplexStruct field 0" {
+            -re ".*[child_compound_type_description $CU_field_0 $SS].*$SS_fields_types.*" {
+                pass "explore type union ComplexUnion field 0"
+                gdb_test_multiple "\0" "return to ComplexUnion from field 0" {
+                    -re ".*[compound_type_description $CU].*$CU_fields_types.*" {
+                        pass "return to ComplexUnion from field 0"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "1" "explore type union ComplexUnion field 1" {
+            -re ".*[child_array_type_description $CU_field_1 $SS].*" {
+                pass "explore type union ComplexUnion field 1"
+                gdb_test_multiple "\0" "return to ComplexUnion array" {
+                    -re ".*[compound_type_description $CU].*$CU_fields_types.*" {
+                        pass "return to ComplexUnion from field 1"
+                    }
+                }
+            }
+        }
+        gdb_test_multiple "\0" "return to GDB prompt from ComplexUnion type exploration" {
+            -re "$gdb_prompt" {
+                pass "return to GDB prompt from ComplexUnion type exploration"
+            }
+        }
+    }
+}
diff -rupN src/gdb/testsuite/gdb.base/Makefile.in src_explore/src/gdb/testsuite/gdb.base/Makefile.in
--- src/gdb/testsuite/gdb.base/Makefile.in	2011-11-01 20:21:25.000000000 +0530
+++ src_explore/src/gdb/testsuite/gdb.base/Makefile.in	2012-02-04 12:03:13.812856890 +0530
@@ -11,7 +11,7 @@ EXECUTABLES = a2-run advance all-types a
 	chng-syms code_elim1 code_elim2 commands compiler complex \
 	condbreak consecutive constvars coremaker cursal cvexpr \
 	dbx-test del disasm-end-cu display dump dup-sect dup-sect.debug \
-	dup-sect.stripped ending-run execd-prog expand-psymtabs exprs \
+	dup-sect.stripped ending-run execd-prog expand-psymtabs explore exprs \
 	fileio find finish fixsection float foll-exec foll-fork foll-vfork \
 	frame-args freebpcmd fullname funcargs gcore \
 	gcore-buffer-overflow-012* \

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2012-02-08  9:34     ` Siva Chandra
@ 2012-02-17  7:40       ` Doug Evans
  2012-02-17 10:00         ` Siva Chandra
  2012-02-17 10:09         ` Siva Chandra
  0 siblings, 2 replies; 12+ messages in thread
From: Doug Evans @ 2012-02-17  7:40 UTC (permalink / raw)
  To: Siva Chandra; +Cc: gdb-patches

On Wed, Feb 8, 2012 at 1:33 AM, Siva Chandra <sivachandra@google.com> wrote:
> Hello,
>
> I have not yet seen anything about a policy on contributions
> implemented in extension languages. However, I have now implemented
> the 'explore' command in C. The patch is attached. I will add
> documentation after the basics of this patch are approved.
>
> 2012-02-08 Siva Chandra <sivachandra@google.com>
>
>         New command 'explore' which helps explore values in scope.
>         * explore.c: Implemention of the 'explore' command.
>         * Makefile.in: Add rules for compiling and linked explore.c.
>         * testsuite/gdb.base/explore.c: C program used for testing
>         the new 'explore' command.
>         * testsuite/gdb.base/explore.exp: Tests for the new 'explore'
>         command.
>         * testsuite/gdb.base/Makefile.in: Add explore to EXECUTABLES

Hi.
Don't be afraid to ping more often.  [E.g., Once a week is certainly
not too frequently IMO.]

I'm not sure what others prefer, but I like the python version.
http://sourceware.org/ml/gdb-patches/2011-12/msg00831.html
Please write the docs, etc. for the python version, and I'll review
and approve it (modulo the docs, but Eli is pretty responsive).


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2012-02-17  7:40       ` Doug Evans
@ 2012-02-17 10:00         ` Siva Chandra
  2012-02-17 13:14           ` Phil Muldoon
  2012-02-17 10:09         ` Siva Chandra
  1 sibling, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2012-02-17 10:00 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

> On Wed, Feb 8, 2012 at 1:33 AM, Siva Chandra <sivachandra@google.com> wrote:
>> Hello,
>>
>> I have not yet seen anything about a policy on contributions
>> implemented in extension languages. However, I have now implemented
>> the 'explore' command in C. The patch is attached. I will add
>> documentation after the basics of this patch are approved.
>>
>> 2012-02-08 Siva Chandra <sivachandra@google.com>
>>
>>         New command 'explore' which helps explore values in scope.
>>         * explore.c: Implemention of the 'explore' command.
>>         * Makefile.in: Add rules for compiling and linked explore.c.
>>         * testsuite/gdb.base/explore.c: C program used for testing
>>         the new 'explore' command.
>>         * testsuite/gdb.base/explore.exp: Tests for the new 'explore'
>>         command.
>>         * testsuite/gdb.base/Makefile.in: Add explore to EXECUTABLES

Doug > I'm not sure what others prefer, but I like the python version.
Doug > http://sourceware.org/ml/gdb-patches/2011-12/msg00831.html
Doug > Please write the docs, etc. for the python version, and I'll review
Doug > and approve it (modulo the docs, but Eli is pretty responsive).

Along with the fact that there wasn't anything available on the policy
for contributions in extensions languages, I have seen that GUILE is
now going to be another officially supported extension language. In
which case, if 'explore' can be useful to a wide audience, isn't it
better in C so that it is available to everyone?


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2012-02-17  7:40       ` Doug Evans
  2012-02-17 10:00         ` Siva Chandra
@ 2012-02-17 10:09         ` Siva Chandra
  1 sibling, 0 replies; 12+ messages in thread
From: Siva Chandra @ 2012-02-17 10:09 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

> On Wed, Feb 8, 2012 at 1:33 AM, Siva Chandra <sivachandra@google.com> wrote:
>> Hello,
>>
>> I have not yet seen anything about a policy on contributions
>> implemented in extension languages. However, I have now implemented
>> the 'explore' command in C. The patch is attached. I will add
>> documentation after the basics of this patch are approved.
>>
>> 2012-02-08 Siva Chandra <sivachandra@google.com>
>>
>>         New command 'explore' which helps explore values in scope.
>>         * explore.c: Implemention of the 'explore' command.
>>         * Makefile.in: Add rules for compiling and linked explore.c.
>>         * testsuite/gdb.base/explore.c: C program used for testing
>>         the new 'explore' command.
>>         * testsuite/gdb.base/explore.exp: Tests for the new 'explore'
>>         command.
>>         * testsuite/gdb.base/Makefile.in: Add explore to EXECUTABLES
>

Doug > I'm not sure what others prefer, but I like the python version.
Doug > http://sourceware.org/ml/gdb-patches/2011-12/msg00831.html
Doug > Please write the docs, etc. for the python version, and I'll review
Doug > and approve it (modulo the docs, but Eli is pretty responsive).

BTW, did you mean that you prefer the python version because of its
better quality, or is it because of it being in python?

Thanks,
Siva Chandra


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] A new command 'explore'
  2012-02-17 10:00         ` Siva Chandra
@ 2012-02-17 13:14           ` Phil Muldoon
  0 siblings, 0 replies; 12+ messages in thread
From: Phil Muldoon @ 2012-02-17 13:14 UTC (permalink / raw)
  To: Siva Chandra; +Cc: Doug Evans, gdb-patches

On 02/17/2012 09:55 AM, Siva Chandra wrote:
> Doug > I'm not sure what others prefer, but I like the python

> Along with the fact that there wasn't anything available
> on the policy for contributions in extensions languages, I have seen
> that GUILE is now going to be another officially supported extension
> language. In which case, if 'explore' can be useful to a wide
> audience, isn't it better in C so that it is available to everyone?

You can of course choose to submit the patch however you wish ... you
are the author!  But I do not think the discussions on GUILE should
preclude any continuing effort on commands written in Python.  Not a
single API exists for GUILE at the moment. So my 2 cents, I agree with
Doug, I liked the Python version better.

Cheers,

Phil



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2012-02-17 12:03 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CAGyQ6gyAwr2zuv3NYpdk09vPyYpuFkyiErEhw-vws-WfuLDxiw@mail.gmail.com>
2011-12-19 19:05 ` [RFC] A new command 'explore' Siva Chandra
2011-12-21 19:36   ` Tom Tromey
2011-12-22 11:00     ` Siva Chandra
2011-12-22  5:21   ` Joel Brobecker
2011-12-22 11:48     ` Siva Chandra
2011-12-22 12:53       ` Joel Brobecker
2011-12-27  6:29   ` Siva Chandra
2012-02-08  9:34     ` Siva Chandra
2012-02-17  7:40       ` Doug Evans
2012-02-17 10:00         ` Siva Chandra
2012-02-17 13:14           ` Phil Muldoon
2012-02-17 10:09         ` Siva Chandra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox