From 7b7154b3968810bf605e06c07c0fdbce098dc07e Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Date: Wed, 24 Oct 2012 15:00:07 +0200 Subject: [PATCH] blah --- index.html | 22 +- samples/explore.py | 694 +++++++++++++++++++++++++++++++++++++++++++++ samples/plist.gdb | 9 + samples/script.py | 43 +++ samples/test2.c | 2 + 5 files changed, 769 insertions(+), 1 deletion(-) create mode 100644 samples/explore.py create mode 100644 samples/plist.gdb create mode 100644 samples/script.py diff --git a/index.html b/index.html index 17b8b45..c7761ae 100644 --- a/index.html +++ b/index.html @@ -97,7 +97,7 @@ End with a line saying just "end".
-

Custom commands

+

Custom commands (1/2)

 (gdb) define plist
 Type commands for definition of "plist".
@@ -111,6 +111,26 @@ End with a line saying just "end".
 
+
+

Custom commands (2/2)

+

You can create hooks which run just before or after any gdb command:

+
+ +
+

Did you know...?

+

GDB has many other semi-unknown cool features (which I won't talk about today)

+ +
+ + + diff --git a/samples/explore.py b/samples/explore.py new file mode 100644 index 0000000..f62be7d --- /dev/null +++ b/samples/explore.py @@ -0,0 +1,694 @@ +# 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_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'.\n" % + (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 ("\n'%s' is a pointer to a value of type '%s'" % + (expr, str(value.type.target()))) + option = raw_input("Do you want to explore it as an array or a " + "single value pointer [a/s] : ") + if option == "s": + deref_value = None + try: + deref_value = value.dereference() + str(deref_value) + except gdb.MemoryError: + print ("'%s' a pointer pointing to an invalid memory " + "location." % expr) + if is_child: + Explorer.return_to_parent_value_prompt() + return False + Explorer.explore_expr("*%s" % Explorer.guard_expr(expr), + deref_value, is_child) + if option == "a": + while True: + index = 0 + try: + index = int(raw_input("Enter the index you want to " + "explore in '%s': " % expr)) + except ValueError: + break + element_expr = "%s[%d]" % (Explorer.guard_expr(expr), index) + element = gdb.parse_and_eval(element_expr) + try: + str(element) + except gdb.MemoryError: + print "Cannot read value at index %d." % index + continue + Explorer.explore_expr(element_expr, element, True) + if option != "s" and option != "a": + if is_child: + Explorer.retur_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 of a pointer type pointing 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 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 elements of type '%s'." % + (expr, str(target_type))) + index = 0 + try: + index = int(raw_input("Enter the index 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 type with elements of type '%s'." % + (name, str(target_type))) + + explore_type(target_type, "the array element of %s" % name, 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 explore_expr(expr, value, is_child): + """Function to explore structs/classes and union values. + See Explorer.explore_expr for more information. + """ + type_code = value.type.code + type_desc = "" + if type_code == gdb.TYPE_CODE_STRUCT: + type_desc = "struct/class" + else: + type_desc = "union" + print("The value of '%s' is a %s of type '%s' with the following " + "fields:\n" % (expr, type_desc, str(value.type))) + + fields = value.type.fields() + has_explorable_fields = False + choice_to_compound_field_map = { } + current_choice = 0 + print_list = [ ] + for field in fields: + field_full_name = Explorer.guard_expr(expr) + "." + field.name + 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" + + if is_child: + print ("\n%s is a %s of type '%s' " + "with the following fields:\n" % + (name, type_desc, str(datatype))) + else: + print ("\n'%s' is a %s with the following " + "fields:\n" % + (name, type_desc)) + + fields = datatype.fields() + has_explorable_fields = False + current_choice = 0 + choice_to_compound_field_map = { } + print_list = [ ] + for field in fields: + rhs = ("" % + (current_choice, str(field.type))) + print_list.append((field.name, rhs)) + choice_to_compound_field_map[str(current_choice)] = ( + field.name, field.type) + 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: + Explorer.explore_type( + ("field '%s' of %s" % + (choice_to_compound_field_map[choice][0], name)), + choice_to_compound_field_map[choice][1], + True) + else: + Explorer.explore_type( + ("field '%s' of '%s'" % + (choice_to_compound_field_map[choice][0], name)), + choice_to_compound_field_map[choice][1], + True) + return True + else: + if is_child: + Explorer.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 ("\nThe 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("\nThe type of %s is a typedef of type '%s'." % + (name, str(actual_type))) + else: + print("\nThe 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' command requires an argument." + % name) + return False + else: + return True + + @staticmethod + def get_type_from_str(type_str): + """A utility function to deduce the gdb.Type value from a string + representing the type. + + Arguments: + type_str: The type string from which the gdb.Type value should be + deduced. + + Returns: + The deduced gdb.Type value if possible, None otherwise. + """ + try: + # Assume the current language to be C/C++ and make a try. + return gdb.parse_and_eval("(%s *)0" % type_str).type.target() + except RuntimeError: + # If assumption of current language to be C/C++ was wrong, then + # lookup the type using the API. + try: + return gdb.lookup_type(type_str) + except RuntimeError: + return None + + @staticmethod + def get_value_from_str(value_str): + """A utility function to deduce the gdb.Value value from a string + representing the value. + + Arguments: + value_str: The value string from which the gdb.Value value should + be deduced. + + Returns: + The deduced gdb.Value value if possible, None otherwise. + """ + try: + return gdb.parse_and_eval(value_str) + except RuntimeError: + return None + + +class ExploreCommand(gdb.Command): + """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. Entering + an invalid input will also result in similar behaviour. + """ + + 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. Entering + an invalid input will also result in similar behaviour. + """ + + 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. Entering + an invalid input will also result in similar behaviour. + """ + + 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/samples/plist.gdb b/samples/plist.gdb new file mode 100644 index 0000000..446c3a4 --- /dev/null +++ b/samples/plist.gdb @@ -0,0 +1,9 @@ +define plist + set $ptr = $arg0 + while $ptr != 0 + print *$ptr + set $ptr = $ptr->next + end +end + + diff --git a/samples/script.py b/samples/script.py new file mode 100644 index 0000000..d862955 --- /dev/null +++ b/samples/script.py @@ -0,0 +1,43 @@ +import gdb +from PyQt4 import QtGui + +def print_node(node, recursion_level): + if len(node.type.fields()) == 0: + print "node = %s", str(node) + result = str(node) + else: + result = {} + for f in node.type.fields(): + child = node[f.name] + if child.type.code == gdb.TYPE_CODE_PTR and recursion_level > 0: + if (child == 0): + result_child = "NULL" + else: + result_child = print_node(child.dereference(), recursion_level - 1) + else: + result_child = str(child) + result[f.name] = result_child + return result + +class ViewerCommand(gdb.Command): + """View data structures""" + def __init__(self): + super(ViewerCommand, self).__init__("view", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL) + + + def invoke(self, arg, from_tty): + args = gdb.string_to_argv(arg) + value = gdb.parse_and_eval(args[0]) + if len(args) > 1: + recursion_level = int(args[1]) + else: + recursion_level = 3 + print print_node(value.dereference(), recursion_level) + + + + + +ViewerCommand() + + diff --git a/samples/test2.c b/samples/test2.c index 06aff4c..351fbaf 100644 --- a/samples/test2.c +++ b/samples/test2.c @@ -3,12 +3,14 @@ typedef struct snode { int value; + float blah; struct snode *next; } node; void add(node **head, int x) { node *n = malloc(sizeof(node)); n->value = x; + n->blah = 1234.0f; n->next = *head; *head = n; } -- 2.34.1