|
| 1 | +import os |
| 2 | +import sys |
| 3 | +from typing import Tuple |
| 4 | + |
| 5 | +import gdb |
| 6 | +import gdb.printing |
| 7 | +import gdb.types |
| 8 | +from gdb.printing import PrettyPrinter |
| 9 | + |
| 10 | +import natvis |
| 11 | +import parser |
| 12 | + |
| 13 | + |
| 14 | +def format_type(type, force_name: str = None) -> Tuple[str, str]: |
| 15 | + if type.code == gdb.TYPE_CODE_PTR: |
| 16 | + target_pre, target_post = format_type(type.target()) |
| 17 | + return target_pre + "*", target_post |
| 18 | + elif type.code == gdb.TYPE_CODE_ARRAY: |
| 19 | + base = type.target() |
| 20 | + size = int(type.sizeof / base.sizeof) |
| 21 | + |
| 22 | + target_pre, target_post = format_type(type.target()) |
| 23 | + |
| 24 | + return target_pre, target_post + "[" + str(size) + "]" |
| 25 | + elif type.code == gdb.TYPE_CODE_STRUCT or type.code == gdb.TYPE_CODE_UNION: |
| 26 | + if type.code == gdb.TYPE_CODE_STRUCT: |
| 27 | + out = "struct" |
| 28 | + else: |
| 29 | + out = "union" |
| 30 | + |
| 31 | + if force_name is not None: |
| 32 | + out += " " + force_name |
| 33 | + |
| 34 | + out += " {\n" |
| 35 | + for f in type.fields(): |
| 36 | + pre, post = format_type(f.type) |
| 37 | + out += "\n".join(" " + x for x in pre.splitlines()) |
| 38 | + |
| 39 | + out += (" " + f.name if f.name is not None else "") + post + ";\n" |
| 40 | + out += "}" |
| 41 | + |
| 42 | + return out, "" |
| 43 | + elif type.code == gdb.TYPE_CODE_TYPEDEF: |
| 44 | + return format_type(type.target(), force_name) |
| 45 | + else: |
| 46 | + return type.name or "", "" |
| 47 | + |
| 48 | + |
| 49 | +def stringify_type(type, type_name: str = None): |
| 50 | + pre, post = format_type(type, type_name) |
| 51 | + |
| 52 | + return pre + post + ";" |
| 53 | + |
| 54 | + |
| 55 | +class NatvisPrinter: |
| 56 | + def __init__(self, type: natvis.NatvisType, val): |
| 57 | + self.val = val |
| 58 | + self.type = type |
| 59 | + self.c_type = stringify_type(self.val.type, "val_type") |
| 60 | + |
| 61 | + def check_condition(self, cond: str) -> bool: |
| 62 | + if cond is None: |
| 63 | + return True |
| 64 | + |
| 65 | + return bool(self._get_value(cond)) |
| 66 | + |
| 67 | + def _get_value(self, expression): |
| 68 | + val = parser.evaluate_expression(self.val, self.c_type, expression) |
| 69 | + if val is not None: |
| 70 | + return val |
| 71 | + else: |
| 72 | + # Return the expression as a string in case the execution failed |
| 73 | + return "{" + expression + "}" |
| 74 | + |
| 75 | + def children(self): |
| 76 | + for item in self.type.expand_items: |
| 77 | + if self.check_condition(item.condition): |
| 78 | + yield item.name, self._get_value(item.expression.base_expression) |
| 79 | + |
| 80 | + def to_string(self): |
| 81 | + for string in self.type.display_parsers: |
| 82 | + if self.check_condition(string.condition): |
| 83 | + display_args = [] |
| 84 | + for code in string.parser.code_parts: |
| 85 | + display_args.append(str(self._get_value(code.base_expression))) |
| 86 | + return string.parser.template_string.format(*display_args) |
| 87 | + |
| 88 | + return "No visualizer available" |
| 89 | + |
| 90 | + |
| 91 | +class NatvisPrettyPrinter(PrettyPrinter): |
| 92 | + def __init__(self, name, subprinters=None): |
| 93 | + super().__init__(name, subprinters) |
| 94 | + self.manager = natvis.NatvisManager() |
| 95 | + |
| 96 | + def __call__(self, val): |
| 97 | + type = gdb.types.get_basic_type(val.type) |
| 98 | + if not type: |
| 99 | + type = val.type |
| 100 | + if not type: |
| 101 | + return None |
| 102 | + if type.name is None: |
| 103 | + # We can't handle unnamed types |
| 104 | + return None |
| 105 | + |
| 106 | + symbol = gdb.lookup_symbol(type.name) |
| 107 | + |
| 108 | + if symbol is None: |
| 109 | + return None |
| 110 | + |
| 111 | + symbtab = symbol[0].symtab |
| 112 | + |
| 113 | + filename = symbtab.filename |
| 114 | + |
| 115 | + natvis_type = self.manager.lookup_type(type.name, filename) |
| 116 | + |
| 117 | + if natvis_type is None: |
| 118 | + return None |
| 119 | + |
| 120 | + return NatvisPrinter(natvis_type, val) |
| 121 | + |
| 122 | + |
| 123 | +def add_natvis_printers(): |
| 124 | + if os.environ.get("GDB_NATVIS_DEBUG") is not None: |
| 125 | + import pydevd as pydevd |
| 126 | + pydevd.settrace('localhost', port=41879, stdoutToServer=True, stderrToServer=True, suspend=False) |
| 127 | + |
| 128 | + gdb.printing.register_pretty_printer(gdb.current_objfile(), NatvisPrettyPrinter("Natvis")) |
0 commit comments