| """GDB pretty-printer macros for GNU Make.""" | |
| 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('*') | |
| if tn not in _TYPES: | |
| _TYPES[tn] = gdb.lookup_type(tn) | |
| while tn != tname: | |
| # Want a pointer type | |
| t = tn | |
| tn += '*' | |
| _TYPES[tn] = _TYPES[t].pointer() | |
| return _TYPES[tname] | |
| 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): | |
| """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() | |
| 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 <listptr>' | |
| 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.""" | |
| def __init__(self, val): | |
| """Create a FileLocation object.""" | |
| self.val = val | |
| def to_string(self): | |
| """Convert a FileLocation to a string.""" | |
| if int(self.val['filenm']): | |
| return "%s:%d" % (str(self.val['filenm']), self.val['lineno']) | |
| 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.""" | |
| 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']: | |
| a = '?=' | |
| else: | |
| a = '=' | |
| flags = [] | |
| s = str(self.val['flavor']) | |
| if s != 'f_bogus': | |
| flags.append(s) | |
| s = str(self.val['origin']) | |
| if s != 'o_default': | |
| flags.append(s) | |
| s = str(self.val['export']) | |
| if s != 'v_default': | |
| flags.append(s) | |
| return '%s[%s]: "%s" %s "%s"' % ( | |
| self.val['fileinfo'], ','.join(flags), | |
| self.val['name'].string(), a, self.val['value'].string()) | |
| class HashTablePrinter(object): | |
| """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 range(0, self.val['ht_size']): | |
| v = lst[i] | |
| 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.""" | |
| 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) | |
| yield (nm, ptr) | |
| 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.""" | |
| 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 | |
| ptr = self.val.address | |
| i = 0 | |
| while not isNullptr(ptr): | |
| nm = '[%d] ' % (i) | |
| yield (nm, ptr['set']) | |
| if int(ptr['set']) == int(VariableSetListPrinter.GLOBALSET): | |
| yield (nm, "global_variable_set") | |
| else: | |
| yield (nm, str(ptr['set'].dereference())) | |
| i += 1 | |
| 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) |