diff --git a/make-gdb.py b/make-gdb.py index d5a52854..e06d2729 100644 --- a/make-gdb.py +++ b/make-gdb.py @@ -1,11 +1,15 @@ -import re +"""GDB pretty-printer macros for GNU make.""" + +import gdb # pylint: disable=import-error +import gdb.printing # pylint: disable=import-error -import gdb #pylint: disable=import-error -import gdb.printing #pylint: disable=import-error # Memoize types we commonly use _TYPES = {} + + def getType(tname): + """Given a type name return a GDB type.""" global _TYPES if tname not in _TYPES: tn = tname.rstrip('*') @@ -18,48 +22,129 @@ def getType(tname): _TYPES[tn] = _TYPES[t].pointer() return _TYPES[tname] -class ShowArgv(gdb.Function): - """Return the pretty-print of a null-terminated array of strings + +def isNullptr(val): + """Return True if the value is a null pointer.""" + return int(val.cast(getType('unsigned long long'))) == 0 + + +class ShowArgv(gdb.Command): + """Print a null-terminated array of strings. Argument: A char** where the last one is NULL (e.g., argv) """ def __init__(self): - gdb.Function.__init__(self, "showargv") + """Create the showargv function.""" + gdb.Command.__init__(self, "showargv", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + """Show the argv.""" + args = gdb.string_to_argv(arg) + if len(args) != 1: + raise gdb.GdbError(self._usage) + + val = gdb.parse_and_eval(args[0]) + if val is None: + raise gdb.GdbError('%s is not a valid expression' % (args[0])) + + strs = [] + while not isNullptr(val.dereference()): + strs.append('"'+val.dereference().string()+'"') + val += 1 + + gdb.write("[%d] = [%s]\n" % (len(strs), ', '.join(strs))) + gdb.flush() - def invoke(self, argv): - str = '[' - i = 0 - while argv[i] != 0: - if i > 0: - str += ', ' - str += argv[i].string() - i += 1 - str += ']' - return str ShowArgv() +class ShowNextList(gdb.Command): + """Print a structure that has a "next" pointer. + + Argument: + A pointer to a struct which contains a "next" member. + """ + + _usage = 'usage: showlist ' + + def __init__(self): + """Create a "showlist" function.""" + gdb.Command.__init__(self, "showlist", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + """Show the elements in the provided list.""" + args = gdb.string_to_argv(arg) + if len(args) != 1: + raise gdb.GdbError(self._usage) + + val = gdb.parse_and_eval(args[0]) + if val is None: + raise gdb.GdbError('%s is not a valid expression' % (args[0])) + i = 0 + while not isNullptr(val): + gdb.write("%s : %s\n" % (val, val.dereference())) + gdb.flush() + i += 1 + val = val['next'] + gdb.write("%s contains %d elements\n" % (args[0], i)) + gdb.flush() + + +ShowNextList() + + class FileLocation(object): - """Print a file location""" + """Print a file location.""" def __init__(self, val): + """Create a FileLocation object.""" self.val = val def to_string(self): - if long(self.val['filenm']): + """Convert a FileLocation to a string.""" + if int(self.val['filenm']): return "%s:%d" % (str(self.val['filenm']), self.val['lineno']) - return '' + return 'NILF' + + +class StringListPrinter(object): + """Print a stringlist.""" + + def __init__(self, val): + """Create a StringListPrinter object.""" + self.val = val + + def to_string(self): + """Convert a HashTable into a string.""" + return "size=%d, capacity=%d" % (self.val['idx'], self.val['max']) + + def children(self): + """Yield each string in the list.""" + i = 0 + elts = self.val['list'] + while i < self.val['idx']: + nm = '[%d] ' % i + yield (nm, elts.dereference()) + i += 1 + elts += 1 + + def display_hint(self): + """Show the display hint for the pretty-printer.""" + return 'array' + class VariablePrinter(object): - """Print a struct variable""" + """Print a struct variable.""" def __init__(self, val): + """Create a VariablePrinter object.""" self.val = val def to_string(self): + """Convert a VariablePrinter object into a string.""" if self.val['append']: a = '+=' elif self.val['conditional']: @@ -80,48 +165,58 @@ class VariablePrinter(object): self.val['fileinfo'], ','.join(flags), self.val['name'].string(), a, self.val['value'].string()) + class HashTablePrinter(object): - """Manage a hash table.""" + """Pretty-print a hash table.""" DELITEM = None def __init__(self, val): + """Create a HashTablePrinter object.""" self.val = val def to_string(self): + """Convert a HashTable into a string.""" return "size=%d, capacity=%d, empty=%d, collisions=%d, rehashes=%d" % ( self.val['ht_size'], self.val['ht_capacity'], self.val['ht_empty_slots'], self.val['ht_collisions'], self.val['ht_rehashes']) def children(self): + """Yield each ID and value.""" for (i, v) in self.iterator(): nm = '[%d] ' % i yield (nm, i) yield (nm, v) def iterator(self): + """Provide an iterator for HashTable.""" if HashTablePrinter.DELITEM is None: HashTablePrinter.DELITEM = gdb.lookup_global_symbol('hash_deleted_item').value() lst = self.val['ht_vec'] - for i in xrange(0, self.val['ht_size']): + for i in range(0, self.val['ht_size']): v = lst[i] - if long(v) != 0 and v != HashTablePrinter.DELITEM: + if int(v) != 0 and v != HashTablePrinter.DELITEM: yield (i, v) def display_hint(self): + """Show the display hint for the pretty-printer.""" return 'map' + class VariableSetPrinter(object): - """Print a variable_set""" + """Print a variable_set.""" def __init__(self, val): + """Create a variable_set pretty-printer.""" self.tbl = HashTablePrinter(val['table']) def to_string(self): + """Convert a variable_set to string.""" return self.tbl.to_string() def children(self): + """Iterate through variables and values.""" for (i, v) in self.tbl.iterator(): ptr = v.cast(getType('struct variable*')) nm = '[%d] ' % (i) @@ -129,29 +224,35 @@ class VariableSetPrinter(object): yield (nm, str(ptr.dereference())) def display_hint(self): + """Show the display hint for the pretty-printer.""" return 'map' + class VariableSetListPrinter(object): - """Print a variable_set_list""" + """Print a variable_set_list.""" GLOBALSET = None def __init__(self, val): + """Create a variable_set_list pretty-printer.""" self.val = val def to_string(self): + """Convert a variable_set_list to string.""" return str(self.val.address) def children(self): + """Iterate through variables and values.""" if VariableSetListPrinter.GLOBALSET is None: block = gdb.lookup_global_symbol('init_hash_global_variable_set').symtab.static_block() - VariableSetListPrinter.GLOBALSET = gdb.lookup_symbol('global_variable_set', block)[0].value().address + VariableSetListPrinter.GLOBALSET = gdb.lookup_symbol( + 'global_variable_set', block)[0].value().address ptr = self.val.address i = 0 - while long(ptr) != 0: + while not isNullptr(ptr): nm = '[%d] ' % (i) yield (nm, ptr['set']) - if long(ptr['set']) == long(VariableSetListPrinter.GLOBALSET): + if int(ptr['set']) == int(VariableSetListPrinter.GLOBALSET): yield (nm, "global_variable_set") else: yield (nm, str(ptr['set'].dereference())) @@ -159,17 +260,22 @@ class VariableSetListPrinter(object): ptr = ptr['next'] def display_hint(self): + """Show the display hint for the pretty-printer.""" return 'map' + def build_pretty_printer(): + """Install all the pretty-printers.""" pp = gdb.printing.RegexpCollectionPrettyPrinter("gnumake") pp.add_printer('floc', r'^floc$', FileLocation) + pp.add_printer('stringlist', r'^stringlist$', StringListPrinter) pp.add_printer('variable', r'^variable$', VariablePrinter) pp.add_printer('hashtable', r'^hash_table$', HashTablePrinter) pp.add_printer('variableset', r'^variable_set$', VariableSetPrinter) pp.add_printer('variablesetlist', r'^variable_set_list$', VariableSetListPrinter) return pp + # Use replace=True so we can re-source this file gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer(), replace=True)