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"]["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"]["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 99def _enumerate_array_elements(element_ptrs): 100 for (i, element_ptr) in enumerate(element_ptrs): 101 key = "[{}]".format(i) 102 element = element_ptr.dereference() 103 104 try: 105 # rust-lang/rust#64343: passing deref expr to `str` allows 106 # catching exception on garbage pointer 107 str(element) 108 except RuntimeError: 109 yield key, "inaccessible" 110 111 break 112 113 yield key, element 114 115class StdSliceProvider(printer_base): 116 def __init__(self, valobj): 117 self._valobj = valobj 118 self._length = int(valobj["length"]) 119 self._data_ptr = valobj["data_ptr"] 120 121 def to_string(self): 122 return "{}(size={})".format(self._valobj.type, self._length) 123 124 def children(self): 125 return _enumerate_array_elements( 126 self._data_ptr + index for index in xrange(self._length) 127 ) 128 129 @staticmethod 130 def display_hint(): 131 return "array" 132 133class StdVecProvider(printer_base): 134 def __init__(self, valobj): 135 self._valobj = valobj 136 self._length = int(valobj["len"]) 137 self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) 138 139 def to_string(self): 140 return "Vec(size={})".format(self._length) 141 142 def children(self): 143 return _enumerate_array_elements( 144 self._data_ptr + index for index in xrange(self._length) 145 ) 146 147 @staticmethod 148 def display_hint(): 149 return "array" 150 151 152class StdVecDequeProvider(printer_base): 153 def __init__(self, valobj): 154 self._valobj = valobj 155 self._head = int(valobj["head"]) 156 self._size = int(valobj["len"]) 157 # BACKCOMPAT: rust 1.75 158 cap = valobj["buf"]["cap"] 159 if cap.type.code != gdb.TYPE_CODE_INT: 160 cap = cap[ZERO_FIELD] 161 self._cap = int(cap) 162 self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) 163 164 def to_string(self): 165 return "VecDeque(size={})".format(self._size) 166 167 def children(self): 168 return _enumerate_array_elements( 169 (self._data_ptr + ((self._head + index) % self._cap)) for index in xrange(self._size) 170 ) 171 172 @staticmethod 173 def display_hint(): 174 return "array" 175 176 177class StdRcProvider(printer_base): 178 def __init__(self, valobj, is_atomic=False): 179 self._valobj = valobj 180 self._is_atomic = is_atomic 181 self._ptr = unwrap_unique_or_non_null(valobj["ptr"]) 182 self._value = self._ptr["data" if is_atomic else "value"] 183 self._strong = self._ptr["strong"]["v" if is_atomic else "value"]["value"] 184 self._weak = self._ptr["weak"]["v" if is_atomic else "value"]["value"] - 1 185 186 def to_string(self): 187 if self._is_atomic: 188 return "Arc(strong={}, weak={})".format(int(self._strong), int(self._weak)) 189 else: 190 return "Rc(strong={}, weak={})".format(int(self._strong), int(self._weak)) 191 192 def children(self): 193 yield "value", self._value 194 yield "strong", self._strong 195 yield "weak", self._weak 196 197 198class StdCellProvider(printer_base): 199 def __init__(self, valobj): 200 self._value = valobj["value"]["value"] 201 202 def to_string(self): 203 return "Cell" 204 205 def children(self): 206 yield "value", self._value 207 208 209class StdRefProvider(printer_base): 210 def __init__(self, valobj): 211 self._value = valobj["value"].dereference() 212 self._borrow = valobj["borrow"]["borrow"]["value"]["value"] 213 214 def to_string(self): 215 borrow = int(self._borrow) 216 if borrow >= 0: 217 return "Ref(borrow={})".format(borrow) 218 else: 219 return "Ref(borrow_mut={})".format(-borrow) 220 221 def children(self): 222 yield "*value", self._value 223 yield "borrow", self._borrow 224 225 226class StdRefCellProvider(printer_base): 227 def __init__(self, valobj): 228 self._value = valobj["value"]["value"] 229 self._borrow = valobj["borrow"]["value"]["value"] 230 231 def to_string(self): 232 borrow = int(self._borrow) 233 if borrow >= 0: 234 return "RefCell(borrow={})".format(borrow) 235 else: 236 return "RefCell(borrow_mut={})".format(-borrow) 237 238 def children(self): 239 yield "value", self._value 240 yield "borrow", self._borrow 241 242 243class StdNonZeroNumberProvider(printer_base): 244 def __init__(self, valobj): 245 fields = valobj.type.fields() 246 assert len(fields) == 1 247 field = list(fields)[0] 248 249 inner_valobj = valobj[field.name] 250 251 inner_fields = inner_valobj.type.fields() 252 assert len(inner_fields) == 1 253 inner_field = list(inner_fields)[0] 254 255 self._value = str(inner_valobj[inner_field.name]) 256 257 def to_string(self): 258 return self._value 259 260 261# Yields children (in a provider's sense of the word) for a BTreeMap. 262def children_of_btree_map(map): 263 # Yields each key/value pair in the node and in any child nodes. 264 def children_of_node(node_ptr, height): 265 def cast_to_internal(node): 266 internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1) 267 internal_type = gdb.lookup_type(internal_type_name) 268 return node.cast(internal_type.pointer()) 269 270 if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"): 271 # BACKCOMPAT: rust 1.49 272 node_ptr = node_ptr["ptr"] 273 node_ptr = unwrap_unique_or_non_null(node_ptr) 274 leaf = node_ptr.dereference() 275 keys = leaf["keys"] 276 vals = leaf["vals"] 277 edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None 278 length = leaf["len"] 279 280 for i in xrange(0, length + 1): 281 if height > 0: 282 child_ptr = edges[i]["value"]["value"] 283 for child in children_of_node(child_ptr, height - 1): 284 yield child 285 if i < length: 286 # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays. 287 key_type_size = keys.type.sizeof 288 val_type_size = vals.type.sizeof 289 key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()") 290 val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()") 291 yield key, val 292 293 if map["length"] > 0: 294 root = map["root"] 295 if root.type.name.startswith("core::option::Option<"): 296 root = root.cast(gdb.lookup_type(root.type.name[21:-1])) 297 node_ptr = root["node"] 298 height = root["height"] 299 for child in children_of_node(node_ptr, height): 300 yield child 301 302 303class StdBTreeSetProvider(printer_base): 304 def __init__(self, valobj): 305 self._valobj = valobj 306 307 def to_string(self): 308 return "BTreeSet(size={})".format(self._valobj["map"]["length"]) 309 310 def children(self): 311 inner_map = self._valobj["map"] 312 for i, (child, _) in enumerate(children_of_btree_map(inner_map)): 313 yield "[{}]".format(i), child 314 315 @staticmethod 316 def display_hint(): 317 return "array" 318 319 320class StdBTreeMapProvider(printer_base): 321 def __init__(self, valobj): 322 self._valobj = valobj 323 324 def to_string(self): 325 return "BTreeMap(size={})".format(self._valobj["length"]) 326 327 def children(self): 328 for i, (key, val) in enumerate(children_of_btree_map(self._valobj)): 329 yield "key{}".format(i), key 330 yield "val{}".format(i), val 331 332 @staticmethod 333 def display_hint(): 334 return "map" 335 336 337# BACKCOMPAT: rust 1.35 338class StdOldHashMapProvider(printer_base): 339 def __init__(self, valobj, show_values=True): 340 self._valobj = valobj 341 self._show_values = show_values 342 343 self._table = self._valobj["table"] 344 self._size = int(self._table["size"]) 345 self._hashes = self._table["hashes"] 346 self._hash_uint_type = self._hashes.type 347 self._hash_uint_size = self._hashes.type.sizeof 348 self._modulo = 2 ** self._hash_uint_size 349 self._data_ptr = self._hashes[ZERO_FIELD]["pointer"] 350 351 self._capacity_mask = int(self._table["capacity_mask"]) 352 self._capacity = (self._capacity_mask + 1) % self._modulo 353 354 marker = self._table["marker"].type 355 self._pair_type = marker.template_argument(0) 356 self._pair_type_size = self._pair_type.sizeof 357 358 self._valid_indices = [] 359 for idx in range(self._capacity): 360 data_ptr = self._data_ptr.cast(self._hash_uint_type.pointer()) 361 address = data_ptr + idx 362 hash_uint = address.dereference() 363 hash_ptr = hash_uint[ZERO_FIELD]["pointer"] 364 if int(hash_ptr) != 0: 365 self._valid_indices.append(idx) 366 367 def to_string(self): 368 if self._show_values: 369 return "HashMap(size={})".format(self._size) 370 else: 371 return "HashSet(size={})".format(self._size) 372 373 def children(self): 374 start = int(self._data_ptr) & ~1 375 376 hashes = self._hash_uint_size * self._capacity 377 align = self._pair_type_size 378 len_rounded_up = (((((hashes + align) % self._modulo - 1) % self._modulo) & ~( 379 (align - 1) % self._modulo)) % self._modulo - hashes) % self._modulo 380 381 pairs_offset = hashes + len_rounded_up 382 pairs_start = gdb.Value(start + pairs_offset).cast(self._pair_type.pointer()) 383 384 for index in range(self._size): 385 table_index = self._valid_indices[index] 386 idx = table_index & self._capacity_mask 387 element = (pairs_start + idx).dereference() 388 if self._show_values: 389 yield "key{}".format(index), element[ZERO_FIELD] 390 yield "val{}".format(index), element[FIRST_FIELD] 391 else: 392 yield "[{}]".format(index), element[ZERO_FIELD] 393 394 def display_hint(self): 395 return "map" if self._show_values else "array" 396 397 398class StdHashMapProvider(printer_base): 399 def __init__(self, valobj, show_values=True): 400 self._valobj = valobj 401 self._show_values = show_values 402 403 table = self._table() 404 table_inner = table["table"] 405 capacity = int(table_inner["bucket_mask"]) + 1 406 ctrl = table_inner["ctrl"]["pointer"] 407 408 self._size = int(table_inner["items"]) 409 self._pair_type = table.type.template_argument(0).strip_typedefs() 410 411 self._new_layout = not table_inner.type.has_key("data") 412 if self._new_layout: 413 self._data_ptr = ctrl.cast(self._pair_type.pointer()) 414 else: 415 self._data_ptr = table_inner["data"]["pointer"] 416 417 self._valid_indices = [] 418 for idx in range(capacity): 419 address = ctrl + idx 420 value = address.dereference() 421 is_presented = value & 128 == 0 422 if is_presented: 423 self._valid_indices.append(idx) 424 425 def _table(self): 426 if self._show_values: 427 hashbrown_hashmap = self._valobj["base"] 428 elif self._valobj.type.fields()[0].name == "map": 429 # BACKCOMPAT: rust 1.47 430 # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap 431 hashbrown_hashmap = self._valobj["map"]["base"] 432 else: 433 # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap 434 hashbrown_hashmap = self._valobj["base"]["map"] 435 return hashbrown_hashmap["table"] 436 437 def to_string(self): 438 if self._show_values: 439 return "HashMap(size={})".format(self._size) 440 else: 441 return "HashSet(size={})".format(self._size) 442 443 def children(self): 444 pairs_start = self._data_ptr 445 446 for index in range(self._size): 447 idx = self._valid_indices[index] 448 if self._new_layout: 449 idx = -(idx + 1) 450 element = (pairs_start + idx).dereference() 451 if self._show_values: 452 yield "key{}".format(index), element[ZERO_FIELD] 453 yield "val{}".format(index), element[FIRST_FIELD] 454 else: 455 yield "[{}]".format(index), element[ZERO_FIELD] 456 457 def display_hint(self): 458 return "map" if self._show_values else "array" 459