| # GDB 'explore' command. |
| # Copyright (C) 2012-2013 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 |
| import sys |
| |
| if sys.version_info[0] > 2: |
| # Python 3 renamed raw_input to input |
| raw_input = input |
| |
| 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]) |
| |
| for pair in print_list: |
| print (" %*s = %s" % (max_field_name_length, 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 = ("<Enter %d to explore this field of type " |
| "'%s'>" % (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 = ("<Enter %d to explore this %s of type " |
| "'%s'>" % |
| (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.return_to_parent_value() |
| 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 = ("<Enter %d to explore this %s of type '%s'>" % |
| (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) |
| return |
| |
| raise gdb.GdbError(("'%s' is not a type or value in the current " |
| "context." % arg_str)) |
| |
| |
| Explorer.init_env() |
| |
| ExploreCommand() |
| ExploreValueCommand() |
| ExploreTypeCommand() |