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