from sys import version_info import gdb if version_info[0] >= 3: xrange = range ZERO_FIELD = "__0" FIRST_FIELD = "__1" def unwrap_unique_or_non_null(unique_or_nonnull): # BACKCOMPAT: rust 1.32 # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 # BACKCOMPAT: rust 1.60 # https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f ptr = unique_or_nonnull["pointer"] return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]] class EnumProvider: def __init__(self, valobj): content = valobj[valobj.type.fields()[0]] fields = content.type.fields() self.empty = len(fields) == 0 if not self.empty: if len(fields) == 1: discriminant = 0 else: discriminant = int(content[fields[0]]) + 1 self.active_variant = content[fields[discriminant]] self.name = fields[discriminant].name self.full_name = "{}::{}".format(valobj.type.name, self.name) else: self.full_name = valobj.type.name def to_string(self): return self.full_name def children(self): if not self.empty: yield self.name, self.active_variant class StdStringProvider: def __init__(self, valobj): self.valobj = valobj vec = valobj["vec"] self.length = int(vec["len"]) self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) def to_string(self): return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) @staticmethod def display_hint(): return "string" class StdOsStringProvider: def __init__(self, valobj): self.valobj = valobj buf = self.valobj["inner"]["inner"] is_windows = "Wtf8Buf" in buf.type.name vec = buf[ZERO_FIELD] if is_windows else buf self.length = int(vec["len"]) self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) def to_string(self): return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) def display_hint(self): return "string" class StdStrProvider: def __init__(self, valobj): self.valobj = valobj self.length = int(valobj["length"]) self.data_ptr = valobj["data_ptr"] def to_string(self): return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) @staticmethod def display_hint(): return "string" def _enumerate_array_elements(element_ptrs): for (i, element_ptr) in enumerate(element_ptrs): key = "[{}]".format(i) element = element_ptr.dereference() try: # rust-lang/rust#64343: passing deref expr to `str` allows # catching exception on garbage pointer str(element) except RuntimeError: yield key, "inaccessible" break yield key, element class StdSliceProvider: def __init__(self, valobj): self.valobj = valobj self.length = int(valobj["length"]) self.data_ptr = valobj["data_ptr"] def to_string(self): return "{}(size={})".format(self.valobj.type, self.length) def children(self): return _enumerate_array_elements( self.data_ptr + index for index in xrange(self.length) ) @staticmethod def display_hint(): return "array" class StdVecProvider: def __init__(self, valobj): self.valobj = valobj self.length = int(valobj["len"]) self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) def to_string(self): return "Vec(size={})".format(self.length) def children(self): return _enumerate_array_elements( self.data_ptr + index for index in xrange(self.length) ) @staticmethod def display_hint(): return "array" class StdVecDequeProvider: def __init__(self, valobj): self.valobj = valobj self.head = int(valobj["head"]) self.tail = int(valobj["tail"]) self.cap = int(valobj["buf"]["cap"]) self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) if self.head >= self.tail: self.size = self.head - self.tail else: self.size = self.cap + self.head - self.tail def to_string(self): return "VecDeque(size={})".format(self.size) def children(self): return _enumerate_array_elements( (self.data_ptr + ((self.tail + index) % self.cap)) for index in xrange(self.size) ) @staticmethod def display_hint(): return "array" class StdRcProvider: def __init__(self, valobj, is_atomic=False): self.valobj = valobj self.is_atomic = is_atomic self.ptr = unwrap_unique_or_non_null(valobj["ptr"]) self.value = self.ptr["data" if is_atomic else "value"] self.strong = self.ptr["strong"]["v" if is_atomic else "value"]["value"] self.weak = self.ptr["weak"]["v" if is_atomic else "value"]["value"] - 1 def to_string(self): if self.is_atomic: return "Arc(strong={}, weak={})".format(int(self.strong), int(self.weak)) else: return "Rc(strong={}, weak={})".format(int(self.strong), int(self.weak)) def children(self): yield "value", self.value yield "strong", self.strong yield "weak", self.weak class StdCellProvider: def __init__(self, valobj): self.value = valobj["value"]["value"] def to_string(self): return "Cell" def children(self): yield "value", self.value class StdRefProvider: def __init__(self, valobj): self.value = valobj["value"].dereference() self.borrow = valobj["borrow"]["borrow"]["value"]["value"] def to_string(self): borrow = int(self.borrow) if borrow >= 0: return "Ref(borrow={})".format(borrow) else: return "Ref(borrow_mut={})".format(-borrow) def children(self): yield "*value", self.value yield "borrow", self.borrow class StdRefCellProvider: def __init__(self, valobj): self.value = valobj["value"]["value"] self.borrow = valobj["borrow"]["value"]["value"] def to_string(self): borrow = int(self.borrow) if borrow >= 0: return "RefCell(borrow={})".format(borrow) else: return "RefCell(borrow_mut={})".format(-borrow) def children(self): yield "value", self.value yield "borrow", self.borrow # Yields children (in a provider's sense of the word) for a BTreeMap. def children_of_btree_map(map): # Yields each key/value pair in the node and in any child nodes. def children_of_node(node_ptr, height): def cast_to_internal(node): internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1) internal_type = gdb.lookup_type(internal_type_name) return node.cast(internal_type.pointer()) if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"): # BACKCOMPAT: rust 1.49 node_ptr = node_ptr["ptr"] node_ptr = unwrap_unique_or_non_null(node_ptr) leaf = node_ptr.dereference() keys = leaf["keys"] vals = leaf["vals"] edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None length = leaf["len"] for i in xrange(0, length + 1): if height > 0: child_ptr = edges[i]["value"]["value"] for child in children_of_node(child_ptr, height - 1): yield child if i < length: # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays. key_type_size = keys.type.sizeof val_type_size = vals.type.sizeof key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()") val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()") yield key, val if map["length"] > 0: root = map["root"] if root.type.name.startswith("core::option::Option<"): root = root.cast(gdb.lookup_type(root.type.name[21:-1])) node_ptr = root["node"] height = root["height"] for child in children_of_node(node_ptr, height): yield child class StdBTreeSetProvider: def __init__(self, valobj): self.valobj = valobj def to_string(self): return "BTreeSet(size={})".format(self.valobj["map"]["length"]) def children(self): inner_map = self.valobj["map"] for i, (child, _) in enumerate(children_of_btree_map(inner_map)): yield "[{}]".format(i), child @staticmethod def display_hint(): return "array" class StdBTreeMapProvider: def __init__(self, valobj): self.valobj = valobj def to_string(self): return "BTreeMap(size={})".format(self.valobj["length"]) def children(self): for i, (key, val) in enumerate(children_of_btree_map(self.valobj)): yield "key{}".format(i), key yield "val{}".format(i), val @staticmethod def display_hint(): return "map" # BACKCOMPAT: rust 1.35 class StdOldHashMapProvider: def __init__(self, valobj, show_values=True): self.valobj = valobj self.show_values = show_values self.table = self.valobj["table"] self.size = int(self.table["size"]) self.hashes = self.table["hashes"] self.hash_uint_type = self.hashes.type self.hash_uint_size = self.hashes.type.sizeof self.modulo = 2 ** self.hash_uint_size self.data_ptr = self.hashes[ZERO_FIELD]["pointer"] self.capacity_mask = int(self.table["capacity_mask"]) self.capacity = (self.capacity_mask + 1) % self.modulo marker = self.table["marker"].type self.pair_type = marker.template_argument(0) self.pair_type_size = self.pair_type.sizeof self.valid_indices = [] for idx in range(self.capacity): data_ptr = self.data_ptr.cast(self.hash_uint_type.pointer()) address = data_ptr + idx hash_uint = address.dereference() hash_ptr = hash_uint[ZERO_FIELD]["pointer"] if int(hash_ptr) != 0: self.valid_indices.append(idx) def to_string(self): if self.show_values: return "HashMap(size={})".format(self.size) else: return "HashSet(size={})".format(self.size) def children(self): start = int(self.data_ptr) & ~1 hashes = self.hash_uint_size * self.capacity align = self.pair_type_size len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~( (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo pairs_offset = hashes + len_rounded_up pairs_start = gdb.Value(start + pairs_offset).cast(self.pair_type.pointer()) for index in range(self.size): table_index = self.valid_indices[index] idx = table_index & self.capacity_mask element = (pairs_start + idx).dereference() if self.show_values: yield "key{}".format(index), element[ZERO_FIELD] yield "val{}".format(index), element[FIRST_FIELD] else: yield "[{}]".format(index), element[ZERO_FIELD] def display_hint(self): return "map" if self.show_values else "array" class StdHashMapProvider: def __init__(self, valobj, show_values=True): self.valobj = valobj self.show_values = show_values table = self.table() table_inner = table["table"] capacity = int(table_inner["bucket_mask"]) + 1 ctrl = table_inner["ctrl"]["pointer"] self.size = int(table_inner["items"]) self.pair_type = table.type.template_argument(0).strip_typedefs() self.new_layout = not table_inner.type.has_key("data") if self.new_layout: self.data_ptr = ctrl.cast(self.pair_type.pointer()) else: self.data_ptr = table_inner["data"]["pointer"] self.valid_indices = [] for idx in range(capacity): address = ctrl + idx value = address.dereference() is_presented = value & 128 == 0 if is_presented: self.valid_indices.append(idx) def table(self): if self.show_values: hashbrown_hashmap = self.valobj["base"] elif self.valobj.type.fields()[0].name == "map": # BACKCOMPAT: rust 1.47 # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap hashbrown_hashmap = self.valobj["map"]["base"] else: # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap hashbrown_hashmap = self.valobj["base"]["map"] return hashbrown_hashmap["table"] def to_string(self): if self.show_values: return "HashMap(size={})".format(self.size) else: return "HashSet(size={})".format(self.size) def children(self): pairs_start = self.data_ptr for index in range(self.size): idx = self.valid_indices[index] if self.new_layout: idx = -(idx + 1) element = (pairs_start + idx).dereference() if self.show_values: yield "key{}".format(index), element[ZERO_FIELD] yield "val{}".format(index), element[FIRST_FIELD] else: yield "[{}]".format(index), element[ZERO_FIELD] def display_hint(self): return "map" if self.show_values else "array"