* [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).