* [PATCHv2 1/2] Implement locals TUI window [not found] <20210604162400.3255-1-ssbssa.ref@yahoo.de> @ 2021-06-04 16:23 ` Hannes Domani 2021-06-04 16:24 ` [PATCHv2 2/2] Use method children instead of to_string in pretty printers Hannes Domani 2023-11-13 20:07 ` [PATCHv2 1/2] Implement locals TUI window Tom Tromey 0 siblings, 2 replies; 4+ messages in thread From: Hannes Domani @ 2021-06-04 16:23 UTC (permalink / raw) To: gdb-patches gdb/ChangeLog: 2021-06-04 Hannes Domani <ssbssa@yahoo.de> PR tui/17849 * data-directory/Makefile.in: Add tui_windows.py. * python/lib/gdb/command/tui_windows.py: New file. --- v2: - Added ChangeLog. - The code changed a lot. --- gdb/data-directory/Makefile.in | 1 + gdb/python/lib/gdb/command/tui_windows.py | 912 ++++++++++++++++++++++ 2 files changed, 913 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 8b65790cdd9..b8383ea7808 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -82,6 +82,7 @@ PYTHON_FILE_LIST = \ gdb/command/frame_filters.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..4b75c7b9b7d --- /dev/null +++ b/gdb/python/lib/gdb/command/tui_windows.py @@ -0,0 +1,912 @@ +# Additional TUI windows. +# Copyright 2021 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 + +PY3 = sys.version_info[0] == 3 + +if PY3: + basestring = str + +custom_windows = {} + + +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, title): + self.win = win + self.line_ofs = 0 + self.col_ofs = 0 + self.lines = [] + win.title = title + global custom_windows + custom_windows[title] = self + + def close(self): + global custom_windows + del custom_windows[self.win.title] + + def render(self): + self.line_ofs = 0 + self.col_ofs = 0 + + 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() + + +def val_cmp_color(prev, cur, sym_not_init, argument, empty=False): + """Returns the color escape sequences for variable name and value.""" + var_col_s, var_col_e, val_col_s, val_col_e = "", "", "", "" + if empty: + # Variable without contents + var_col_s, var_col_e = "\033[1;30m", "\033[0m" + elif prev is None: + # New variable + var_col_s, var_col_e = "\033[1;32m", "\033[0m" + elif prev != cur: + # 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 sym_not_init: + # Variable was not yet initialized + var_col_s, var_col_e = "\033[33m", "\033[0m" + return (var_col_s, var_col_e, val_col_s, val_col_e) + + +def octal_escape(s): + """Escape non-printable characters as octal string.""" + return "".join(c if ord(c) < 128 else "\\%03o" % ord(c) for c in s) + + +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, basestring): + l = 1 + else: + l = len(valstr) + if l == 0: + if is_lazy and str(valstr.type) == "wchar_t *": + valstr = 'L""' + else: + valstr = '""' + else: + valstr = gdb.Value(valstr).format_string(symbols=False, address=False) + if isinstance(valstr, gdb.Value): + valstr = valstr.format_string(symbols=False, address=False) + elif not type(valstr) is str: + valstr = str(valstr) + return valstr + + +def is_typedef_of(valtype, name): + """Check for type variants.""" + if valtype.name == name: + return True + while valtype.code == gdb.TYPE_CODE_TYPEDEF: + valtype = valtype.target() + if valtype.name == name: + return True + return False + + +def is_string_element_type(t): + """Return if an array of this type is considered a string.""" + target_type = t.target().strip_typedefs() + return target_type.code == gdb.TYPE_CODE_INT and ( + target_type.name == "char" or is_typedef_of(t.target(), "wchar_t") + ) + + +class ArrayPrinter(object): + """Pretty printer for arrays.""" + + def __init__(self, v, t): + self.v = v + self.t = t + + def to_string(self): + if is_string_element_type(self.t): + return self.v.format_string(symbols=False, raw=True, address=True) + + def children(self): + (low, high) = self.t.range() + for i in range(low, high + 1): + yield "[%d]" % i, self.v[i] + + +class StructPrinter(object): + """Pretty printer for structs/unions.""" + + def __init__(self, v, dyn_type, raw): + self.v = v + self.raw = raw + self.dyn_name = None + if dyn_type and v.type.code == gdb.TYPE_CODE_STRUCT: + try: + if v.type != v.dynamic_type: + self.v = v.cast(v.dynamic_type) + self.dyn_name = " <" + self.v.type.name + ">" + except: + pass + + def to_string(self): + return self.dyn_name + + def children(self): + fields = self.v.type.fields() + num = 1 + for f in fields: + if not hasattr(f, "bitpos"): + continue + try: + n = f.name + except: + n = None + if not n: + n = "<anonymous>" + elif f.is_base_class: + n = "<" + n + ">" + + child_v = self.v[f] + + # Disable dynamic_type for base classes, to prevent infinite + # recursion. + if child_v.type.code == gdb.TYPE_CODE_STRUCT and f.is_base_class: + pp = None + if not self.raw: + try: + pp = gdb.default_visualizer(v) + except: + pass + if pp is None: + child_v = StructPrinter(child_v, False, self.raw) + + yield n, child_v + + def address(self): + return self.v.address + + +class PointerPrinter(object): + """Pretty printer for pointers.""" + + def __init__(self, v): + self.v = v + + def to_string(self): + return self.v.format_string(symbols=False, raw=True, address=True) + + def children(self): + try: + val = self.v.dereference() + except Exception as e: + val = e + yield "[0]", val + + +class ValuePrinter(object): + """Pretty printer for value types that don't have children.""" + + def __init__(self, v, fmt): + self.v = v + self.fmt = fmt + + def to_string(self): + if self.fmt: + return self.v.format_string( + symbols=False, raw=True, address=True, format=self.fmt + ) + else: + return self.v.format_string(symbols=False, raw=True, address=True) + + +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. + """ + + def __init__(self, win, title, convenience_name): + super(VariableWindow, self).__init__(win, title) + self.prev_vals = {} + self.line_names = [] + self.convenience_name = convenience_name + + 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[0] is not None: + prev[0] = not prev[0] + self.refill(True) + self.redraw() + elif button == 3: + # On right-click, set element for convenience variable. + if prev is not None and prev[3] is not None: + gdb.set_convenience_variable(self.convenience_name, prev[3]) + self.refill(True) + self.redraw() + + def refill(self, keep_prev=False): + self.lines = [] + self.line_names = [] + cur_vals = {} + frame = None + for v in self.variables(): + symbol = v.symbol() + if isinstance(symbol, gdb.Symbol): + name = symbol.name + else: + name = symbol + + value = 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() + value = symbol.value(frame) + + sym_not_init = False + if hasattr(v, "undeclared"): + sym_not_init = v.undeclared() + + argument = False + if hasattr(v, "argument"): + argument = v.argument() + + num = 0 + if hasattr(v, "number"): + num = v.number() + + raw = False + if hasattr(v, "raw"): + raw = v.raw() + + fmt = None + if hasattr(v, "format"): + fmt = v.format() + + error = None + if hasattr(v, "error"): + error = v.error() + + self.add_val( + name, + name, + value, + 0, + num, + cur_vals, + keep_prev, + False, + sym_not_init, + argument, + raw, + fmt, + error, + ) + self.prev_vals = cur_vals + + def add_val( + self, + n, + fn, + v, + inset, + num, + cur_vals, + keep_prev, + def_expand, + sym_not_init, + argument, + raw, + fmt, + error, + ): + n2 = fn + if inset == 0: + if num == 0: + count = 1 + while n2 in cur_vals: + count += 1 + n2 = "%s:%d" % (n, count) + else: + n2 = "%d:%s" % (num, n) + + cur_entry = [None, False, None, None] + cur_vals[n2] = cur_entry + expand = None + prev_val = None + if n2 in self.prev_vals: + pv = self.prev_vals[n2] + expand = pv[0] + if keep_prev: + prev_val = pv[2] + else: + prev_val = pv[1] + cur_entry[0] = expand + cur_entry[2] = prev_val + + spaces = " " * inset + if num > 0: + numstr = "%d: " % num + else: + numstr = "" + + if isinstance(v, Exception): + error = v.message + v = None + + if v is None: + if error is None: + (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color( + False, False, sym_not_init, argument, empty=True + ) + self.lines.append(spaces + numstr + " " + var_col_s + n + var_col_e) + else: + (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color( + False, False, sym_not_init, argument + ) + self.lines.append( + spaces + + numstr + + " " + + var_col_s + + n + + var_col_e + + " = \033[1;30m<" + + error + + ">\033[0m" + ) + self.line_names.append(n2) + return + + if isinstance(v, basestring): + (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color( + prev_val, v, sym_not_init, argument + ) + self.lines.append( + spaces + + numstr + + " " + + var_col_s + + n + + var_col_e + + " = " + + val_col_s + + v + + val_col_e + ) + self.line_names.append(n2) + cur_entry[1] = v + return + + is_pp = hasattr(v, "to_string") or hasattr(v, "children") + + v_addr = None + if is_pp: + if hasattr(v, "address"): + try: + v_addr = v.address() + except: + v_addr = None + elif isinstance(v, gdb.Value): + is_optimized_out = False + try: + if v.type.code in [gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF]: + v = v.referenced_value() + + is_optimized_out = v.is_optimized_out + except: + self.add_val( + n, + fn, + None, + inset, + num, + cur_vals, + keep_prev, + False, + sym_not_init, + argument, + raw, + fmt, + str(sys.exc_info()[1]), + ) + return + + if is_optimized_out and v.type.strip_typedefs().code not in [ + gdb.TYPE_CODE_STRUCT, + gdb.TYPE_CODE_UNION, + ]: + self.add_val( + n, + fn, + None, + inset, + num, + cur_vals, + keep_prev, + False, + sym_not_init, + argument, + raw, + fmt, + "optimized out", + ) + return + + v_addr = v.address + else: + v = gdb.Value(v) + + cv_str = "" + if v_addr is not None: + v_addr = v_addr.dereference().reference_value() + cur_entry[3] = v_addr + cv = gdb.convenience_variable(self.convenience_name) + if ( + cv is not None + and cv.address == v_addr.address + and cv.type == v_addr.type + ): + cv_str = " = \033[1;36m$" + self.convenience_name + "\033[0m" + + pp = None + if is_pp: + pp = v + elif not raw: + try: + pp = gdb.default_visualizer(v) + except: + pp = None + + def_expand_child = False + if pp is None: + t = v.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 + + if t.code in [gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_RANGE]: + pp = ArrayPrinter(v, t) + elif t.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]: + pp = StructPrinter(v, True, raw) + elif t.code == gdb.TYPE_CODE_PTR and not ptr_target_type in [ + gdb.TYPE_CODE_FUNC, + gdb.TYPE_CODE_METHOD, + ]: + pp = PointerPrinter(v) + else: + pp = ValuePrinter(v, fmt) + + try: + if expand is None and hasattr(pp, "children"): + expand = def_expand + cur_entry[0] = expand + expand_str = " " + if expand is not None: + if expand: + expand_str = "- " + else: + expand_str = "+ " + numstr += expand_str + + 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 = octal_escape(valstr) + sp = valstr.split("\n") + (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color( + prev_val, valstr, sym_not_init, argument + ) + self.lines.append( + spaces + + numstr + + var_col_s + + n + + var_col_e + + " = " + + val_col_s + + sp.pop(0) + + val_col_e + + cv_str + ) + self.line_names.append(n2) + for extra in sp: + self.lines.append(spaces + " " + extra) + self.line_names.append(None) + cur_entry[1] = valstr + else: + (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color( + prev_val, False, sym_not_init, argument + ) + self.lines.append(spaces + numstr + var_col_s + n + var_col_e + cv_str) + self.line_names.append(n2) + + if expand: + childs = None + if hasattr(pp, "children"): + childs = pp.children() + if childs: + 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 + count = 0 + key = None + for c in childs: + (nc, vc) = c + count += 1 + fnc = ":".join([n2, nc]) + if (count % 2) == 1: + key = None + if isinstance(vc, basestring): + key = vc + else: + vc_check = vc + if vc_check.type.code in [ + gdb.TYPE_CODE_REF, + gdb.TYPE_CODE_RVALUE_REF, + ]: + vc_check = vc_check.referenced_value() + key_pp = gdb.default_visualizer(vc_check) + if key_pp: + if hasattr(key_pp, "to_string") and not hasattr( + key_pp, "children" + ): + vc_check = key_pp.to_string() + if vc_check is not None: + hint = None + if hasattr(key_pp, "display_hint"): + hint = key_pp.display_hint() + key = value_string(vc_check, hint) + else: + t_check = vc_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 fmt: + key = vc_check.format_string( + symbols=False, raw=True, format=fmt + ) + else: + key = vc_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", + fnc, + vc, + inset + 1, + 0, + cur_vals, + keep_prev, + False, + False, + False, + raw, + fmt, + None, + ) + else: + if key is not None: + self.add_val( + "[" + str(key) + "]", + fnc, + vc, + inset + 1, + 0, + cur_vals, + keep_prev, + False, + False, + False, + raw, + fmt, + None, + ) + else: + self.add_val( + "value", + fnc, + vc, + inset + 1, + 0, + cur_vals, + keep_prev, + False, + False, + False, + raw, + fmt, + None, + ) + else: + for c in childs: + (nc, vc) = c + fnc = ":".join([n2, nc]) + self.add_val( + nc, + fnc, + vc, + inset + 1, + 0, + cur_vals, + keep_prev, + def_expand_child, + False, + False, + raw, + fmt, + None, + ) + except: + self.add_val( + n, + fn, + None, + inset, + num, + cur_vals, + keep_prev, + False, + sym_not_init, + argument, + raw, + fmt, + str(sys.exc_info()[1]), + ) + + +class VarNameValue(object): + """Object returned by LocalsWindow.variables.""" + + def __init__( + self, sym, val=None, undecl=False, arg=False, num=0, r=False, fmt=None, err=None + ): + self.sym = sym + self.val = val + self.undecl = undecl + self.arg = arg + self.num = num + self.r = r + self.fmt = fmt + self.err = err + + def symbol(self): + return self.sym + + def value(self): + return self.val + + def undeclared(self): + return self.undecl + + def argument(self): + return self.arg + + def number(self): + return self.num + + def raw(self): + return self.r + + def format(self): + return self.fmt + + def error(self): + return self.err + + +class LocalsWindow(VariableWindow): + """Window for local variables.""" + + def __init__(self, win): + super(LocalsWindow, self).__init__(win, "locals", "lv") + + 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 + 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: + sym_not_init = ( + symbol.is_variable + and symbol.line > 0 + and cur_line <= symbol.line + ) + yield VarNameValue( + symbol.name, + val=symbol.value(frame), + undecl=sym_not_init, + arg=symbol.is_argument, + ) + if block.function: + break + block = block.superblock + + +gdb.register_window_type("locals", LocalsWindow) + + +def before_prompt_handler(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() + + +gdb.events.before_prompt.connect(before_prompt_handler) + + +gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1") -- 2.31.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCHv2 2/2] Use method children instead of to_string in pretty printers 2021-06-04 16:23 ` [PATCHv2 1/2] Implement locals TUI window Hannes Domani @ 2021-06-04 16:24 ` Hannes Domani 2021-07-17 19:19 ` Joel Brobecker 2023-11-13 20:07 ` [PATCHv2 1/2] Implement locals TUI window Tom Tromey 1 sibling, 1 reply; 4+ messages in thread From: Hannes Domani @ 2021-06-04 16:24 UTC (permalink / raw) To: gdb-patches With '-pretty on' the output is basically the same, but like this the pointer members can be expanded in the TUI variable windows. gdb/ChangeLog: 2021-06-04 Hannes Domani <ssbssa@yahoo.de> * gdb-gdb.py.in: Use method children in pretty printers. --- v2: - Added ChangeLog. - Reformatted with black. --- gdb/gdb-gdb.py.in | 175 ++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 98 deletions(-) diff --git a/gdb/gdb-gdb.py.in b/gdb/gdb-gdb.py.in index af9fcfedc2f..1432d7bbcf8 100644 --- a/gdb/gdb-gdb.py.in +++ b/gdb/gdb-gdb.py.in @@ -107,17 +107,13 @@ class StructTypePrettyPrinter: def __init__(self, val): self.val = val - def to_string(self): - fields = [] - fields.append("pointer_type = %s" % self.val["pointer_type"]) - fields.append("reference_type = %s" % self.val["reference_type"]) - fields.append("chain = %s" % self.val["reference_type"]) - fields.append( - "instance_flags = %s" % TypeFlagsPrinter(self.val["m_instance_flags"]) - ) - fields.append("length = %d" % self.val["length"]) - fields.append("main_type = %s" % self.val["main_type"]) - return "\n{" + ",\n ".join(fields) + "}" + def children(self): + for f in self.val.type.fields(): + name = f.name + val = self.val[f] + if name == "m_instance_flags": + val = str(TypeFlagsPrinter(val)) + yield name, val class StructMainTypePrettyPrinter: @@ -142,46 +138,6 @@ class StructMainTypePrettyPrinter: ] return "|".join(fields) - def owner_to_string(self): - """Return an image of component "owner".""" - if self.val["m_flag_objfile_owned"] != 0: - return "%s (objfile)" % self.val["m_owner"]["objfile"] - else: - return "%s (gdbarch)" % self.val["m_owner"]["gdbarch"] - - def struct_field_location_img(self, field_val): - """Return an image of the loc component inside the given field - gdb.Value. - """ - loc_val = field_val["loc"] - loc_kind = str(field_val["loc_kind"]) - if loc_kind == "FIELD_LOC_KIND_BITPOS": - return "bitpos = %d" % loc_val["bitpos"] - elif loc_kind == "FIELD_LOC_KIND_ENUMVAL": - return "enumval = %d" % loc_val["enumval"] - elif loc_kind == "FIELD_LOC_KIND_PHYSADDR": - return "physaddr = 0x%x" % loc_val["physaddr"] - elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": - return "physname = %s" % loc_val["physname"] - elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": - return "dwarf_block = %s" % loc_val["dwarf_block"] - else: - return "loc = ??? (unsupported loc_kind value)" - - def struct_field_img(self, fieldno): - """Return an image of the main_type field number FIELDNO.""" - f = self.val["flds_bnds"]["fields"][fieldno] - label = "flds_bnds.fields[%d]:" % fieldno - if f["artificial"]: - label += " (artificial)" - fields = [] - fields.append("name = %s" % f["name"]) - fields.append("type = %s" % f["m_type"]) - fields.append("loc_kind = %s" % f["loc_kind"]) - fields.append("bitsize = %d" % f["bitsize"]) - fields.append(self.struct_field_location_img(f)) - return label + "\n" + " {" + ",\n ".join(fields) + "}" - def bound_img(self, bound_name): """Return an image of the given main_type's bound.""" bounds = self.val["flds_bnds"]["bounds"].dereference() @@ -197,17 +153,6 @@ class StructMainTypePrettyPrinter: info.append("upper_bound_is_count") return "{} ({})".format(str(b["m_data"]["baton"]), ",".join(info)) - def bounds_img(self): - """Return an image of the main_type bounds.""" - b = self.val["flds_bnds"]["bounds"].dereference() - low = self.bound_img("low") - high = self.bound_img("high") - - img = "flds_bnds.bounds = {%s, %s}" % (low, high) - if b["flag_bound_evaluated"]: - img += " [evaluated]" - return img - def type_specific_img(self): """Return a string image of the main_type type_specific union. Only the relevant component of that union is printed (based on @@ -216,54 +161,86 @@ class StructMainTypePrettyPrinter: type_specific_kind = str(self.val["type_specific_field"]) type_specific = self.val["type_specific"] if type_specific_kind == "TYPE_SPECIFIC_NONE": - img = "type_specific_field = %s" % type_specific_kind + return "type_specific_field", type_specific_kind elif type_specific_kind == "TYPE_SPECIFIC_CPLUS_STUFF": - img = "cplus_stuff = %s" % type_specific["cplus_stuff"] + return "cplus_stuff", type_specific["cplus_stuff"] elif type_specific_kind == "TYPE_SPECIFIC_GNAT_STUFF": - img = ( - "gnat_stuff = {descriptive_type = %s}" - % type_specific["gnat_stuff"]["descriptive_type"] + return ( + "gnat_stuff.descriptive_type", + type_specific["gnat_stuff"]["descriptive_type"], ) elif type_specific_kind == "TYPE_SPECIFIC_FLOATFORMAT": - img = "floatformat[0..1] = %s" % type_specific["floatformat"] + return "floatformat[0..1]", type_specific["floatformat"] elif type_specific_kind == "TYPE_SPECIFIC_FUNC": - img = ( - "calling_convention = %d" - % type_specific["func_stuff"]["calling_convention"] + return ( + "calling_convention", + type_specific["func_stuff"]["calling_convention"], ) # tail_call_list is not printed. elif type_specific_kind == "TYPE_SPECIFIC_SELF_TYPE": - img = "self_type = %s" % type_specific["self_type"] - elif type_specific_kind == "TYPE_SPECIFIC_FIXED_POINT": - # The scaling factor is an opaque structure, so we cannot - # decode its value from Python (not without insider knowledge). - img = ( - "scaling_factor: <opaque> (call __gmpz_dump with " - " _mp_num and _mp_den fields if needed)" - ) - else: - img = ( - "type_specific = ??? (unknown type_secific_kind: %s)" - % type_specific_kind - ) - return img + return "self_type", type_specific["self_type"] + return ( + "type_specific", + "??? (unknown type_secific_kind: %s)" % type_specific_kind, + ) - def to_string(self): - """Return a pretty-printed image of our main_type.""" - fields = [] - fields.append("name = %s" % self.val["name"]) - fields.append("code = %s" % self.val["code"]) - fields.append("flags = [%s]" % self.flags_to_string()) - fields.append("owner = %s" % self.owner_to_string()) - fields.append("target_type = %s" % self.val["target_type"]) + def children(self): + yield "name", self.val["name"].format_string(symbols=False, address=False) + yield "code", self.val["code"] + yield "flags", "[%s]" % self.flags_to_string() + if self.val["m_flag_objfile_owned"] != 0: + yield "owner.objfile", self.val["m_owner"]["objfile"] + else: + yield "owner.gdbarch", self.val["m_owner"]["gdbarch"] + yield "target_type", self.val["target_type"] if self.val["nfields"] > 0: - for fieldno in range(self.val["nfields"]): - fields.append(self.struct_field_img(fieldno)) + fields = self.val["flds_bnds"]["fields"] + field_array_type = ( + fields.type.target().array(self.val["nfields"] - 1).pointer() + ) + yield "flds_bnds.fields", fields.cast(field_array_type).dereference() if self.val["code"] == gdb.TYPE_CODE_RANGE: - fields.append(self.bounds_img()) - fields.append(self.type_specific_img()) + b = self.val["flds_bnds"]["bounds"].dereference() + low = self.bound_img("low") + high = self.bound_img("high") + + img = "{%s, %s}" % (low, high) + if b["flag_bound_evaluated"]: + img += " [evaluated]" + yield "flds_bnds.bounds", img + yield self.type_specific_img() + + +class StructFieldPrettyPrinter: + """Pretty-print an objet of type field""" + + def __init__(self, val): + self.val = val + + def children(self): + if self.val["artificial"]: + yield "artificial", self.val["artificial"] + yield "name", self.val["name"].format_string(symbols=False, address=False) + yield "type", self.val["m_type"] + yield "loc_kind", self.val["loc_kind"] + yield "bitsize", self.val["bitsize"] - return "\n{" + ",\n ".join(fields) + "}" + loc_val = self.val["loc"] + loc_kind = str(self.val["loc_kind"]) + if loc_kind == "FIELD_LOC_KIND_BITPOS": + yield "bitpos", loc_val["bitpos"] + elif loc_kind == "FIELD_LOC_KIND_ENUMVAL": + yield "enumval", loc_val["enumval"] + elif loc_kind == "FIELD_LOC_KIND_PHYSADDR": + yield "physaddr", loc_val["physaddr"] + elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": + yield "physname", loc_val["physname"].format_string( + symbols=False, address=False + ) + elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": + yield "dwarf_block", loc_val["dwarf_block"] + else: + yield "loc", "???" class CoreAddrPrettyPrinter: @@ -284,6 +261,8 @@ def type_lookup_function(val): return StructTypePrettyPrinter(val) elif val.type.tag == "main_type": return StructMainTypePrettyPrinter(val) + elif val.type.tag == "field": + return StructFieldPrettyPrinter(val) elif val.type.name == "CORE_ADDR": return CoreAddrPrettyPrinter(val) return None -- 2.31.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCHv2 2/2] Use method children instead of to_string in pretty printers 2021-06-04 16:24 ` [PATCHv2 2/2] Use method children instead of to_string in pretty printers Hannes Domani @ 2021-07-17 19:19 ` Joel Brobecker 0 siblings, 0 replies; 4+ messages in thread From: Joel Brobecker @ 2021-07-17 19:19 UTC (permalink / raw) To: Hannes Domani via Gdb-patches; +Cc: Joel Brobecker Hi Hannes, On Fri, Jun 04, 2021 at 06:24:00PM +0200, Hannes Domani via Gdb-patches wrote: > With '-pretty on' the output is basically the same, but like this the > pointer members can be expanded in the TUI variable windows. > > gdb/ChangeLog: > > 2021-06-04 Hannes Domani <ssbssa@yahoo.de> > > * gdb-gdb.py.in: Use method children in pretty printers. It looks like no one has reviewed this patch, yet, so I took a look. Overall, this looks good to me, except for one area: > --- > v2: > - Added ChangeLog. > - Reformatted with black. > --- > gdb/gdb-gdb.py.in | 175 ++++++++++++++++++++-------------------------- > 1 file changed, 77 insertions(+), 98 deletions(-) > > diff --git a/gdb/gdb-gdb.py.in b/gdb/gdb-gdb.py.in > index af9fcfedc2f..1432d7bbcf8 100644 > --- a/gdb/gdb-gdb.py.in > +++ b/gdb/gdb-gdb.py.in > @@ -107,17 +107,13 @@ class StructTypePrettyPrinter: > def __init__(self, val): > self.val = val > > - def to_string(self): > - fields = [] > - fields.append("pointer_type = %s" % self.val["pointer_type"]) > - fields.append("reference_type = %s" % self.val["reference_type"]) > - fields.append("chain = %s" % self.val["reference_type"]) > - fields.append( > - "instance_flags = %s" % TypeFlagsPrinter(self.val["m_instance_flags"]) > - ) > - fields.append("length = %d" % self.val["length"]) > - fields.append("main_type = %s" % self.val["main_type"]) > - return "\n{" + ",\n ".join(fields) + "}" > + def children(self): > + for f in self.val.type.fields(): > + name = f.name > + val = self.val[f] > + if name == "m_instance_flags": > + val = str(TypeFlagsPrinter(val)) > + yield name, val > > > class StructMainTypePrettyPrinter: > @@ -142,46 +138,6 @@ class StructMainTypePrettyPrinter: > ] > return "|".join(fields) > > - def owner_to_string(self): > - """Return an image of component "owner".""" > - if self.val["m_flag_objfile_owned"] != 0: > - return "%s (objfile)" % self.val["m_owner"]["objfile"] > - else: > - return "%s (gdbarch)" % self.val["m_owner"]["gdbarch"] > - > - def struct_field_location_img(self, field_val): > - """Return an image of the loc component inside the given field > - gdb.Value. > - """ > - loc_val = field_val["loc"] > - loc_kind = str(field_val["loc_kind"]) > - if loc_kind == "FIELD_LOC_KIND_BITPOS": > - return "bitpos = %d" % loc_val["bitpos"] > - elif loc_kind == "FIELD_LOC_KIND_ENUMVAL": > - return "enumval = %d" % loc_val["enumval"] > - elif loc_kind == "FIELD_LOC_KIND_PHYSADDR": > - return "physaddr = 0x%x" % loc_val["physaddr"] > - elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": > - return "physname = %s" % loc_val["physname"] > - elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": > - return "dwarf_block = %s" % loc_val["dwarf_block"] > - else: > - return "loc = ??? (unsupported loc_kind value)" > - > - def struct_field_img(self, fieldno): > - """Return an image of the main_type field number FIELDNO.""" > - f = self.val["flds_bnds"]["fields"][fieldno] > - label = "flds_bnds.fields[%d]:" % fieldno > - if f["artificial"]: > - label += " (artificial)" > - fields = [] > - fields.append("name = %s" % f["name"]) > - fields.append("type = %s" % f["m_type"]) > - fields.append("loc_kind = %s" % f["loc_kind"]) > - fields.append("bitsize = %d" % f["bitsize"]) > - fields.append(self.struct_field_location_img(f)) > - return label + "\n" + " {" + ",\n ".join(fields) + "}" > - > def bound_img(self, bound_name): > """Return an image of the given main_type's bound.""" > bounds = self.val["flds_bnds"]["bounds"].dereference() > @@ -197,17 +153,6 @@ class StructMainTypePrettyPrinter: > info.append("upper_bound_is_count") > return "{} ({})".format(str(b["m_data"]["baton"]), ",".join(info)) > > - def bounds_img(self): > - """Return an image of the main_type bounds.""" > - b = self.val["flds_bnds"]["bounds"].dereference() > - low = self.bound_img("low") > - high = self.bound_img("high") > - > - img = "flds_bnds.bounds = {%s, %s}" % (low, high) > - if b["flag_bound_evaluated"]: > - img += " [evaluated]" > - return img > - > def type_specific_img(self): > """Return a string image of the main_type type_specific union. > Only the relevant component of that union is printed (based on > @@ -216,54 +161,86 @@ class StructMainTypePrettyPrinter: > type_specific_kind = str(self.val["type_specific_field"]) > type_specific = self.val["type_specific"] > if type_specific_kind == "TYPE_SPECIFIC_NONE": > - img = "type_specific_field = %s" % type_specific_kind > + return "type_specific_field", type_specific_kind > elif type_specific_kind == "TYPE_SPECIFIC_CPLUS_STUFF": > - img = "cplus_stuff = %s" % type_specific["cplus_stuff"] > + return "cplus_stuff", type_specific["cplus_stuff"] > elif type_specific_kind == "TYPE_SPECIFIC_GNAT_STUFF": > - img = ( > - "gnat_stuff = {descriptive_type = %s}" > - % type_specific["gnat_stuff"]["descriptive_type"] > + return ( > + "gnat_stuff.descriptive_type", > + type_specific["gnat_stuff"]["descriptive_type"], > ) > elif type_specific_kind == "TYPE_SPECIFIC_FLOATFORMAT": > - img = "floatformat[0..1] = %s" % type_specific["floatformat"] > + return "floatformat[0..1]", type_specific["floatformat"] > elif type_specific_kind == "TYPE_SPECIFIC_FUNC": > - img = ( > - "calling_convention = %d" > - % type_specific["func_stuff"]["calling_convention"] > + return ( > + "calling_convention", > + type_specific["func_stuff"]["calling_convention"], > ) > # tail_call_list is not printed. > elif type_specific_kind == "TYPE_SPECIFIC_SELF_TYPE": > - img = "self_type = %s" % type_specific["self_type"] > - elif type_specific_kind == "TYPE_SPECIFIC_FIXED_POINT": > - # The scaling factor is an opaque structure, so we cannot > - # decode its value from Python (not without insider knowledge). > - img = ( > - "scaling_factor: <opaque> (call __gmpz_dump with " > - " _mp_num and _mp_den fields if needed)" > - ) > - else: > - img = ( > - "type_specific = ??? (unknown type_secific_kind: %s)" > - % type_specific_kind > - ) > - return img > + return "self_type", type_specific["self_type"] > + return ( > + "type_specific", > + "??? (unknown type_secific_kind: %s)" % type_specific_kind, > + ) It looks to me like you collapsed the case of TYPE_SPECIFIC_FIXED_POINT with the "else:" part. While we are unable to print the actual data in both cases, I still think it's worth showing preserving the current behavior, because it confirms the type-specific kind, and also shows to the user that the value is opaque, with a tipe on how to get it in practice. The rest looked good to me. > > - def to_string(self): > - """Return a pretty-printed image of our main_type.""" > - fields = [] > - fields.append("name = %s" % self.val["name"]) > - fields.append("code = %s" % self.val["code"]) > - fields.append("flags = [%s]" % self.flags_to_string()) > - fields.append("owner = %s" % self.owner_to_string()) > - fields.append("target_type = %s" % self.val["target_type"]) > + def children(self): > + yield "name", self.val["name"].format_string(symbols=False, address=False) > + yield "code", self.val["code"] > + yield "flags", "[%s]" % self.flags_to_string() > + if self.val["m_flag_objfile_owned"] != 0: > + yield "owner.objfile", self.val["m_owner"]["objfile"] > + else: > + yield "owner.gdbarch", self.val["m_owner"]["gdbarch"] > + yield "target_type", self.val["target_type"] > if self.val["nfields"] > 0: > - for fieldno in range(self.val["nfields"]): > - fields.append(self.struct_field_img(fieldno)) > + fields = self.val["flds_bnds"]["fields"] > + field_array_type = ( > + fields.type.target().array(self.val["nfields"] - 1).pointer() > + ) > + yield "flds_bnds.fields", fields.cast(field_array_type).dereference() > if self.val["code"] == gdb.TYPE_CODE_RANGE: > - fields.append(self.bounds_img()) > - fields.append(self.type_specific_img()) > + b = self.val["flds_bnds"]["bounds"].dereference() > + low = self.bound_img("low") > + high = self.bound_img("high") > + > + img = "{%s, %s}" % (low, high) > + if b["flag_bound_evaluated"]: > + img += " [evaluated]" > + yield "flds_bnds.bounds", img > + yield self.type_specific_img() > + > + > +class StructFieldPrettyPrinter: > + """Pretty-print an objet of type field""" > + > + def __init__(self, val): > + self.val = val > + > + def children(self): > + if self.val["artificial"]: > + yield "artificial", self.val["artificial"] > + yield "name", self.val["name"].format_string(symbols=False, address=False) > + yield "type", self.val["m_type"] > + yield "loc_kind", self.val["loc_kind"] > + yield "bitsize", self.val["bitsize"] > > - return "\n{" + ",\n ".join(fields) + "}" > + loc_val = self.val["loc"] > + loc_kind = str(self.val["loc_kind"]) > + if loc_kind == "FIELD_LOC_KIND_BITPOS": > + yield "bitpos", loc_val["bitpos"] > + elif loc_kind == "FIELD_LOC_KIND_ENUMVAL": > + yield "enumval", loc_val["enumval"] > + elif loc_kind == "FIELD_LOC_KIND_PHYSADDR": > + yield "physaddr", loc_val["physaddr"] > + elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": > + yield "physname", loc_val["physname"].format_string( > + symbols=False, address=False > + ) > + elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": > + yield "dwarf_block", loc_val["dwarf_block"] > + else: > + yield "loc", "???" > > > class CoreAddrPrettyPrinter: > @@ -284,6 +261,8 @@ def type_lookup_function(val): > return StructTypePrettyPrinter(val) > elif val.type.tag == "main_type": > return StructMainTypePrettyPrinter(val) > + elif val.type.tag == "field": > + return StructFieldPrettyPrinter(val) > elif val.type.name == "CORE_ADDR": > return CoreAddrPrettyPrinter(val) > return None > -- > 2.31.1 > -- Joel ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCHv2 1/2] Implement locals TUI window 2021-06-04 16:23 ` [PATCHv2 1/2] Implement locals TUI window Hannes Domani 2021-06-04 16:24 ` [PATCHv2 2/2] Use method children instead of to_string in pretty printers Hannes Domani @ 2023-11-13 20:07 ` Tom Tromey 1 sibling, 0 replies; 4+ messages in thread From: Tom Tromey @ 2023-11-13 20:07 UTC (permalink / raw) To: Hannes Domani via Gdb-patches; +Cc: Hannes Domani >>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes: I'm sorry about the extreme delay on this. Hannes> gdb/ChangeLog: Hannes> 2021-06-04 Hannes Domani <ssbssa@yahoo.de> Hannes> PR tui/17849 Hannes> * data-directory/Makefile.in: Add tui_windows.py. Hannes> * python/lib/gdb/command/tui_windows.py: New file. I recently became aware there was a v2 of this patch. I must have missed it when you originally sent it. Hannes> +PY3 = sys.version_info[0] == 3 Hannes> + Hannes> +if PY3: Hannes> + basestring = str Can drop this now. Hannes> +def val_cmp_color(prev, cur, sym_not_init, argument, empty=False): Hannes> + """Returns the color escape sequences for variable name and value.""" Hannes> + var_col_s, var_col_e, val_col_s, val_col_e = "", "", "", "" Hannes> + if empty: Hannes> + # Variable without contents Hannes> + var_col_s, var_col_e = "\033[1;30m", "\033[0m" Hannes> + elif prev is None: Hannes> + # New variable Hannes> + var_col_s, var_col_e = "\033[1;32m", "\033[0m" Hannes> + elif prev != cur: Hannes> + # Variable contents changed Hannes> + val_col_s, val_col_e = "\033[1;31m", "\033[0m" Hannes> + if argument: Hannes> + # Variable is a function argument Hannes> + var_col_s, var_col_e = "\033[1;35m", "\033[0m" Hannes> + elif sym_not_init: Hannes> + # Variable was not yet initialized Hannes> + var_col_s, var_col_e = "\033[33m", "\033[0m" Hannes> + return (var_col_s, var_col_e, val_col_s, val_col_e) Perhaps these should have 'set style' parameters? Or reuse existing ones? Hannes> +def is_string_element_type(t): Hannes> + """Return if an array of this type is considered a string.""" Hannes> + target_type = t.target().strip_typedefs() Hannes> + return target_type.code == gdb.TYPE_CODE_INT and ( Hannes> + target_type.name == "char" or is_typedef_of(t.target(), "wchar_t") Hannes> + ) There's a more direct way of doing some of this stuff now. This code can probably reuse the work done for DAP. Hannes> +class ArrayPrinter(object): Hannes> + """Pretty printer for arrays.""" Same for this and the other "no-op" printers. Hannes> +class StructPrinter(object): ... Hannes> + for f in fields: Hannes> + if not hasattr(f, "bitpos"): Hannes> + continue Oops, I think this points out a bug in NoOpStructPrinter. I'll fix that. Hannes> + # Disable dynamic_type for base classes, to prevent infinite Hannes> + # recursion. Hannes> + if child_v.type.code == gdb.TYPE_CODE_STRUCT and f.is_base_class: Hannes> + pp = None Hannes> + if not self.raw: Hmm I wonder if this is one as well. Hannes> + if key is not None: Hannes> + self.add_val( Hannes> + "[" + str(key) + "]", Hannes> + fnc, Hannes> + vc, Hannes> + inset + 1, Hannes> + 0, Hannes> + cur_vals, Hannes> + keep_prev, Hannes> + False, Hannes> + False, Hannes> + False, Hannes> + raw, Hannes> + fmt, Hannes> + None, Hannes> + ) Hannes> + else: Hannes> + self.add_val( Hannes> + "value", Hannes> + fnc, Hannes> + vc, Hannes> + inset + 1, Hannes> + 0, Hannes> + cur_vals, Hannes> + keep_prev, Hannes> + False, Hannes> + False, Hannes> + False, Hannes> + raw, Hannes> + fmt, Hannes> + None, Hannes> + ) Some things like this could probably be simplified? I found some of this code pretty hard to follow. Tom ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2023-11-13 20:08 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <20210604162400.3255-1-ssbssa.ref@yahoo.de> 2021-06-04 16:23 ` [PATCHv2 1/2] Implement locals TUI window Hannes Domani 2021-06-04 16:24 ` [PATCHv2 2/2] Use method children instead of to_string in pretty printers Hannes Domani 2021-07-17 19:19 ` Joel Brobecker 2023-11-13 20:07 ` [PATCHv2 1/2] Implement locals TUI window 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).