|
| 1 | + |
| 2 | +import gdb |
| 3 | +import re |
| 4 | + |
| 5 | +################################################################################################# |
| 6 | +##### Type pretty printers |
| 7 | +################################################################################################# |
| 8 | + |
| 9 | +class NimTypePrinter(gdb.types.TypePrinter): |
| 10 | + "Nim type printer, one printer for all Nim types" |
| 11 | + |
| 12 | + type_hash_regex = re.compile("^(.+?)_([A-Za-z0-9]+)$") |
| 13 | + |
| 14 | + type_map_static = { |
| 15 | + 'NI': 'int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32', 'NI64': 'in64', |
| 16 | + 'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32', 'NU64': 'uint64', |
| 17 | + 'NF': 'float', 'NF32': 'float32', 'NF32': 'float64', |
| 18 | + 'NIM_BOOL': 'bool', 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', |
| 19 | + 'NimStringDesc': 'string' |
| 20 | + } |
| 21 | + |
| 22 | + def __init__ (self): |
| 23 | + super (NimTypePrinter, self).__init__ ("NimTypePrinter") |
| 24 | + |
| 25 | + @staticmethod |
| 26 | + def rti(type_name): |
| 27 | + "Get static const TNimType variable, should be available for every non trivial Nim type" |
| 28 | + |
| 29 | + m = NimTypePrinter.type_hash_regex.match(type_name) |
| 30 | + if m is not None: |
| 31 | + try: |
| 32 | + return gdb.parse_and_eval("NTI_" + m.group(2) + "_") |
| 33 | + except: |
| 34 | + return None |
| 35 | + |
| 36 | + def instantiate(self): |
| 37 | + return self._recognizer() |
| 38 | + |
| 39 | + class _recognizer(object): |
| 40 | + |
| 41 | + def recognize(self, type_obj): |
| 42 | + |
| 43 | + tname = "" |
| 44 | + if type_obj.tag is not None: |
| 45 | + tname = type_obj.tag |
| 46 | + elif type_obj.name is not None: |
| 47 | + tname = type_obj.name |
| 48 | + else: |
| 49 | + return None |
| 50 | + |
| 51 | + result = NimTypePrinter.type_map_static.get(tname, None) |
| 52 | + if result is not None: |
| 53 | + return result |
| 54 | + |
| 55 | + rti = NimTypePrinter.rti(tname) |
| 56 | + if rti is not None: |
| 57 | + return str(rti['name']) |
| 58 | + |
| 59 | + return None |
| 60 | + |
| 61 | +nimobjfile = gdb.current_objfile() or gdb.objfiles()[0] |
| 62 | +nimobjfile.type_printers = [] |
| 63 | +nimobjfile.type_printers = [NimTypePrinter()] |
| 64 | + |
| 65 | + |
| 66 | +################################################################################################# |
| 67 | +##### GDB Function, equivalent of Nim's $ operator |
| 68 | +################################################################################################# |
| 69 | + |
| 70 | +class DollarPrintFunction (gdb.Function): |
| 71 | + "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)" |
| 72 | + |
| 73 | + _gdb_dollar_functions = gdb.execute("info functions dollar__", True, True) |
| 74 | + dollar_functions = re.findall('NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);', _gdb_dollar_functions) |
| 75 | + |
| 76 | + def __init__ (self): |
| 77 | + super (DollarPrintFunction, self).__init__("dollar") |
| 78 | + |
| 79 | + @staticmethod |
| 80 | + def invoke_static(arg): |
| 81 | + |
| 82 | + for func, arg_typ in DollarPrintFunction.dollar_functions: |
| 83 | + |
| 84 | + if arg.type.name == arg_typ: |
| 85 | + func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() |
| 86 | + return func_value(arg) |
| 87 | + |
| 88 | + if arg.type.name + " *" == arg_typ: |
| 89 | + func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() |
| 90 | + return func_value(arg.address) |
| 91 | + |
| 92 | + raise ValueError("No suitable Nim $ operator found for type: " + arg.type.name) |
| 93 | + |
| 94 | + def invoke(self, arg): |
| 95 | + return self.invoke_static(arg) |
| 96 | + |
| 97 | +DollarPrintFunction() |
| 98 | + |
| 99 | + |
| 100 | +################################################################################################# |
| 101 | +##### GDB Command, equivalent of Nim's $ operator |
| 102 | +################################################################################################# |
| 103 | + |
| 104 | +class DollarPrintCmd (gdb.Command): |
| 105 | + """Dollar print command for Nim, `$ expr` will invoke Nim's $ operator""" |
| 106 | + |
| 107 | + def __init__ (self): |
| 108 | + super (DollarPrintCmd, self).__init__ ("$", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION) |
| 109 | + |
| 110 | + def invoke (self, arg, from_tty): |
| 111 | + param = gdb.parse_and_eval(arg) |
| 112 | + gdb.write(str(DollarPrintFunction.invoke_static(param)) + "\n", gdb.STDOUT) |
| 113 | + |
| 114 | +DollarPrintCmd() |
| 115 | + |
| 116 | + |
| 117 | +################################################################################################# |
| 118 | +##### Value pretty printers |
| 119 | +################################################################################################# |
| 120 | + |
| 121 | +class NimBoolPrinter: |
| 122 | + |
| 123 | + pattern = re.compile(r'^NIM_BOOL$') |
| 124 | + |
| 125 | + def __init__(self, val): |
| 126 | + self.val = val |
| 127 | + |
| 128 | + def display_hint(self): |
| 129 | + return 'bool' |
| 130 | + |
| 131 | + def to_string(self): |
| 132 | + if self.val == 0: |
| 133 | + return "false" |
| 134 | + else: |
| 135 | + return "true" |
| 136 | + |
| 137 | +################################################################ |
| 138 | + |
| 139 | +class NimStringPrinter: |
| 140 | + |
| 141 | + pattern = re.compile(r'^NimStringDesc \*$') |
| 142 | + |
| 143 | + def __init__(self, val): |
| 144 | + self.val = val |
| 145 | + |
| 146 | + def display_hint(self): |
| 147 | + return 'string' |
| 148 | + |
| 149 | + def to_string(self): |
| 150 | + if self.val: |
| 151 | + l = int(self.val['Sup']['len']) |
| 152 | + return self.val['data'][0].address.string("utf-8", "ignore", l) |
| 153 | + else: |
| 154 | + return "<nil>" |
| 155 | + |
| 156 | + |
| 157 | +################################################################ |
| 158 | + |
| 159 | +class NimEnumPrinter: |
| 160 | + |
| 161 | + pattern = re.compile(r'^tyEnum_(.+?)_([A-Za-z0-9]+)$') |
| 162 | + |
| 163 | + reprEnum = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTIONS_DOMAIN).value() |
| 164 | + |
| 165 | + def __init__(self, val): |
| 166 | + self.val = val |
| 167 | + if self.reprEnum is None: |
| 168 | + raise ValueError("reprEnum function symbol is not found, can't display Nim enum. reprEnum was likely removed by dead code elimination") |
| 169 | + |
| 170 | + def display_hint(self): |
| 171 | + return 'enum' |
| 172 | + |
| 173 | + def to_string(self): |
| 174 | + try: |
| 175 | + m = self.pattern.match(str(self.val.type)) |
| 176 | + nti = gdb.parse_and_eval("NTI_" + m.group(2) + "_").address |
| 177 | + return self.reprEnum(self.val, nti) |
| 178 | + except Exception as e: |
| 179 | + gdb.write("reprEnum exception: " + str(e) + "\n", gdb.STDERR) |
| 180 | + |
| 181 | + |
| 182 | +################################################################ |
| 183 | + |
| 184 | +class NimSetPrinter: |
| 185 | + |
| 186 | + pattern = re.compile(r'^tySet_(.+?)_([A-Za-z0-9]+)$') |
| 187 | + |
| 188 | + def __init__(self, val): |
| 189 | + self.val = val |
| 190 | + |
| 191 | + def to_string(self): |
| 192 | + try: |
| 193 | + return DollarPrintFunction.invoke_static(self.val) |
| 194 | + except: |
| 195 | + gdb.write("RTI information not found for set, likely removed by dead code elimination: " + str(self.val.type) + "\n", gdb.STDERR) |
| 196 | + |
| 197 | + |
| 198 | +################################################################ |
| 199 | + |
| 200 | +class NimSeqPrinter: |
| 201 | + |
| 202 | + pattern = re.compile(r'^tySequence_.*$') |
| 203 | + |
| 204 | + def __init__(self, val): |
| 205 | + self.val = val |
| 206 | + |
| 207 | + def display_hint(self): |
| 208 | + return 'array' |
| 209 | + |
| 210 | + def to_string(self): |
| 211 | + return 'seq' |
| 212 | + |
| 213 | + def children(self): |
| 214 | + if not self.val: |
| 215 | + yield ("seq", "<nil>") |
| 216 | + raise StopIteration |
| 217 | + |
| 218 | + len = int(self.val['Sup']['len']) |
| 219 | + for i in range(len): |
| 220 | + yield ('[{0}]'.format(i), self.val["data"][i]) |
| 221 | + |
| 222 | +################################################################ |
| 223 | + |
| 224 | +class NimObjectPrinter: |
| 225 | + |
| 226 | + pattern = re.compile(r'^tyObject_.*$') |
| 227 | + |
| 228 | + def __init__(self, val): |
| 229 | + self.val = val |
| 230 | + |
| 231 | + def display_hint(self): |
| 232 | + return 'object' |
| 233 | + |
| 234 | + def to_string(self): |
| 235 | + return str(self.val.type) |
| 236 | + |
| 237 | + def children(self): |
| 238 | + if not self.val: |
| 239 | + yield "object", "<nil>" |
| 240 | + raise StopIteration |
| 241 | + |
| 242 | + for (i, field) in enumerate(self.val.type.fields()): |
| 243 | + if field.type.code == gdb.TYPE_CODE_UNION: |
| 244 | + yield _union_field |
| 245 | + else: |
| 246 | + yield (field.name, self.val[field]) |
| 247 | + |
| 248 | + def _union_field(self, i, field): |
| 249 | + rti = NimTypePrinter.rti(self.val.type.name) |
| 250 | + if rti is None: |
| 251 | + return (field.name, "UNION field can't be displayed without RTI") |
| 252 | + |
| 253 | + node_sons = rti['node'].dereference()['sons'] |
| 254 | + prev_field = self.val.type.fields()[i - 1] |
| 255 | + |
| 256 | + descriminant_node = None |
| 257 | + for i in range(int(node['len'])): |
| 258 | + son = node_sons[i].dereference() |
| 259 | + if son['name'].string("utf-8", "ignore") == str(prev_field.name): |
| 260 | + descriminant_node = son |
| 261 | + break |
| 262 | + if descriminant_node is None: |
| 263 | + raise ValueError("Can't find union descriminant field in object RTI") |
| 264 | + |
| 265 | + if descriminant_node is None: raise ValueError("Can't find union field in object RTI") |
| 266 | + union_node = descriminant_node['sons'][int(self.val[prev_field])].dereference() |
| 267 | + union_val = self.val[field] |
| 268 | + |
| 269 | + for f1 in union_val.type.fields(): |
| 270 | + for f2 in union_val[f1].type.fields(): |
| 271 | + if str(f2.name) == union_node['name'].string("utf-8", "ignore"): |
| 272 | + return (str(f2.name), union_val[f1][f2]) |
| 273 | + |
| 274 | + raise ValueError("RTI is absent or incomplete, can't find union definition in RTI") |
| 275 | + |
| 276 | + |
| 277 | +################################################################ |
| 278 | + |
| 279 | +def makematcher(klass): |
| 280 | + def matcher(val): |
| 281 | + try: |
| 282 | + if klass.pattern.match(str(val.type)): |
| 283 | + return klass(val) |
| 284 | + except Exception, e: |
| 285 | + print("Nim matcher exception: ", str(e)) |
| 286 | + return matcher |
| 287 | + |
| 288 | +nimobjfile.pretty_printers = [] |
| 289 | +nimobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')]) |
| 290 | + |
0 commit comments