https://gcc.gnu.org/g:de124ffe1439d63e4531f8aaf3641bb8f4260c33

commit r16-5284-gde124ffe1439d63e4531f8aaf3641bb8f4260c33
Author: Tom Tromey <[email protected]>
Date:   Mon Nov 3 14:02:22 2025 -0700

    Add 'num_children' method to relevant pretty-printers
    
    A user pointed out that, in DAP mode, gdb would hang while trying to
    display a certain vector.  See
    
        https://sourceware.org/bugzilla/show_bug.cgi?id=33594
    
    This is caused by a combination of things: the vector is
    uninitialized, DAP requires a count of the number of children of a
    variable, and libstdc++ printers don't implement the 'num_children'
    method, so gdb tries to count children by iterating.
    
    In this case, the vector has a nonsensical size:
    
        (gdb) p myVector
        $1 = std::vector of length -34979931, capacity -33992726
    
    This patch adds a 'num_children' method to a subset of the
    pretty-printers, in particular ones where I thought the length might
    be arbitrarily large and susceptible to being garbage when the object
    isn't initialized.
    
    I've also specifically added a check to the vector printer for the
    case where the length is negative.
    
    These container printers could be further improved by adding the
    'child' method, allowing random access to child objects.  However I
    haven't done that here.
    
    libstdc++-v3/ChangeLog
    
            * python/libstdcxx/v6/printers.py (StdVectorPrinter._bounds):
            New method.
            (StdVectorPrinter.to_string): Use it.
            (StdVectorPrinter.num_children): New method.
            (StdStackOrQueuePrinter.num_children): New method.
            (StdMapPrinter.num_children): New method.
            (StdSetPrinter.num_children): New method.
            (StdDequePrinter._size): New method.
            (StdDequePrinter.to_string): Use it.
            (StdDequePrinter.num_children): New method.
            (Tr1UnorderedSetPrinter.num_children): New method.
            (Tr1UnorderedMapPrinter.num_children): New method.
            (StdSpanPrinter.num_children): New method.

Diff:
---
 libstdc++-v3/python/libstdcxx/v6/printers.py | 62 +++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 10 deletions(-)

diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py 
b/libstdc++-v3/python/libstdcxx/v6/printers.py
index 7a5a627479fd..f7f0489abf9a 100644
--- a/libstdc++-v3/python/libstdcxx/v6/printers.py
+++ b/libstdc++-v3/python/libstdcxx/v6/printers.py
@@ -572,7 +572,10 @@ class StdVectorPrinter(printer_base):
                               self._val['_M_impl']['_M_finish'],
                               self._is_bool)
 
-    def to_string(self):
+    # Helper to compute the bounds of the vector.
+    # Returns a tuple: (length, capacity, suffix)
+    # SUFFIX is a type-name suffix to print.
+    def _bounds(self):
         start = self._val['_M_impl']['_M_start']
         finish = self._val['_M_impl']['_M_finish']
         end = self._val['_M_impl']['_M_end_of_storage']
@@ -582,13 +585,27 @@ class StdVectorPrinter(printer_base):
             fo = self._val['_M_impl']['_M_finish']['_M_offset']
             itype = start.dereference().type
             bl = 8 * itype.sizeof
-            length = bl * (finish - start) + fo
-            capacity = bl * (end - start)
-            return ('%s<bool> of length %d, capacity %d'
-                    % (self._typename, int(length), int(capacity)))
+            length = int(bl * (finish - start) + fo)
+            capacity = int(bl * (end - start))
+            suffix = '<bool>'
         else:
-            return ('%s of length %d, capacity %d'
-                    % (self._typename, int(finish - start), int(end - start)))
+            length = int(finish - start)
+            capacity = int(end - start)
+            suffix = ''
+        if length < 0:
+            # Probably uninitialized.
+            length = 0
+            capacity = 0
+        return (length, capacity, suffix)
+
+    def to_string(self):
+        (length, capacity, suffix) = self._bounds()
+        return ('%s%s of length %d, capacity %d'
+                % (self._typename, suffix, length, capacity))
+
+    def num_children(self):
+        (length, capacity, suffix) = self._bounds()
+        return length
 
     def display_hint(self):
         return 'array'
@@ -733,6 +750,11 @@ class StdStackOrQueuePrinter(printer_base):
         return '%s wrapping: %s' % (self._typename,
                                     self._visualizer.to_string())
 
+    def num_children(self):
+        if hasattr(self._visualizer, 'num_children'):
+            return self._visualizer.num_children()
+        return None
+
     def display_hint(self):
         if hasattr(self._visualizer, 'display_hint'):
             return self._visualizer.display_hint()
@@ -876,6 +898,9 @@ class StdMapPrinter(printer_base):
         node = lookup_node_type('_Rb_tree_node', self._val.type).pointer()
         return self._iter(RbtreeIterator(self._val), node)
 
+    def num_children(slf):
+        return len(RbtreeIterator(self._val))
+
     def display_hint(self):
         return 'map'
 
@@ -915,6 +940,8 @@ class StdSetPrinter(printer_base):
         node = lookup_node_type('_Rb_tree_node', self._val.type).pointer()
         return self._iter(RbtreeIterator(self._val), node)
 
+    def num_children(slf):
+        return len(RbtreeIterator(self._val))
 
 class StdBitsetPrinter(printer_base):
     """Print a std::bitset."""
@@ -1006,7 +1033,8 @@ class StdDequePrinter(printer_base):
         else:
             self._buffer_size = 1
 
-    def to_string(self):
+    # Helper to compute the size.
+    def _size(self):
         start = self._val['_M_impl']['_M_start']
         end = self._val['_M_impl']['_M_finish']
 
@@ -1014,9 +1042,11 @@ class StdDequePrinter(printer_base):
         delta_s = start['_M_last'] - start['_M_cur']
         delta_e = end['_M_cur'] - end['_M_first']
 
-        size = self._buffer_size * delta_n + delta_s + delta_e
+        return long(self._buffer_size * delta_n + delta_s + delta_e)
 
-        return '%s with %s' % (self._typename, num_elements(long(size)))
+    def to_string(self):
+        size = self._size()
+        return '%s with %s' % (self._typename, num_elements(size))
 
     def children(self):
         start = self._val['_M_impl']['_M_start']
@@ -1024,6 +1054,9 @@ class StdDequePrinter(printer_base):
         return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'],
                           end['_M_cur'], self._buffer_size)
 
+    def num_children(self):
+        return self._size()
+
     def display_hint(self):
         return 'array'
 
@@ -1210,6 +1243,9 @@ class Tr1UnorderedSetPrinter(printer_base):
             return izip(counter, Tr1HashtableIterator(self._hashtable()))
         return izip(counter, StdHashtableIterator(self._hashtable()))
 
+    def num_children(self):
+        return int(self._hashtable()['_M_element_count'])
+
 
 class Tr1UnorderedMapPrinter(printer_base):
     """Print a std::unordered_map or tr1::unordered_map."""
@@ -1254,6 +1290,9 @@ class Tr1UnorderedMapPrinter(printer_base):
         # Zip the two iterators together.
         return izip(counter, data)
 
+    def num_children(self):
+        return int(self._hashtable()['_M_element_count'])
+
     def display_hint(self):
         return 'map'
 
@@ -1949,6 +1988,9 @@ class StdSpanPrinter(printer_base):
     def children(self):
         return self._iterator(self._val['_M_ptr'], self._size)
 
+    def num_children(self):
+        return int(self._size)
+
     def display_hint(self):
         return 'array'

Reply via email to