diff -rupN src/gdb/doc/gdb.texinfo src_explore_c/gdb/doc/gdb.texinfo --- src/gdb/doc/gdb.texinfo 2012-02-10 06:54:58.000000000 +0530 +++ src_explore_c/gdb/doc/gdb.texinfo 2012-03-03 17:49:33.002127001 +0530 @@ -7055,8 +7055,10 @@ instruction. @cindex printing data @cindex examining data +@cindex exploring data @kindex print @kindex inspect +@kindex explore @c "inspect" is not quite a synonym if you are using Epoch, which we do not @c document because it is nonstandard... Under Epoch it displays in a @c different window or something like that. @@ -7093,6 +7095,31 @@ fields of a struct or a class are declar command rather than @code{print}. @xref{Symbols, ,Examining the Symbol Table}. +Another way of examining values of expressions and type information is +through the @code{explore} command. It offers an interactive way to +start at the highest level (the most abstract level) of the data +type of an expression (or, the data type itself) and explore all the way +down to leaf scalar values embedded in the higher level data types. + +@table @code +@item explore @var{arg} +@var{arg} is either an expression (in the source language), or a type +visible in the current context of the program being debugged. + +@item explore value @var{expr} +@cindex explore value +This variation lets one explicitly specify that value exploration is +being invoked on the expression @var{expr}. @var{expr} should be an +expression valid in the current context of the program being debugged. + +@item explore type @var{arg} +@cindex explore type +This variation lets one explicitly specify that type exploration is +being invoked on @var{arg} which can be either be an expression or a +type. If it were an expression, then the type of value of the +expression is explored. +@end table + @menu * Expressions:: Expressions * Ambiguous Expressions:: Ambiguous Expressions diff -rupN src/gdb/explore.c src_explore_c/gdb/explore.c --- src/gdb/explore.c 1970-01-01 05:30:00.000000000 +0530 +++ src_explore_c/gdb/explore.c 2012-03-05 16:05:11.953541122 +0530 @@ -0,0 +1,1165 @@ +/* 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 . */ + +#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; +} + +/* 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 = type_to_string (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) +{ + const char *type_desc; + if (TYPE_CODE (type) == TYPE_CODE_ENUM) + { + type_desc = "an enum"; + } + else + { + type_desc = "a scalar"; + } + + if (is_child) + { + char *type_name = type_to_string (type); + printf_filtered (_("%s is of %s type '%s'.\n"), path, type_desc, + type_name); + return_to_parent_type_prompt (); + return_to_parent_type (); + + xfree (type_name); + } + else + { + printf_filtered (_("'%s' is %s type.\n"), path, type_desc); + } + + 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 = type_to_string (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 = type_to_string (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 = type_to_string (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 = type_to_string (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 +{ + /* The name of the field. */ + + const char *field_name; + + /* The field value. Valid only for value exploration. */ + + struct value *field_value; + + /* The field type. */ + + struct type *field_type; + + /* 1 if this field corresponds to a base class, 0 otherwise. Used for + C++ structs/classes. */ + + int is_base_class; +}; + +/* 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; + + field_type_name = type_to_string (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 + { + const char *field_desc_str; + if (fields[i].is_base_class) + { + field_desc_str = "base class"; + } + else + { + field_desc_str = "field"; + } + + printf_filtered (_("\n"), + i, field_desc_str, 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, *type_desc; + enum type_code val_type_code = TYPE_CODE (val_type); + int nfields = TYPE_NFIELDS (val_type); + int n_baseclasses = 0; + 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 = type_to_string (val_type); + make_cleanup (xfree, type_name); + + if (val_type_code == TYPE_CODE_STRUCT) + { + type_desc = "struct/class"; + } + else + { + type_desc = "union"; + } + + printf_filtered (_("The value of '%s' is a %s of type '%s' with the " + "following fields:\n"), exp_str, type_desc, type_name); + + if (HAVE_CPLUS_STRUCT (val_type)) + { + n_baseclasses = TYPE_N_BASECLASSES (val_type); + } + + 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 (i < n_baseclasses) + { + fields[i].is_base_class = 1; + } + else + { + fields[i].is_base_class = 0; + } + + 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; + const char *type_desc; + enum type_code typecode = TYPE_CODE (type); + int nfields = TYPE_NFIELDS (type); + int n_baseclasses = 0; + 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 = type_to_string (type); + make_cleanup (xfree, type_name); + + if (typecode == TYPE_CODE_STRUCT) + { + type_desc = "struct/class"; + } + else + { + type_desc = "union"; + } + + if (is_child) + { + printf_filtered (_("%s is a %s of type '%s' with the following " + "fields:\n"), path, type_desc, type_name); + } + else + { + printf_filtered (_("'%s' is a %s with the following fields:\n"), + path, type_desc); + } + + if (HAVE_CPLUS_STRUCT (type)) + { + n_baseclasses = TYPE_N_BASECLASSES (type); + } + + 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; + if (i < n_baseclasses) + { + fields[i].is_base_class = 1; + } + else + { + fields[i].is_base_class = 0; + } + } + + print_fields (fields, nfields, typecode, 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 reference value (C++). */ + +static int +explore_reference_value (struct value *val, const char *exp_str, int is_child) +{ + struct type *deref_type = TYPE_TARGET_TYPE (value_type (val)); + char *deref_type_name = type_to_string (deref_type); + + printf_filtered (_("'%s' is a reference to a value of type '%s'.\n"), + exp_str, deref_type_name); + + explore_value (coerce_ref (val), exp_str, is_child); + + xfree (deref_type_name); + return 0; +} + +/* Explore a reference type (C++). */ + +static int +explore_reference_type (struct type *type, const char *path, int is_child) +{ + struct type *target_type = TYPE_TARGET_TYPE (type); + char *type_name = type_to_string (target_type); + char *new_path = NULL; + + printf_filtered (_("%s is of a reference to a value of type " + "'%s'.\n"), path, type_name); + + if (is_child) + { + new_path = xstrprintf (_("%s"), path); + } + else + { + new_path = xstrprintf (_("%s"), type_name); + } + + explore_type (target_type, new_path, is_child); + + xfree (type_name); + xfree (new_path); + 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 = type_to_string (curr_type); + make_cleanup (xfree, curr_type_name); + + orig_type = check_typedef (curr_type); + orig_type_name = type_to_string (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 = type_to_string (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_REF: + repeat = explore_reference_value (val, arg_str, is_child); + break; + case TYPE_CODE_TYPEDEF: + repeat = explore_typedef_value (val, arg_str, is_child); + break; + default: + type_name = type_to_string (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_REF: + repeat = explore_reference_type (curr_type, path, is_child); + break; + case TYPE_CODE_TYPEDEF: + repeat = explore_typedef_type (curr_type, path, is_child); + break; + default: + type_name = type_to_string (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; + struct value *exp_val; + + 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; + } + + /* If it cannot be evaluated into a type, try to evaluate it into a + value. */ + exp_val = parse_as_value (arg_str); + if (exp_val) + { + struct type *exp_type = value_type (exp_val); + char *type_name = (char *) type_to_string (exp_type); + + printf_filtered (_("'%s' is of type '%s'.\n"), arg_str, type_name); + explore_type (exp_type, type_name, 0); + + free_all_values (); + xfree (type_name); + return; + } + + printf_filtered (_("'%s' does not evaluate to any value or 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 a value or a type valid in the current context.\n\ +Usage:\n\n\ +explore ARG\n\n\ +- ARG is either a valid expression or a type name.\n\ +- At any stage of exploration, hit the return key (instead of a\n\ + choice, if any) to return to the enclosing type or value. Entering\n\ + an invalid input will also result in similar behaviour."), + &explore_list, "explore ", 1, &cmdlist); + + add_cmd (_("value"), no_class, explore_value_command, _("\ +Explore value of an expression valid in the current context.\n\ +Usage:\n\n\ +explore value ARG\n\n\ +- ARG is a valid expression.\n\ +- At any stage of exploration, hit the return key (instead of a\n\ + choice, if any) to return to the enclosing value. Entering\n\ + an invalid input will also result in similar behaviour."), + &explore_list); + + add_cmd (_("type"), no_class, explore_type_command, _("\ +Explore a type or the type of an expression valid in the current context.\n\ +Usage:\n\n\ +explore type ARG\n\n\ +- ARG is a valid expression or a type name.\n\ +- At any stage of exploration, hit the return key (instead of a\n\ + choice, if any) to return to the enclosing type. Entering\n\ + an invalid input will also result in similar behaviour."), + &explore_list); +} diff -rupN src/gdb/Makefile.in src_explore_c/gdb/Makefile.in --- src/gdb/Makefile.in 2012-02-09 21:36:43.000000000 +0530 +++ src_explore_c/gdb/Makefile.in 2012-02-19 11:47:43.093098943 +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 \ @@ -859,7 +859,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $ block.o symtab.o psymtab.o symfile.o symmisc.o linespec.o dictionary.o \ infcall.o \ infcmd.o infrun.o \ - expprint.o environ.o stack.o thread.o \ + explore.o expprint.o environ.o stack.o thread.o \ exceptions.o \ filesystem.o \ inf-child.o \ diff -rupN src/gdb/NEWS src_explore_c/gdb/NEWS --- src/gdb/NEWS 2012-03-05 16:55:36.652342189 +0530 +++ src_explore_c/gdb/NEWS 2012-03-05 16:58:50.692329574 +0530 @@ -49,6 +49,11 @@ ** "catch load" and "catch unload" can be used to stop when a shared library is loaded or unloaded, respectively. + ** "explore" and its sub commands "explore value" and "explore type" + can be used to recurrsively explore values and types of + expressions. The command "explore type" can be used to explore + types directly. + *** Changes in GDB 7.4 * GDB now handles ambiguous linespecs more consistently; the existing diff -rupN src/gdb/testsuite/gdb.base/explore.c src_explore_c/gdb/testsuite/gdb.base/explore.c --- src/gdb/testsuite/gdb.base/explore.c 1970-01-01 05:30:00.000000000 +0530 +++ src_explore_c/gdb/testsuite/gdb.base/explore.c 2012-02-19 11:42:27.493097731 +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 . +*/ + +#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_c/gdb/testsuite/gdb.base/explore.exp --- src/gdb/testsuite/gdb.base/explore.exp 1970-01-01 05:30:00.000000000 +0530 +++ src_explore_c/gdb/testsuite/gdb.base/explore.exp 2012-02-26 12:21:09.854970415 +0530 @@ -0,0 +1,461 @@ +# 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 . + +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_desc type_name } { + return "The value of '$value_name' is a $type_desc 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\[ \]+=\[ \]+