public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter
       [not found] <20240619170754.1651-1-ssbssa.ref@yahoo.de>
@ 2024-06-19 17:07 ` Hannes Domani
  2024-06-19 17:07   ` [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer Hannes Domani
                     ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Hannes Domani @ 2024-06-19 17:07 UTC (permalink / raw)
  To: gdb-patches

Currently anonymous fields are not shown at all, this shows
them with the name [anonymous].

And it also marks base classes with <>, e.g. <BaseClass>, so
they can be distinguished from struct fields.
---
 gdb/python/lib/gdb/printing.py | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 55ba43585ec..542909bf631 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -351,8 +351,13 @@ class NoOpStructPrinter(gdb.ValuePrinter):
 
     def children(self):
         for field in self.__ty.fields():
-            if hasattr(field, "bitpos") and field.name is not None:
-                yield (field.name, self.__value[field])
+            if hasattr(field, "bitpos"):
+                name = field.name
+                if name is None:
+                    name = "[anonymous]"
+                elif field.is_base_class:
+                    name = "<" + name + ">"
+                yield (name, self.__value[field])
 
 
 def make_visualizer(value):
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer
  2024-06-19 17:07 ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Hannes Domani
@ 2024-06-19 17:07   ` Hannes Domani
  2024-06-24 15:48     ` Tom Tromey
  2024-06-19 17:07   ` [PATCH 3/4] Use custom format argument in NoOpScalarPrinter Hannes Domani
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Hannes Domani @ 2024-06-19 17:07 UTC (permalink / raw)
  To: gdb-patches

In case someone just wants the no-op visualizer.
---
 gdb/python/lib/gdb/printing.py | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 542909bf631..917059a6eb6 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -360,13 +360,17 @@ class NoOpStructPrinter(gdb.ValuePrinter):
                 yield (name, self.__value[field])
 
 
-def make_visualizer(value):
+def make_visualizer(value, raw=False):
     """Given a gdb.Value, wrap it in a pretty-printer.
 
     If a pretty-printer is found by the usual means, it is returned.
-    Otherwise, VALUE will be wrapped in a no-op visualizer."""
+    Otherwise, VALUE will be wrapped in a no-op visualizer.
 
-    result = gdb.default_visualizer(value)
+    Arguments:
+        raw: Disables normal pretty-printers.
+    """
+
+    result = None if raw else gdb.default_visualizer(value)
     if result is not None:
         # Found a pretty-printer.
         pass
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 3/4] Use custom format argument in NoOpScalarPrinter
  2024-06-19 17:07 ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Hannes Domani
  2024-06-19 17:07   ` [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer Hannes Domani
@ 2024-06-19 17:07   ` Hannes Domani
  2024-06-24 15:49     ` Tom Tromey
  2024-06-19 17:07   ` [PATCH 4/4] Implement locals TUI window Hannes Domani
  2024-06-24 15:45   ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Tom Tromey
  3 siblings, 1 reply; 7+ messages in thread
From: Hannes Domani @ 2024-06-19 17:07 UTC (permalink / raw)
  To: gdb-patches

So values can be shown e.g. in hexadecimal notation.
---
 gdb/python/lib/gdb/printing.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 917059a6eb6..1e9b59bb165 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -274,10 +274,13 @@ class FlagEnumerationPrinter(PrettyPrinter):
 class NoOpScalarPrinter(gdb.ValuePrinter):
     """A no-op pretty printer that wraps a scalar value."""
 
-    def __init__(self, value):
+    def __init__(self, value, format=None):
         self.__value = value
+        self.__format = format
 
     def to_string(self):
+        if self.__format:
+            return self.__value.format_string(raw=True, format=self.__format)
         return self.__value.format_string(raw=True)
 
 
@@ -360,7 +363,7 @@ class NoOpStructPrinter(gdb.ValuePrinter):
                 yield (name, self.__value[field])
 
 
-def make_visualizer(value, raw=False):
+def make_visualizer(value, raw=False, format=None):
     """Given a gdb.Value, wrap it in a pretty-printer.
 
     If a pretty-printer is found by the usual means, it is returned.
@@ -377,7 +380,7 @@ def make_visualizer(value, raw=False):
     else:
         ty = value.type.strip_typedefs()
         if ty.is_string_like:
-            result = NoOpScalarPrinter(value)
+            result = NoOpScalarPrinter(value, format)
         elif ty.code == gdb.TYPE_CODE_ARRAY:
             result = NoOpArrayPrinter(ty, value)
         elif ty.is_array_like:
@@ -393,7 +396,7 @@ def make_visualizer(value, raw=False):
         ):
             result = NoOpPointerReferencePrinter(value)
         else:
-            result = NoOpScalarPrinter(value)
+            result = NoOpScalarPrinter(value, format)
     return result
 
 
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 4/4] Implement locals TUI window
  2024-06-19 17:07 ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Hannes Domani
  2024-06-19 17:07   ` [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer Hannes Domani
  2024-06-19 17:07   ` [PATCH 3/4] Use custom format argument in NoOpScalarPrinter Hannes Domani
@ 2024-06-19 17:07   ` Hannes Domani
  2024-06-24 15:45   ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Tom Tromey
  3 siblings, 0 replies; 7+ messages in thread
From: Hannes Domani @ 2024-06-19 17:07 UTC (permalink / raw)
  To: gdb-patches

This creates a new TUI window called "locals", which shows all local
arguments and variables.

With the mouse it's possible to interactively expand and collapse
struct fields, arrays, pointers, and pretty-printer children.
This expansion is displayed in a tree-like manner, and works also for
multiple levels.

Different colors are used to distuingish arguments,
not-yet-declared variables, changed variable values, added variables,
and errors.
---
 gdb/data-directory/Makefile.in            |   1 +
 gdb/python/lib/gdb/command/tui_windows.py | 794 ++++++++++++++++++++++
 2 files changed, 795 insertions(+)
 create mode 100644 gdb/python/lib/gdb/command/tui_windows.py

diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 98a43529b07..cd28a6402e3 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -89,6 +89,7 @@ PYTHON_FILE_LIST = \
 	gdb/command/missing_debug.py \
 	gdb/command/pretty_printers.py \
 	gdb/command/prompt.py \
+	gdb/command/tui_windows.py \
 	gdb/command/type_printers.py \
 	gdb/command/unwinders.py \
 	gdb/command/xmethods.py \
diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
new file mode 100644
index 00000000000..9b21e61086f
--- /dev/null
+++ b/gdb/python/lib/gdb/command/tui_windows.py
@@ -0,0 +1,794 @@
+# Additional TUI windows.
+# Copyright 2024 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 <http://www.gnu.org/licenses/>.
+
+import gdb
+import sys
+import re
+import locale
+
+custom_windows = {}
+
+encoding = locale.getdefaultlocale()[1]
+
+
+col_esc_seq_re = re.compile("(\033\\[[0-9;]*m)")
+
+
+def colored_substr(text, offset, count):
+    """Returns a substring ignoring the color escape sequences in offset and
+    count, but keeping them in the returned string.
+
+    The returned string contains exactly count printable characters, filling
+    it with spaces if necessary."""
+
+    col_esc_seq = False
+    init_col_esc_seq = None
+    last_col_esc_seq = None
+    sub = ""
+    for p in col_esc_seq_re.split(text):
+        t = ""
+        if offset > 0:
+            if col_esc_seq:
+                init_col_esc_seq = p
+            else:
+                l = len(p)
+                if l <= offset:
+                    offset -= l
+                else:
+                    if init_col_esc_seq:
+                        sub += init_col_esc_seq
+                    t = p[offset:]
+                    offset = 0
+        else:
+            t = p
+        if count > 0:
+            if col_esc_seq:
+                sub += t
+            else:
+                l = len(t)
+                if l <= count:
+                    sub += t
+                    count -= l
+                else:
+                    sub += t[:count]
+                    count = 0
+        elif col_esc_seq:
+            last_col_esc_seq = p
+        col_esc_seq = not col_esc_seq
+    if last_col_esc_seq:
+        sub += last_col_esc_seq
+    if count > 0:
+        sub += " " * count
+    return sub
+
+
+class TextWindow(object):
+    """Base text window class which takes care of drawing and scrolling.
+
+    An inheriting class needs to implement the refill method which fills
+    the list self.lines with the lines that should be displayed.
+    This method will be called each time a prompt is presented to the user.
+    """
+
+    def __init__(self, win):
+        self.win = win
+        self.line_ofs = 0
+        self.col_ofs = 0
+        self.lines = []
+        win.title = self._window_name
+        global custom_windows
+        custom_windows[self._window_name] = self
+
+    def close(self):
+        global custom_windows
+        del custom_windows[self._window_name]
+
+    def render(self):
+        self.col_ofs = 0
+        self.redraw()
+
+    def hscroll(self, num):
+        prev_col_ofs = self.col_ofs
+        self.col_ofs = self.col_ofs + num
+        if self.col_ofs < 0:
+            self.col_ofs = 0
+        if self.col_ofs != prev_col_ofs:
+            self.redraw()
+
+    def vscroll(self, num):
+        prev_line_ofs = self.line_ofs
+        self.line_ofs = self.line_ofs + num
+        l = len(self.lines)
+        if self.line_ofs >= l:
+            self.line_ofs = l - 1
+        if self.line_ofs < 0:
+            self.line_ofs = 0
+        if self.line_ofs != prev_line_ofs:
+            self.redraw()
+
+    def redraw(self):
+        l = len(self.lines)
+        if self.line_ofs > 0 and self.line_ofs + self.win.height > l:
+            self.line_ofs = l - self.win.height
+            if self.line_ofs < 0:
+                self.line_ofs = 0
+        start = self.line_ofs
+        stop = self.line_ofs + self.win.height
+        if stop > l:
+            stop = l
+        if stop > start:
+            self.win.write(
+                "".join(
+                    [
+                        colored_substr(l, self.col_ofs, self.win.width)
+                        for l in self.lines[start:stop]
+                    ]
+                ),
+                True,
+            )
+        else:
+            self.win.erase()
+
+    @classmethod
+    def register_window_type(cls, window_name):
+        cls._window_name = window_name
+        gdb.register_window_type(window_name, cls)
+
+
+def octal_escape(s):
+    """Escape non-printable characters as octal string."""
+    return "".join(chr(b) if b < 128 else "\\%03o" % b for b in s.encode(encoding))
+
+
+def output_unchanged(s):
+    return s
+
+
+output_convert = output_unchanged
+
+if sys.platform == "win32":
+    output_convert = octal_escape
+
+
+def value_string(valstr, hint):
+    """Convert value to string representation."""
+    if hint == "string":
+        is_lazy = type(valstr).__name__ == "LazyString"
+        if is_lazy:
+            l = valstr.length
+        elif not isinstance(valstr, str):
+            l = 1
+        else:
+            l = len(valstr)
+        if l == 0:
+            if is_lazy and str(valstr.type) == "wchar_t *":
+                return 'L""'
+            else:
+                return '""'
+        else:
+            return gdb.Value(valstr).format_string(symbols=False, address=False)
+    if isinstance(valstr, gdb.Value):
+        return valstr.format_string(symbols=False, address=False)
+    elif not isinstance(valstr, str):
+        return str(valstr)
+    return valstr
+
+
+class VariableWindow(TextWindow):
+    """Base variable window class for interactive expansion of child values.
+
+    An inheriting class needs to implement the variables method which returns
+    an iterable of objects (based on what FrameDecorator.frame_args returns).
+    These objects must implement a symbol method, returning either a Python
+    string of a gdb.Symbol.
+    And optionally they can implement these methods:
+    - value: Returns either a Python value, a gdb.Value, or a pretty-printer
+        (if not implemented, or returns None, and the symbol method returns a
+        gdb.Symbol, the value of the gdb.Symbol is used).
+    - undeclared: Returns a Boolean indicating if the declaration of this
+        variable was not yet reached (default=False).
+    - argument: Returns a Boolean indicating this variable is an argument
+        (default=False).
+    - number: If this returns a positive integer, it is shown before
+        symbol name (default=0).
+    - raw: Returns a Boolean indicating that no pretty-printers should be
+        used to format the value (default=False).
+    - format: Same as the format argument of Value.format_string.
+    - error: Python string indicating an error message that is shown if
+        method value returns None.
+    - expand: Returns a Boolean indicating the children of this value should
+        be immediately expanded.
+    """
+
+    class LineInfo(object):
+        def __init__(self):
+            self.expand = None
+            self.cur_val = False
+            self.prev_val = None
+
+    def __init__(self, win):
+        super(VariableWindow, self).__init__(win)
+        self.prev_vals = {}
+        self.line_names = []
+
+    def click(self, x, y, button):
+        line = y + self.line_ofs
+        if line < len(self.line_names):
+            name = self.line_names[line]
+            if name:
+                prev = self.prev_vals[name]
+                if button == 1:
+                    # On left-click, invert expansion of children.
+                    if prev is not None and prev.expand is not None:
+                        prev.expand = not prev.expand
+                        self.refill(True)
+                        if prev.expand:
+                            # Scroll down to show expanded contents.
+                            c = 0
+                            l = len(self.line_names)
+                            while line + c < l and c < self.win.height:
+                                n = self.line_names[line + c]
+                                if n is not None and not n.startswith(name):
+                                    break
+                                c += 1
+                            if line + c > self.line_ofs + self.win.height:
+                                self.line_ofs = line + c - self.win.height
+                        self.redraw()
+
+    def refill(self, keep_prev=False):
+        self.lines = []
+        self.line_names = []
+        self.cur_vals = {}
+        self.raw = False
+        self.format = None
+        self.keep_prev = keep_prev
+        frame = None
+        for v in self.variables():
+            symbol = v.symbol()
+            if isinstance(symbol, gdb.Symbol):
+                name = symbol.name
+            else:
+                name = symbol
+
+            value = None
+            error = None
+            if hasattr(v, "value"):
+                value = v.value()
+            if value is None and isinstance(symbol, gdb.Symbol):
+                if frame is None:
+                    frame = gdb.selected_frame()
+                try:
+                    value = symbol.value(frame)
+                except:
+                    error = str(sys.exc_info()[1])
+
+            undeclared = False
+            if hasattr(v, "undeclared"):
+                undeclared = v.undeclared()
+
+            argument = False
+            if hasattr(v, "argument"):
+                argument = v.argument()
+
+            number = 0
+            if hasattr(v, "number"):
+                number = v.number()
+
+            self.raw = False
+            if hasattr(v, "raw"):
+                self.raw = v.raw()
+
+            self.format = None
+            if hasattr(v, "format"):
+                self.format = v.format()
+
+            if error is None and hasattr(v, "error"):
+                error = v.error()
+
+            expand = False
+            if hasattr(v, "expand"):
+                expand = v.expand()
+
+            self.add_val(
+                name,
+                name,
+                value,
+                0,
+                number=number,
+                undeclared=undeclared,
+                argument=argument,
+                error=error,
+                def_expand=expand,
+            )
+        self.prev_vals = self.cur_vals
+        del self.cur_vals
+        del self.raw
+        del self.format
+        del self.keep_prev
+
+    def add_val(
+        self,
+        name,
+        full_name,
+        val,
+        inset,
+        number=0,
+        undeclared=False,
+        argument=False,
+        error=None,
+        def_expand=False,
+    ):
+        # line_name needs to be a unique name in the cur_vals dict.
+        if number == 0:
+            line_name = full_name
+            count = 1
+            while line_name in self.cur_vals:
+                count += 1
+                line_name = "%s:%d" % (name, count)
+        else:
+            line_name = "%d:%s" % (number, name)
+
+        # cur_vals stores info for each line
+        cur_entry = VariableWindow.LineInfo()
+        self.cur_vals[line_name] = cur_entry
+        expand = None
+        prev_val = None
+        if line_name in self.prev_vals:
+            pv = self.prev_vals[line_name]
+            expand = pv.expand
+            if self.keep_prev:
+                prev_val = pv.prev_val
+            else:
+                prev_val = pv.cur_val
+            cur_entry.expand = expand
+            cur_entry.prev_val = prev_val
+
+        if isinstance(val, Exception):
+            error = str(val)
+            val = None
+
+        if val is None:
+            self.add_line(
+                inset, number, name, line_name, undeclared, argument, error=error
+            )
+            return
+
+        if isinstance(val, str):
+            val = output_convert(val)
+            self.add_line(
+                inset, number, name, line_name, undeclared, argument, val, prev_val
+            )
+            cur_entry.cur_val = val
+            return
+
+        is_pp = (
+            hasattr(val, "to_string")
+            or hasattr(val, "children")
+            or isinstance(val, gdb.ValuePrinter)
+        )
+
+        if is_pp:
+            pass
+        elif isinstance(val, gdb.Value):
+            is_optimized_out = False
+            try:
+                if val.type.code in [gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF]:
+                    val = val.referenced_value()
+
+                is_optimized_out = val.is_optimized_out
+            except:
+                self.add_line(
+                    inset,
+                    number,
+                    name,
+                    line_name,
+                    undeclared,
+                    argument,
+                    error=str(sys.exc_info()[1]),
+                )
+                return
+
+            if is_optimized_out and val.type.strip_typedefs().code not in [
+                gdb.TYPE_CODE_STRUCT,
+                gdb.TYPE_CODE_UNION,
+            ]:
+                # Not done for structs/unions, since there could be some
+                # members that are not optimized out.
+                self.add_line(
+                    inset,
+                    number,
+                    name,
+                    line_name,
+                    undeclared,
+                    argument,
+                    error="optimized out",
+                )
+                return
+        else:
+            val = gdb.Value(val)
+
+        pp = None
+        if is_pp:
+            pp = val
+        elif not self.raw:
+            try:
+                pp = gdb.default_visualizer(val)
+            except:
+                pp = None
+
+        def_expand_child = False
+        if pp is None:
+            t = val.type.strip_typedefs()
+            if t.code == gdb.TYPE_CODE_PTR:
+                ptr_target_type = t.target().strip_typedefs().code
+                def_expand_child = ptr_target_type != gdb.TYPE_CODE_PTR
+
+            pp = gdb.printing.make_visualizer(val, raw=True, format=self.format)
+
+        try:
+            if expand is None and hasattr(pp, "children"):
+                expand = def_expand
+                cur_entry.expand = expand
+
+            valstr = None
+            if hasattr(pp, "to_string"):
+                valstr = pp.to_string()
+            hint = None
+            if hasattr(pp, "display_hint"):
+                hint = pp.display_hint()
+
+            if valstr is not None:
+                valstr = value_string(valstr, hint)
+                valstr = output_convert(valstr)
+                self.add_line(
+                    inset,
+                    number,
+                    name,
+                    line_name,
+                    undeclared,
+                    argument,
+                    valstr,
+                    prev_val,
+                    expand=expand,
+                )
+                cur_entry.cur_val = valstr
+            else:
+                self.add_line(
+                    inset,
+                    number,
+                    name,
+                    line_name,
+                    undeclared,
+                    argument,
+                    False,
+                    prev_val,
+                    expand=expand,
+                )
+        except:
+            self.add_line(
+                inset,
+                number,
+                name,
+                line_name,
+                undeclared,
+                argument,
+                error=str(sys.exc_info()[1]),
+            )
+            return
+
+        try:
+            if expand:
+                childs = None
+                if hasattr(pp, "children"):
+                    childs = pp.children()
+                if childs:
+                    max_elements = gdb.parameter("print elements")
+                    if hint == "map":
+                        # If key can be displayed in 1 line, show it as:
+                        #   [$KEY_STRING]
+                        #     $VALUE_CHILDREN
+                        # Otherwise, use key/value pairs:
+                        #   key
+                        #     $KEY_CHILDREN
+                        #   value
+                        #     $VALUE_CHILDREN
+                        key = None
+                        for count, c in enumerate(childs, 1):
+                            if max_elements and count > max_elements:
+                                break
+                            (name_child, val_child) = c
+                            if not isinstance(name_child, str):
+                                name_child = "[%s]" % name_child
+                            full_name_child = ":".join([line_name, name_child])
+                            if (count % 2) == 1:
+                                key = None
+                                if isinstance(val_child, str):
+                                    key = val_child
+                                else:
+                                    val_child_check = val_child
+                                    if val_child_check.type.code in [
+                                        gdb.TYPE_CODE_REF,
+                                        gdb.TYPE_CODE_RVALUE_REF,
+                                    ]:
+                                        val_child_check = (
+                                            val_child_check.referenced_value()
+                                        )
+                                    key_pp = gdb.default_visualizer(val_child_check)
+                                    if key_pp:
+                                        if hasattr(key_pp, "to_string") and not hasattr(
+                                            key_pp, "children"
+                                        ):
+                                            val_child_check = key_pp.to_string()
+                                            if val_child_check is not None:
+                                                hint = None
+                                                if hasattr(key_pp, "display_hint"):
+                                                    hint = key_pp.display_hint()
+                                                key = value_string(
+                                                    val_child_check, hint
+                                                )
+                                    else:
+                                        t_check = val_child_check.type.strip_typedefs()
+                                        if t_check.code in [
+                                            gdb.TYPE_CODE_ENUM,
+                                            gdb.TYPE_CODE_INT,
+                                            gdb.TYPE_CODE_FLT,
+                                            gdb.TYPE_CODE_BOOL,
+                                            gdb.TYPE_CODE_COMPLEX,
+                                            gdb.TYPE_CODE_DECFLOAT,
+                                        ]:
+                                            if self.format:
+                                                key = val_child_check.format_string(
+                                                    symbols=False,
+                                                    raw=True,
+                                                    format=self.format,
+                                                )
+                                            else:
+                                                key = val_child_check.format_string(
+                                                    symbols=False, raw=True
+                                                )
+                                if key is not None and "\n" in key:
+                                    key = None
+                                if key is None:
+                                    self.add_val(
+                                        "key", full_name_child, val_child, inset + 1
+                                    )
+                            else:
+                                if key is not None:
+                                    self.add_val(
+                                        "[" + str(key) + "]",
+                                        full_name_child,
+                                        val_child,
+                                        inset + 1,
+                                    )
+                                else:
+                                    self.add_val(
+                                        "value", full_name_child, val_child, inset + 1
+                                    )
+                    else:
+                        for count, c in enumerate(childs, 1):
+                            if max_elements and count > max_elements:
+                                break
+                            (name_child, val_child) = c
+                            if not isinstance(name_child, str):
+                                name_child = "[%s]" % name_child
+                            full_name_child = ":".join([line_name, name_child])
+                            self.add_val(
+                                name_child,
+                                full_name_child,
+                                val_child,
+                                inset + 1,
+                                def_expand=def_expand_child,
+                            )
+        except:
+            self.add_line(
+                inset + 1,
+                0,
+                None,
+                line_name,
+                undeclared,
+                argument,
+                error=str(sys.exc_info()[1]),
+            )
+
+    def add_line(
+        self,
+        inset,
+        number,
+        name,
+        line_name,
+        undeclared,
+        argument,
+        val=None,
+        prev_val=None,
+        error=None,
+        expand=None,
+    ):
+        var_col_s, var_col_e, val_col_s, val_col_e = "", "", "", ""
+        if val is None:
+            if error is None:
+                # Variable without contents
+                var_col_s, var_col_e = "\033[1;30m", "\033[0m"
+            else:
+                # Error
+                val_col_s, val_col_e = "\033[1;30m<", ">\033[0m"
+                val = error
+        elif prev_val is None:
+            # New variable
+            var_col_s, var_col_e = "\033[1;32m", "\033[0m"
+        elif prev_val != val:
+            # Variable contents changed
+            val_col_s, val_col_e = "\033[1;31m", "\033[0m"
+        if argument:
+            # Variable is a function argument
+            var_col_s, var_col_e = "\033[1;35m", "\033[0m"
+        elif undeclared:
+            # Variable was not yet declared
+            var_col_s, var_col_e = "\033[33m", "\033[0m"
+
+        spaces = "  " * inset
+        if number > 0:
+            numstr = "%d: " % number
+        else:
+            numstr = ""
+        expand_str = "  "
+        if expand is True:
+            expand_str = "- "
+        elif expand is False:
+            expand_str = "+ "
+
+        sp = None
+        line = spaces + numstr + expand_str
+        if name:
+            line += var_col_s + name + var_col_e
+        if val:
+            sp = val.split("\n")
+            val = sp.pop(0)
+            if name:
+                line += " = "
+            line += val_col_s + val + val_col_e
+        self.lines.append(line)
+        self.line_names.append(line_name)
+
+        if sp:
+            for extra in sp:
+                self.lines.append(spaces + "    " + val_col_s + extra + val_col_e)
+                self.line_names.append(None)
+
+
+class VarNameValue(object):
+    """Object returned by LocalsWindow.variables."""
+
+    def __init__(
+        self,
+        symbol,
+        value=None,
+        undeclared=False,
+        argument=False,
+        number=0,
+        raw=False,
+        format=None,
+        error=None,
+        expand=False,
+    ):
+        self._symbol = symbol
+        self._value = value
+        self._undeclared = undeclared
+        self._argument = argument
+        self._number = number
+        self._raw = raw
+        self._format = format
+        self._error = error
+        self._expand = expand
+
+    def symbol(self):
+        return self._symbol
+
+    def value(self):
+        return self._value
+
+    def undeclared(self):
+        return self._undeclared
+
+    def argument(self):
+        return self._argument
+
+    def number(self):
+        return self._number
+
+    def raw(self):
+        return self._raw
+
+    def format(self):
+        return self._format
+
+    def error(self):
+        return self._error
+
+    def expand(self):
+        return self._expand
+
+
+class LocalsWindow(VariableWindow):
+    """Window for local variables."""
+
+    def __init__(self, win):
+        super(LocalsWindow, self).__init__(win)
+
+    def variables(self):
+        thread = gdb.selected_thread()
+        thread_valid = thread and thread.is_valid()
+        if thread_valid:
+            frame = gdb.selected_frame()
+            cur_line = frame.find_sal().line
+            raw = gdb.parameter("print raw-values")
+            try:
+                block = frame.block()
+            except:
+                block = None
+            while block:
+                if not block.is_global:
+                    for symbol in block:
+                        if (
+                            symbol.is_argument
+                            or symbol.is_variable
+                            or symbol.is_constant
+                        ):
+                            sym_not_init = (
+                                symbol.is_variable
+                                and symbol.line > 0
+                                and cur_line <= symbol.line
+                            )
+                            error = None
+                            try:
+                                val = symbol.value(frame)
+                            except:
+                                val = None
+                                error = str(sys.exc_info()[1])
+                            yield VarNameValue(
+                                symbol.name,
+                                value=val,
+                                undeclared=sym_not_init,
+                                argument=symbol.is_argument,
+                                raw=raw,
+                                error=error,
+                            )
+                if block.function:
+                    break
+                block = block.superblock
+
+
+try:
+    gdb.register_window_type
+    has_tui = True
+except AttributeError:
+    has_tui = False
+
+
+def refresh_tui_windows(event=None):
+    """Refresh all TextWindow instances when user is presented a prompt."""
+    global custom_windows
+    for key, value in custom_windows.items():
+        if value.win.is_valid():
+            value.refill()
+            value.redraw()
+
+
+if has_tui:
+    gdb.events.before_prompt.connect(refresh_tui_windows)
+
+    LocalsWindow.register_window_type("locals")
+
+    gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1")
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter
  2024-06-19 17:07 ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Hannes Domani
                     ` (2 preceding siblings ...)
  2024-06-19 17:07   ` [PATCH 4/4] Implement locals TUI window Hannes Domani
@ 2024-06-24 15:45   ` Tom Tromey
  3 siblings, 0 replies; 7+ messages in thread
From: Tom Tromey @ 2024-06-24 15:45 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

>>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:

Hannes> Currently anonymous fields are not shown at all, this shows
Hannes> them with the name [anonymous].

Hannes> And it also marks base classes with <>, e.g. <BaseClass>, so
Hannes> they can be distinguished from struct fields.

I think the patch looks good but I wonder if you could write a test case.

Tom

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer
  2024-06-19 17:07   ` [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer Hannes Domani
@ 2024-06-24 15:48     ` Tom Tromey
  0 siblings, 0 replies; 7+ messages in thread
From: Tom Tromey @ 2024-06-24 15:48 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

>>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:

Hannes> -def make_visualizer(value):
Hannes> +def make_visualizer(value, raw=False):

I looked at the caller in the locals patch, and it hard-codes raw=True.

Given that I think it would be better to just introduce a new function
that creates a no-op printer, and then have make_visualizer call that
new function in the no-op case.

Tom

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 3/4] Use custom format argument in NoOpScalarPrinter
  2024-06-19 17:07   ` [PATCH 3/4] Use custom format argument in NoOpScalarPrinter Hannes Domani
@ 2024-06-24 15:49     ` Tom Tromey
  0 siblings, 0 replies; 7+ messages in thread
From: Tom Tromey @ 2024-06-24 15:49 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

>>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:

Hannes> So values can be shown e.g. in hexadecimal notation.

This seems fine but see my note on patch #2.
Also:

Hannes> -def make_visualizer(value, raw=False):
Hannes> +def make_visualizer(value, raw=False, format=None):
Hannes>      """Given a gdb.Value, wrap it in a pretty-printer.
 
... it'd be good to mention the new argument in the doc string.

Also I wonder if the other no-op printers might want a 'format'
argument.

Tom

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2024-06-24 15:49 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20240619170754.1651-1-ssbssa.ref@yahoo.de>
2024-06-19 17:07 ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Hannes Domani
2024-06-19 17:07   ` [PATCH 2/4] Make it possible to disable pretty-printers in make_visualizer Hannes Domani
2024-06-24 15:48     ` Tom Tromey
2024-06-19 17:07   ` [PATCH 3/4] Use custom format argument in NoOpScalarPrinter Hannes Domani
2024-06-24 15:49     ` Tom Tromey
2024-06-19 17:07   ` [PATCH 4/4] Implement locals TUI window Hannes Domani
2024-06-24 15:45   ` [PATCH 1/4] Handle anonymous fields and base classes in NoOpStructPrinter Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).