From 06fc020f10ddb58fc73d9d2a20cde274f14111e4 Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Wed, 11 Apr 2012 05:50:44 +0000 Subject: [PATCH] 2012-04-02 Siva Chandra Reddy New command 'explore' which helps explore values and types in scope. * NEWS: Add an entry about the new 'explore' command. * 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. * doc/gdb.texinfo (Examining Data): Document the 'explore' command. * 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 on C constructs. * testsuite/gdb.python/py-explore.cc: C++ program used for testing the new 'explore' command on C++ constructs. * testsuite/gdb-python/py-explore.exp: Tests for the new 'explore' command on C constructs. * testsuite/gdb-python/py-explore-cc.exp: Tests for the new 'explore' command on C++ constructs. --- gdb/ChangeLog | 9 + gdb/NEWS | 5 + gdb/data-directory/Makefile.in | 3 +- gdb/doc/ChangeLog | 4 + gdb/doc/gdb.texinfo | 147 ++++++ gdb/python/lib/gdb/command/explore.py | 755 +++++++++++++++++++++++++++++ gdb/testsuite/ChangeLog | 13 + gdb/testsuite/gdb.python/Makefile.in | 3 +- gdb/testsuite/gdb.python/py-explore-cc.exp | 146 ++++++ gdb/testsuite/gdb.python/py-explore.c | 82 ++++ gdb/testsuite/gdb.python/py-explore.cc | 54 +++ gdb/testsuite/gdb.python/py-explore.exp | 469 ++++++++++++++++++ 12 files changed, 1688 insertions(+), 2 deletions(-) create mode 100644 gdb/python/lib/gdb/command/explore.py create mode 100644 gdb/testsuite/gdb.python/py-explore-cc.exp create mode 100644 gdb/testsuite/gdb.python/py-explore.c create mode 100644 gdb/testsuite/gdb.python/py-explore.cc create mode 100644 gdb/testsuite/gdb.python/py-explore.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 83dfa08..9e3e135 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2012-04-11 Siva Chandra Reddy + + New command 'explore' which helps explore values and types in + scope. + * NEWS: Add an entry about the new 'explore' command. + * 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. + 2011-04-10 Maciej W. Rozycki * mips-tdep.c (mips_skip_pic_trampoline_code): Correct sign diff --git a/gdb/NEWS b/gdb/NEWS index 5885d32..c287ffa 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -93,6 +93,11 @@ ** "info vtbl" can be used to show the virtual method tables for C++ and Java objects. + ** "explore" and its sub commands "explore value" and "explore type" + can be used to reccursively explore values and types of + expressions. These commands are available only if GDB is + configured with '--with-python'. + * New targets Renesas RL78 rl78-*-elf diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 4296e5a..87c6dd4 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -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 --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 885b46b..ecb3409 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2012-04-11 Siva Chandra Reddy + + * gdb.texinfo (Examining Data): Document the 'explore' command. + 2012-03-28 Joel Brobecker * gdb.texinfo (GDB/MI Variable Objects): Document what happens diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 8002429..37ffbb1 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7198,6 +7198,153 @@ fields of a struct or a class are declared, use the @code{ptype @var{exp}} command rather than @code{print}. @xref{Symbols, ,Examining the Symbol Table}. +@cindex exploring hierarchical data structures +@kindex explore +Another way of examining values of expressions and type information is +through the Python extension command @code{explore} (available only if +the @value{GDBN} build is configured with @code{--with-python}). It +offers an interactive way to start at the highest level (or, 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/fields +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. +@end table + +The working of the @code{explore} command can be illustrated with an +example. If a data type @code{struct ComplexStruct} is defined in your +C program as + +@smallexample +struct SimpleStruct +@{ + int i; + double d; +@}; + +struct ComplexStruct +@{ + struct SimpleStruct *ss_p; + int arr[10]; +@}; +@end smallexample + +@noindent +followed by variable declarations as + +@smallexample +struct SimpleStruct ss = @{ 10, 1.11 @}; +struct ComplexStruct cs = @{ &ss, @{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 @} @}; +@end smallexample + +@noindent +then, the value of the variable @code{cs} can be explored using the +@code{explore} command as follows. + +@smallexample +(gdb) explore cs +The value of `cs' is a struct/class of type `struct ComplexStruct' with +the following fields: + + ss_p = + arr = + +Enter the field number of choice: +@end smallexample + +@noindent +Since the fields of @code{cs} are not scalar values, you are being +prompted to chose the field you want to explore. Let's say you choose +the field @code{ss_p} by entering @code{0}. Then, since this field is a +pointer, you will be asked if it is pointing to a single value. From +the declaration of @code{cs} above, it is indeed pointing to a single +value, hence you enter @code{y}. If you enter @code{n}, then you will +be asked if it were pointing to an array of values, in which case this +field will be explored as if it were an array. + +@smallexample +`cs.ss_p' is a pointer to a value of type `struct SimpleStruct' +Continue exploring it as a pointer to a single value [y/n]: y +The value of `*(cs.ss_p)' is a struct/class of type `struct +SimpleStruct' with the following fields: + + i = 10 .. (Value of type `int') + d = 1.1100000000000001 .. (Value of type `double') + +Press enter to return to parent value: +@end smallexample + +@noindent +If the field @code{arr} of @code{cs} was chosen for exploration by +entering @code{1} earlier, then since it is as array, you will be +prompted to enter the index of the element in the array that you want +to explore. + +@smallexample +`cs.arr' is an array of `int'. +Enter the index of the element you want to explore in `cs.arr': 5 + +`(cs.arr)[5]' is a scalar value of type `int'. + +(cs.arr)[5] = 4 + +Press enter to return to parent value: +@end smallexample + +In general, at any stage of exploration, you can go deeper towards the +leaf values by responding to the prompts appropriately, or hit the +return key to return to the enclosing data structure (the @i{higher} +level data structure). + +Similar to exploring values, you can use the @code{explore} command to +explore types. Instead of specifying a value (which is typically a +variable name or an expression valid in the current context of the +program being debugged), you specify a type name. If you consider the +same example as above, your can explore the type +@code{struct ComplexStruct} by passing the argument +@code{struct ComplexStruct} to the @code{explore} command. + +@smallexample +(gdb) explore struct ComplexStruct +@end smallexample + +@noindent +By responding to the prompts appropriately in the subsequent interactive +session, you can explore the type @code{struct ComplexStruct} in a +manner similar to how the value @code{cs} was explored in the above +example. + +The @code{explore} command also has two sub-commands, +@code{explore value} and @code{explore type}. The former sub-command is +a way to explicitly specify that value exploration of the argument is +being invoked, while the latter is a way to explicitly specify that type +exploration of the argument is being invoked. + +@table @code +@item explore value @var{expr} +@cindex explore value +This sub-command of @code{explore} explores the value of the +expression @var{expr} (if @var{expr} is an expression valid in the +current context of the program being debugged). The behavior of this +command is identical to that of the behavior of the @code{explore} +command being passed the argument @var{expr}. + +@item explore type @var{arg} +@cindex explore type +This sub-command of @code{explore} explores the type of @var{arg} (if +@var{arg} is a type visible in the current context of program being +debugged), or the type of the value/expression @var{arg} (if @var{arg} +is an expression valid in the current context of the program being +debugged). If @var{arg} is a type, then the behavior of this command is +identical to that of the @code{explore} command being passed the +argument @var{arg}. If @var{arg} is an expression, then the behavior of +this command will be identical to that of the @code{explore} command +being passed the type of @var{arg} as the argument. +@end table + @menu * Expressions:: Expressions * Ambiguous Expressions:: Ambiguous Expressions diff --git a/gdb/python/lib/gdb/command/explore.py b/gdb/python/lib/gdb/command/explore.py new file mode 100644 index 0000000..aad3671 --- /dev/null +++ b/gdb/python/lib/gdb/command/explore.py @@ -0,0 +1,755 @@ +# GDB 'explore' command. +# Copyright (C) 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 . + +"""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 guard_expr(expr): + length = len(expr) + guard = False + + if expr[0] == '(' and expr[length-1] == ')': + pass + else: + i = 0 + while i < length: + c = expr[i] + if (c == '_' or ('a' <= c and c <= 'z') or + ('A' <= c and c <= 'Z') or ('0' <= c and c <= '9')): + pass + else: + guard = True + break + i += 1 + + if guard: + return "(" + expr + ")" + else: + return expr + + @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: + 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_REF : ReferenceExplorer, + 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 return_to_parent_value(): + """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: ") + + @staticmethod + def return_to_enclosing_type(): + """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: ") + + +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'." % + (expr, value.type)) + print "%s = %s" % (expr, str(value)) + + if is_child: + Explorer.return_to_parent_value_prompt() + Explorer.return_to_parent_value() + + 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 scalar type '%s'." % + (name, str(datatype))) + else: + print "'%s' is a scalar type." % name + + if is_child: + Explorer.return_to_enclosing_type_prompt() + Explorer.return_to_enclosing_type() + + 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. + """ + print ("'%s' is a pointer to a value of type '%s'" % + (expr, str(value.type.target()))) + option = raw_input("Continue exploring it as a pointer to a single " + "value [y/n]: ") + if option == "y": + 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" % Explorer.guard_expr(expr), + deref_value, is_child) + return False + + option = raw_input("Continue exploring it as a pointer to an " + "array [y/n]: ") + if option == "y": + while True: + index = 0 + try: + index = int(raw_input("Enter the index of the element you " + "want to explore in '%s': " % expr)) + except ValueError: + break + element_expr = "%s[%d]" % (Explorer.guard_expr(expr), index) + element = value[index] + try: + str(element) + except gdb.MemoryError: + print "Cannot read value at index %d." % index + continue + Explorer.explore_expr(element_expr, element, True) + return False + + if is_child: + Explorer.return_to_parent_value() + 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 a pointer to a value of type '%s'." % + (name, str(target_type))) + + Explorer.explore_type("the pointee type of %s" % name, + target_type, + is_child) + return False + + +class ReferenceExplorer(object): + """Internal class used to explore reference (TYPE_CODE_REF) values.""" + + @staticmethod + def explore_expr(expr, value, is_child): + """Function to explore array values. + See Explorer.explore_expr for more information. + """ + referenced_value = value.referenced_value() + Explorer.explore_expr(expr, referenced_value, is_child) + 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() + Explorer.explore_type(name, target_type, is_child) + return False + + +class ArrayExplorer(object): + """Internal class used to explore arrays.""" + + @staticmethod + def explore_expr(expr, value, is_child): + """Function to explore array values. + See Explorer.explore_expr for more information. + """ + target_type = value.type.target() + print ("'%s' is an array of '%s'." % (expr, str(target_type))) + index = 0 + try: + index = int(raw_input("Enter the index of the element you want to " + "explore in '%s': " % expr)) + except ValueError: + if is_child: + Explorer.return_to_parent_value() + 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[%d]" % (Explorer.guard_expr(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() + print "%s is an array of '%s'." % (name, str(target_type)) + + Explorer.explore_type("the array element of %s" % name, target_type, + is_child) + return False + + +class CompoundExplorer(object): + """Internal class used to explore struct, classes and unions.""" + + @staticmethod + def _print_fields(print_list): + """Internal function which prints the fields of a struct/class/union. + """ + max_field_name_length = 0 + for pair in print_list: + if max_field_name_length < len(pair[0]): + max_field_name_length = len(pair[0]) + + format_str = " {0:>%d} = {1}" % max_field_name_length + for pair in print_list: + print format_str.format(pair[0], pair[1]) + + @staticmethod + def _get_real_field_count(fields): + real_field_count = 0; + for field in fields: + if not field.artificial: + real_field_count = real_field_count + 1 + + return real_field_count + + @staticmethod + def explore_expr(expr, value, is_child): + """Function to explore structs/classes and union values. + See Explorer.explore_expr for more information. + """ + datatype = value.type + type_code = datatype.code + fields = datatype.fields() + + if type_code == gdb.TYPE_CODE_STRUCT: + type_desc = "struct/class" + else: + type_desc = "union" + + if CompoundExplorer._get_real_field_count(fields) == 0: + print ("The value of '%s' is a %s of type '%s' with no fields." % + (expr, type_desc, str(value.type))) + if is_child: + Explorer.return_to_parent_value_prompt() + return False + + print ("The value of '%s' is a %s of type '%s' with the following " + "fields:\n" % (expr, type_desc, str(value.type))) + + has_explorable_fields = False + choice_to_compound_field_map = { } + current_choice = 0 + print_list = [ ] + for field in fields: + if field.artificial: + continue + field_full_name = Explorer.guard_expr(expr) + "." + field.name + if field.is_base_class: + field_value = value.cast(field.type) + else: + field_value = value[field.name] + literal_value = "" + if type_code == gdb.TYPE_CODE_UNION: + literal_value = ("" % (current_choice, str(field.type))) + has_explorable_fields = True + else: + if Explorer.is_scalar_type(field.type): + literal_value = ("%s .. (Value of type '%s')" % + (str(field_value), str(field.type))) + else: + if field.is_base_class: + field_desc = "base class" + else: + field_desc = "field" + literal_value = ("" % + (current_choice, field_desc, + str(field.type))) + has_explorable_fields = True + + choice_to_compound_field_map[str(current_choice)] = ( + field_full_name, field_value) + current_choice = current_choice + 1 + + print_list.append((field.name, literal_value)) + + CompoundExplorer._print_fields(print_list) + print "" + + if has_explorable_fields: + choice = raw_input("Enter the field number of choice: ") + 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 + type_desc = "" + if type_code == gdb.TYPE_CODE_STRUCT: + type_desc = "struct/class" + else: + type_desc = "union" + + fields = datatype.fields() + if CompoundExplorer._get_real_field_count(fields) == 0: + if is_child: + print ("%s is a %s of type '%s' with no fields." % + (name, type_desc, str(datatype))) + Explorer.return_to_enclosing_type_prompt() + else: + print "'%s' is a %s with no fields." % (name, type_desc) + return False + + if is_child: + print ("%s is a %s of type '%s' " + "with the following fields:\n" % + (name, type_desc, str(datatype))) + else: + print ("'%s' is a %s with the following " + "fields:\n" % + (name, type_desc)) + + has_explorable_fields = False + current_choice = 0 + choice_to_compound_field_map = { } + print_list = [ ] + for field in fields: + if field.artificial: + continue + if field.is_base_class: + field_desc = "base class" + else: + field_desc = "field" + rhs = ("" % + (current_choice, field_desc, str(field.type))) + print_list.append((field.name, rhs)) + choice_to_compound_field_map[str(current_choice)] = ( + field.name, field.type, field_desc) + current_choice = current_choice + 1 + + CompoundExplorer._print_fields(print_list) + print "" + + if len(choice_to_compound_field_map) > 0: + choice = raw_input("Enter the field number of choice: ") + if choice in choice_to_compound_field_map: + if is_child: + new_name = ("%s '%s' of %s" % + (choice_to_compound_field_map[choice][2], + choice_to_compound_field_map[choice][0], + name)) + else: + new_name = ("%s '%s' of '%s'" % + (choice_to_compound_field_map[choice][2], + choice_to_compound_field_map[choice][0], + name)) + Explorer.explore_type(new_name, + choice_to_compound_field_map[choice][1], True) + return True + else: + if is_child: + Explorer.return_to_enclosing_type() + 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 ("The value of '%s' is 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() + if is_child: + print ("The type of %s is a typedef of type '%s'." % + (name, str(actual_type))) + else: + print ("The type '%s' 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' 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): + """Explore a value or a type valid in the current context. + + Usage: + + explore ARG + + - ARG is either a valid expression or a type name. + - At any stage of exploration, hit the return key (instead of a + choice, if any) to return to the enclosing type or value. + """ + + 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): + """Explore value of an expression valid in the current context. + + Usage: + + explore value ARG + + - ARG is a valid expression. + - At any stage of exploration, hit the return key (instead of a + choice, if any) to return to the enclosing value. + """ + + 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): + """Explore a type or the type of an expression valid in the current + context. + + Usage: + + explore type ARG + + - ARG is a valid expression or a type name. + - At any stage of exploration, hit the return key (instead of a + choice, if any) to return to the enclosing type. + """ + + 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 not None: + Explorer.explore_type(arg_str, datatype, False) + return + + value = ExploreUtils.get_value_from_str(arg_str) + if value is not None: + print "'%s' is of type '%s'." % (arg_str, str(value.type)) + Explorer.explore_type(str(value.type), value.type, False) + + raise gdb.GdbError(("'%s' is not a type or value in the current " + "context." % arg_str)) + + +Explorer.init_env() + +ExploreCommand() +ExploreValueCommand() +ExploreTypeCommand() diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 4aa7f64..422ac8b 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,16 @@ +2012-04-02 Siva Chandra Reddy + + * gdb.python/Makefile.in: Add py-explore and py-explore-cc to + EXECUTABLES. + * gdb.python/py-explore.c: C program used for testing the new + 'explore' command on C constructs. + * gdb.python/py-explore.cc: C++ program used for testing the new + 'explore' command on C++ constructs. + * gdb-python/py-explore.exp: Tests for the new 'explore' + command on C constructs. + * gdb-python/py-explore-cc.exp: Tests for the new 'explore' + command on C++ constructs. + 2012-04-07 Mark Kettenis * gdb.base/funcargs.exp (complex_args): Fix typo. diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in index cf9c142..511df95 100644 --- a/gdb/testsuite/gdb.python/Makefile.in +++ b/gdb/testsuite/gdb.python/Makefile.in @@ -5,7 +5,8 @@ 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-finish-breakpoint py-finish-breakpoint2 py-value-cc + py-finish-breakpoint py-finish-breakpoint2 py-value-cc py-explore \ + py-explore-cc MISCELLANEOUS = py-shared-sl.sl py-events-shlib.so py-events-shlib-nodebug.so diff --git a/gdb/testsuite/gdb.python/py-explore-cc.exp b/gdb/testsuite/gdb.python/py-explore-cc.exp new file mode 100644 index 0000000..afcf453 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-explore-cc.exp @@ -0,0 +1,146 @@ +# Copyright (C) 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 . + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing values to Python. + +if { [skip_cplus_tests] } { continue } + +set testfile "py-explore" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} + +if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +set int_ptr_ref_desc "The value of 'int_ptr_ref' is of type 'int_ptr' which is a typedef of type 'int \\*'.*\'int_ptr_ref' is a pointer to a value of type 'int'.*" + +set b_desc "The value of 'b' is a struct/class of type 'B' with the following fields:.*\ +A = .*\ +i = 10 \\.\\. \\(Value of type 'int'\\).*\ +c = 97 'a' \\.\\. \\(Value of type 'char'\\).*" + +set B_desc "'B' is a struct/class with the following fields:.*\ +A = .*\ +i = .*\ +c = .*" + +if ![runto_main] { + return -1 +} + +gdb_breakpoint [gdb_get_line_number "Break here."] +gdb_continue_to_breakpoint "Break here" ".*Break here.*" + +gdb_test "explore A" "'A' is a struct/class with no fields\." +gdb_test "explore a" "The value of 'a' is a struct/class of type 'const A' with no fields\." +gdb_test "explore int_ref" "'int_ref' is a scalar value of type 'int'.*int_ref = 10" + +gdb_test_multiple "explore int_ptr_ref" "" { + -re "$int_ptr_ref_desc.*Continue exploring it as a pointer to a single value \\\[y/n\\\]:.*" { + pass "explore int_ptr_ref" + gdb_test_multiple "y" "explore_int_ptr_ref_as_single_value_pointer" { + -re "'\[*\]int_ptr_ref' is a scalar value of type 'int'.*\[*\]int_ptr_ref = 10.*$gdb_prompt" { + pass "explore_int_ptr_ref_as_single_value_pointer" + } + } + } +} + +gdb_test_multiple "explore b" "" { + -re "$b_desc.*Enter the field number of choice:.*" { + pass "explore b" + gdb_test_multiple "0" "explore_base_class_A" { + -re "The value of 'b\.A' is a struct/class of type 'A' with no fields\." { + pass "explore_base_class_A" + gdb_test_multiple "\0" "return_to_b_from_A" { + -re ".*$b_desc.*Enter the field number of choice:.*" { + pass "return_to_b_from_A" + gdb_test_multiple "1" "explore_field_i_of_b" { + -re "'b\.i' is a scalar value of type 'int'.*b\.i = 10.*" { + pass "explore_field_i_of_b" + gdb_test_multiple "\0" "return_to_b_from_i" { + -re "$b_desc.*Enter the field number of choice:.*" { + pass "return_to_b_from_i" + } + } + } + } + gdb_test_multiple "2" "explore_field_c_of_b" { + -re "'b\.c' is a scalar value of type 'char'.*b\.c = .*'a'.*" { + pass "explore_field_c_of_b" + gdb_test_multiple "\0" "return_to_b_from_c" { + -re "$b_desc.*Enter the field number of choice:.*" { + pass "return_to_b_from_i" + } + } + } + } + gdb_test_multiple "\0" "return_to_gdb_prompt" { + -re "$gdb_prompt" { + pass "return_to_gdb_prompt_from_b" + } + } + } + } + } + } + } +} + +gdb_test_multiple "explore B" "" { + -re "$B_desc.*Enter the field number of choice:.*" { + pass "explore B" + gdb_test_multiple "0" "explore_base_class_A" { + -re "base class 'A' of 'B' is a struct/class of type 'A' with no fields\." { + pass "explore_base_class_A" + gdb_test_multiple "\0" "return_to_B" { + -re "$B_desc.*Enter the field number of choice:.*" { + pass "return_to_B" + gdb_test_multiple "1" "explore_field_i_of_B" { + -re "field 'i' of 'B' is of a scalar type 'int'.*" { + pass "explore_field_i_of_B" + gdb_test_multiple "\0" "return_to_B_from_i" { + -re "$B_desc.*Enter the field number of choice:.*" { + pass "return_to_B_from_i" + } + } + } + } + gdb_test_multiple "2" "explore_field_c_of_B" { + -re "field 'c' of 'B' is of a scalar type 'char'.*" { + pass "explore_field_c_of_B" + gdb_test_multiple "\0" "return_to_B_from_c" { + -re "$B_desc.*Enter the field number of choice:.*" { + pass "return_to_B_from_c" + } + } + } + } + gdb_test_multiple "\0" "return_to_gdb_prompt" { + -re "$gdb_prompt" { + pass "return_to_gdb_prompt_from_B" + } + } + } + } + } + } + } +} diff --git a/gdb/testsuite/gdb.python/py-explore.c b/gdb/testsuite/gdb.python/py-explore.c new file mode 100644 index 0000000..9dcf0e9 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-explore.c @@ -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 --git a/gdb/testsuite/gdb.python/py-explore.cc b/gdb/testsuite/gdb.python/py-explore.cc new file mode 100644 index 0000000..420ba57 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-explore.cc @@ -0,0 +1,54 @@ +/* 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 . +*/ + +class A { + public: + virtual ~A() { } +}; + +class B : public A { + public: + virtual ~B() { } + + int i; + char c; +}; + +typedef int *int_ptr; + +int +func (const A &a) +{ + int val = 10; + int &int_ref = val; + int_ptr ptr = &val; + int_ptr &int_ptr_ref = ptr; + B b; + + b.i = 10; + b.c = 'a'; + + return 0; /* Break here. */ +} + +int +main () +{ + A obj; + return func (obj); +} diff --git a/gdb/testsuite/gdb.python/py-explore.exp b/gdb/testsuite/gdb.python/py-explore.exp new file mode 100644 index 0000000..5ba98e9 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-explore.exp @@ -0,0 +1,469 @@ +# 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 "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 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 of the element 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 '$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 "Continue exploring it as a pointer to a single value \[\[\]y/n\[\]\]: " + 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\[ \]+=\[ \]+