]> git.ipfire.org Git - thirdparty/make.git/commitdiff
* make-gdb.py: Add pretty-printers and clean up.
authorPaul Smith <psmith@gnu.org>
Sun, 25 Jul 2021 21:19:09 +0000 (17:19 -0400)
committerPaul Smith <psmith@gnu.org>
Mon, 6 Sep 2021 00:05:02 +0000 (20:05 -0400)
Create a pretty-printer for next-pointer lists.
Create a pretty-printer for stringlists.
Change showargv from a function to a command (easier to use).

Clean up some Python PEP violations.  Add support for testing null
pointers and caching gdb.Types.

make-gdb.py

index d5a528549dce1c52e97d495d46c56d1dc8eb1d47..e06d2729e33f2ed6e290e9139e92ed7d043e74b6 100644 (file)
@@ -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 = '['
+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 argv[i] != 0:
-            if i > 0:
-                str += ', '
-            str += argv[i].string()
+        while not isNullptr(val):
+            gdb.write("%s : %s\n" % (val, val.dereference()))
+            gdb.flush()
             i += 1
-        str += ']'
-        return str
+            val = val['next']
+        gdb.write("%s contains %d elements\n" % (args[0], i))
+        gdb.flush()
 
-ShowArgv()
+
+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)