1from sys import version_info 2 3import gdb 4 5if version_info[0] >= 3: 6 xrange = range 7 8ZERO_FIELD = "__0" 9FIRST_FIELD = "__1" 10 11 12def unwrap_unique_or_non_null(unique_or_nonnull): 13 # BACKCOMPAT: rust 1.32 14 # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 15 # BACKCOMPAT: rust 1.60 16 # https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f 17 ptr = unique_or_nonnull["pointer"] 18 return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]] 19 20 21# GDB 14 has a tag class that indicates that extension methods are ok 22# to call. Use of this tag only requires that printers hide local 23# attributes and methods by prefixing them with "_". 24if hasattr(gdb, 'ValuePrinter'): 25 printer_base = gdb.ValuePrinter 26else: 27 printer_base = object 28 29 30class EnumProvider(printer_base): 31 def __init__(self, valobj): 32 content = valobj[valobj.type.fields()[0]] 33 fields = content.type.fields() 34 self._empty = len(fields) == 0 35 if not self._empty: 36 if len(fields) == 1: 37 discriminant = 0 38 else: 39 discriminant = int(content[fields[0]]) + 1 40 self._active_variant = content[fields[discriminant]] 41 self._name = fields[discriminant].name 42 self._full_name = "{}::{}".format(valobj.type.name, self._name) 43 else: 44 self._full_name = valobj.type.name 45 46 def to_string(self): 47 return self._full_name 48 49 def children(self): 50 if not self._empty: 51 yield self._name, self._active_variant 52 53 54class StdStringProvider(printer_base): 55 def __init__(self, valobj): 56 self._valobj = valobj 57 vec = valobj["vec"] 58 self._length = int(vec["len"]) 59 self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"]) 60 61 def to_string(self): 62 return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) 63 64 @staticmethod 65 def display_hint(): 66 return "string" 67 68 69class StdOsStringProvider(printer_base): 70 def __init__(self, valobj): 71 self._valobj = valobj 72 buf = self._valobj["inner"]["inner"] 73 is_windows = "Wtf8Buf" in buf.type.name 74 vec = buf[ZERO_FIELD] if is_windows else buf 75 76 self._length = int(vec["len"]) 77 self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"]) 78 79 def to_string(self): 80 return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) 81 82 def display_hint(self): 83 return "string" 84 85 86class StdStrProvider(printer_base): 87 def __init__(self, valobj): 88 self._valobj = valobj 89 self._length = int(valobj["length"]) 90 self._data_ptr = valobj["data_ptr"] 91 92 def to_string(self): 93 return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) 94 95 @staticmethod 96 def display_hint(): 97 return "string" 98 99 100def _enumerate_array_elements(element_ptrs): 101 for (i, element_ptr) in enumerate(element_ptrs): 102 key = "[{}]".format(i) 103 element = element_ptr.dereference() 104 105 try: 106 # rust-lang/rust#64343: passing deref expr to `str` allows 107 # catching exception on garbage pointer 108 str(element) 109 except RuntimeError: 110 yield key, "inaccessible" 111 112 break 113 114 yield key, element 115 116 117class StdSliceProvider(printer_base): 118 def __init__(self, valobj): 119 self._valobj = valobj 120 self._length = int(valobj["length"]) 121 self._data_ptr = valobj["data_ptr"] 122 123 def to_string(self): 124 return "{}(size={})".format(self._valobj.type, self._length) 125 126 def children(self): 127 return _enumerate_array_elements( 128 self._data_ptr + index for index in xrange(self._length) 129 ) 130 131 @staticmethod 132 def display_hint(): 133 return "array" 134 135 136class StdVecProvider(printer_base): 137 def __init__(self, valobj): 138 self._valobj = valobj 139 self._length = int(valobj["len"]) 140 self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"]) 141 ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0)) 142 self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty) 143 144 def to_string(self): 145 return "Vec(size={})".format(self._length) 146 147 def children(self): 148 return _enumerate_array_elements( 149 self._data_ptr + index for index in xrange(self._length) 150 ) 151 152 @staticmethod 153 def display_hint(): 154 return "array" 155 156 157class StdVecDequeProvider(printer_base): 158 def __init__(self, valobj): 159 self._valobj = valobj 160 self._head = int(valobj["head"]) 161 self._size = int(valobj["len"]) 162 # BACKCOMPAT: rust 1.75 163 cap = valobj["buf"]["inner"]["cap"] 164 if cap.type.code != gdb.TYPE_CODE_INT: 165 cap = cap[ZERO_FIELD] 166 self._cap = int(cap) 167 self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"]) 168 ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0)) 169 self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty) 170 171 def to_string(self): 172 return "VecDeque(size={})".format(self._size) 173 174 def children(self): 175 return _enumerate_array_elements( 176 (self._data_ptr + ((self._head + index) % self._cap)) for index in xrange(self._size) 177 ) 178 179 @staticmethod 180 def display_hint(): 181 return "array" 182 183 184class StdRcProvider(printer_base): 185 def __init__(self, valobj, is_atomic=False): 186 self._valobj = valobj 187 self._is_atomic = is_atomic 188 self._ptr = unwrap_unique_or_non_null(valobj["ptr"]) 189 self._value = self._ptr["data" if is_atomic else "value"] 190 self._strong = self._ptr["strong"]["v" if is_atomic else "value"]["value"] 191 self._weak = self._ptr["weak"]["v" if is_atomic else "value"]["value"] - 1 192 193 def to_string(self): 194 if self._is_atomic: 195 return "Arc(strong={}, weak={})".format(int(self._strong), int(self._weak)) 196 else: 197 return "Rc(strong={}, weak={})".format(int(self._strong), int(self._weak)) 198 199 def children(self): 200 yield "value", self._value 201 yield "strong", self._strong 202 yield "weak", self._weak 203 204 205class StdCellProvider(printer_base): 206 def __init__(self, valobj): 207 self._value = valobj["value"]["value"] 208 209 def to_string(self): 210 return "Cell" 211 212 def children(self): 213 yield "value", self._value 214 215 216class StdRefProvider(printer_base): 217 def __init__(self, valobj): 218 self._value = valobj["value"].dereference() 219 self._borrow = valobj["borrow"]["borrow"]["value"]["value"] 220 221 def to_string(self): 222 borrow = int(self._borrow) 223 if borrow >= 0: 224 return "Ref(borrow={})".format(borrow) 225 else: 226 return "Ref(borrow_mut={})".format(-borrow) 227 228 def children(self): 229 yield "*value", self._value 230 yield "borrow", self._borrow 231 232 233class StdRefCellProvider(printer_base): 234 def __init__(self, valobj): 235 self._value = valobj["value"]["value"] 236 self._borrow = valobj["borrow"]["value"]["value"] 237 238 def to_string(self): 239 borrow = int(self._borrow) 240 if borrow >= 0: 241 return "RefCell(borrow={})".format(borrow) 242 else: 243 return "RefCell(borrow_mut={})".format(-borrow) 244 245 def children(self): 246 yield "value", self._value 247 yield "borrow", self._borrow 248 249 250class StdNonZeroNumberProvider(printer_base): 251 def __init__(self, valobj): 252 fields = valobj.type.fields() 253 assert len(fields) == 1 254 field = list(fields)[0] 255 256 inner_valobj = valobj[field.name] 257 258 inner_fields = inner_valobj.type.fields() 259 assert len(inner_fields) == 1 260 inner_field = list(inner_fields)[0] 261 262 self._value = str(inner_valobj[inner_field.name]) 263 264 def to_string(self): 265 return self._value 266 267 268# Yields children (in a provider's sense of the word) for a BTreeMap. 269def children_of_btree_map(map): 270 # Yields each key/value pair in the node and in any child nodes. 271 def children_of_node(node_ptr, height): 272 def cast_to_internal(node): 273 internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1) 274 internal_type = gdb.lookup_type(internal_type_name) 275 return node.cast(internal_type.pointer()) 276 277 if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"): 278 # BACKCOMPAT: rust 1.49 279 node_ptr = node_ptr["ptr"] 280 node_ptr = unwrap_unique_or_non_null(node_ptr) 281 leaf = node_ptr.dereference() 282 keys = leaf["keys"] 283 vals = leaf["vals"] 284 edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None 285 length = leaf["len"] 286 287 for i in xrange(0, length + 1): 288 if height > 0: 289 child_ptr = edges[i]["value"]["value"] 290 for child in children_of_node(child_ptr, height - 1): 291 yield child 292 if i < length: 293 # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays. 294 key_type_size = keys.type.sizeof 295 val_type_size = vals.type.sizeof 296 key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()") 297 val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()") 298 yield key, val 299 300 if map["length"] > 0: 301 root = map["root"] 302 if root.type.name.startswith("core::option::Option<"): 303 root = root.cast(gdb.lookup_type(root.type.name[21:-1])) 304 node_ptr = root["node"] 305 height = root["height"] 306 for child in children_of_node(node_ptr, height): 307 yield child 308 309 310class StdBTreeSetProvider(printer_base): 311 def __init__(self, valobj): 312 self._valobj = valobj 313 314 def to_string(self): 315 return "BTreeSet(size={})".format(self._valobj["map"]["length"]) 316 317 def children(self): 318 inner_map = self._valobj["map"] 319 for i, (child, _) in enumerate(children_of_btree_map(inner_map)): 320 yield "[{}]".format(i), child 321 322 @staticmethod 323 def display_hint(): 324 return "array" 325 326 327class StdBTreeMapProvider(printer_base): 328 def __init__(self, valobj): 329 self._valobj = valobj 330 331 def to_string(self): 332 return "BTreeMap(size={})".format(self._valobj["length"]) 333 334 def children(self): 335 for i, (key, val) in enumerate(children_of_btree_map(self._valobj)): 336 yield "key{}".format(i), key 337 yield "val{}".format(i), val 338 339 @staticmethod 340 def display_hint(): 341 return "map" 342 343 344# BACKCOMPAT: rust 1.35 345class StdOldHashMapProvider(printer_base): 346 def __init__(self, valobj, show_values=True): 347 self._valobj = valobj 348 self._show_values = show_values 349 350 self._table = self._valobj["table"] 351 self._size = int(self._table["size"]) 352 self._hashes = self._table["hashes"] 353 self._hash_uint_type = self._hashes.type 354 self._hash_uint_size = self._hashes.type.sizeof 355 self._modulo = 2 ** self._hash_uint_size 356 self._data_ptr = self._hashes[ZERO_FIELD]["pointer"] 357 358 self._capacity_mask = int(self._table["capacity_mask"]) 359 self._capacity = (self._capacity_mask + 1) % self._modulo 360 361 marker = self._table["marker"].type 362 self._pair_type = marker.template_argument(0) 363 self._pair_type_size = self._pair_type.sizeof 364 365 self._valid_indices = [] 366 for idx in range(self._capacity): 367 data_ptr = self._data_ptr.cast(self._hash_uint_type.pointer()) 368 address = data_ptr + idx 369 hash_uint = address.dereference() 370 hash_ptr = hash_uint[ZERO_FIELD]["pointer"] 371 if int(hash_ptr) != 0: 372 self._valid_indices.append(idx) 373 374 def to_string(self): 375 if self._show_values: 376 return "HashMap(size={})".format(self._size) 377 else: 378 return "HashSet(size={})".format(self._size) 379 380 def children(self): 381 start = int(self._data_ptr) & ~1 382 383 hashes = self._hash_uint_size * self._capacity 384 align = self._pair_type_size 385 len_rounded_up = (((((hashes + align) % self._modulo - 1) % self._modulo) & ~( 386 (align - 1) % self._modulo)) % self._modulo - hashes) % self._modulo 387 388 pairs_offset = hashes + len_rounded_up 389 pairs_start = gdb.Value(start + pairs_offset).cast(self._pair_type.pointer()) 390 391 for index in range(self._size): 392 table_index = self._valid_indices[index] 393 idx = table_index & self._capacity_mask 394 element = (pairs_start + idx).dereference() 395 if self._show_values: 396 yield "key{}".format(index), element[ZERO_FIELD] 397 yield "val{}".format(index), element[FIRST_FIELD] 398 else: 399 yield "[{}]".format(index), element[ZERO_FIELD] 400 401 def display_hint(self): 402 return "map" if self._show_values else "array" 403 404 405class StdHashMapProvider(printer_base): 406 def __init__(self, valobj, show_values=True): 407 self._valobj = valobj 408 self._show_values = show_values 409 410 table = self._table() 411 table_inner = table["table"] 412 capacity = int(table_inner["bucket_mask"]) + 1 413 ctrl = table_inner["ctrl"]["pointer"] 414 415 self._size = int(table_inner["items"]) 416 self._pair_type = table.type.template_argument(0).strip_typedefs() 417 418 self._new_layout = not table_inner.type.has_key("data") 419 if self._new_layout: 420 self._data_ptr = ctrl.cast(self._pair_type.pointer()) 421 else: 422 self._data_ptr = table_inner["data"]["pointer"] 423 424 self._valid_indices = [] 425 for idx in range(capacity): 426 address = ctrl + idx 427 value = address.dereference() 428 is_presented = value & 128 == 0 429 if is_presented: 430 self._valid_indices.append(idx) 431 432 def _table(self): 433 if self._show_values: 434 hashbrown_hashmap = self._valobj["base"] 435 elif self._valobj.type.fields()[0].name == "map": 436 # BACKCOMPAT: rust 1.47 437 # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap 438 hashbrown_hashmap = self._valobj["map"]["base"] 439 else: 440 # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap 441 hashbrown_hashmap = self._valobj["base"]["map"] 442 return hashbrown_hashmap["table"] 443 444 def to_string(self): 445 if self._show_values: 446 return "HashMap(size={})".format(self._size) 447 else: 448 return "HashSet(size={})".format(self._size) 449 450 def children(self): 451 pairs_start = self._data_ptr 452 453 for index in range(self._size): 454 idx = self._valid_indices[index] 455 if self._new_layout: 456 idx = -(idx + 1) 457 element = (pairs_start + idx).dereference() 458 if self._show_values: 459 yield "key{}".format(index), element[ZERO_FIELD] 460 yield "val{}".format(index), element[FIRST_FIELD] 461 else: 462 yield "[{}]".format(index), element[ZERO_FIELD] 463 464 def display_hint(self): 465 return "map" if self._show_values else "array" 466