| 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]] | |
| def unwrap_scalar_wrappers(wrapper): | |
| while not wrapper.type.is_scalar: | |
| wrapper = wrapper[wrapper.type.fields()[0]] | |
| return wrapper | |
| # GDB 14 has a tag class that indicates that extension methods are ok | |
| # to call. Use of this tag only requires that printers hide local | |
| # attributes and methods by prefixing them with "_". | |
| if hasattr(gdb, "ValuePrinter"): | |
| printer_base = gdb.ValuePrinter | |
| else: | |
| printer_base = object | |
| class EnumProvider(printer_base): | |
| 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(printer_base): | |
| 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"]["inner"]["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(printer_base): | |
| def __init__(self, valobj): | |
| self._valobj = valobj | |
| buf = self._valobj["inner"]["inner"] | |
| is_windows = "Wtf8Buf" in buf.type.name | |
| vec = buf["bytes"] if is_windows else buf | |
| self._length = int(vec["len"]) | |
| self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["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(printer_base): | |
| 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(printer_base): | |
| 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) | |
| ) | |
| def num_children(self): | |
| return self._length | |
| @staticmethod | |
| def display_hint(): | |
| return "array" | |
| class StdBoxStrProvider(printer_base): | |
| 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" | |
| class StdVecProvider(printer_base): | |
| def __init__(self, valobj): | |
| self._valobj = valobj | |
| self._length = int(valobj["len"]) | |
| self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"]) | |
| ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0)) | |
| self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty) | |
| 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) | |
| ) | |
| def num_children(self): | |
| return self._length | |
| @staticmethod | |
| def display_hint(): | |
| return "array" | |
| class StdVecDequeProvider(printer_base): | |
| def __init__(self, valobj): | |
| self._valobj = valobj | |
| head = valobj["head"] | |
| # BACKCOMPAT: rust 1.95 | |
| if head.type.code != gdb.TYPE_CODE_INT: | |
| head = head[ZERO_FIELD] | |
| self._head = int(head) | |
| self._size = int(valobj["len"]) | |
| # BACKCOMPAT: rust 1.75 | |
| cap = valobj["buf"]["inner"]["cap"] | |
| if cap.type.code != gdb.TYPE_CODE_INT: | |
| cap = cap[ZERO_FIELD] | |
| self._cap = int(cap) | |
| self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"]) | |
| ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0)) | |
| self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty) | |
| def to_string(self): | |
| return "VecDeque(size={})".format(self._size) | |
| def children(self): | |
| return _enumerate_array_elements( | |
| (self._data_ptr + ((self._head + index) % self._cap)) | |
| for index in xrange(self._size) | |
| ) | |
| def num_children(self): | |
| return self._size | |
| @staticmethod | |
| def display_hint(): | |
| return "array" | |
| class StdRcProvider(printer_base): | |
| 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"] | |
| # FIXME(shua): the debuginfo template type should be 'str' not 'u8' | |
| if self._ptr.type.target().name == "alloc::rc::RcInner<str>": | |
| length = self._valobj["ptr"]["pointer"]["length"] | |
| u8_ptr_ty = gdb.Type.pointer(gdb.lookup_type("u8")) | |
| ptr = self._value.address.reinterpret_cast(u8_ptr_ty) | |
| self._value = ptr.lazy_string(encoding="utf-8", length=length) | |
| self._strong = unwrap_scalar_wrappers(self._ptr["strong"]) | |
| self._weak = unwrap_scalar_wrappers(self._ptr["weak"]) - 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(printer_base): | |
| def __init__(self, valobj): | |
| self._value = valobj["value"]["value"] | |
| def to_string(self): | |
| return "Cell" | |
| def children(self): | |
| yield "value", self._value | |
| class StdRefProvider(printer_base): | |
| 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(printer_base): | |
| 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 | |
| class StdNonZeroNumberProvider(printer_base): | |
| def __init__(self, valobj): | |
| fields = valobj.type.fields() | |
| assert len(fields) == 1 | |
| field = fields[0] | |
| inner_valobj = valobj[field] | |
| inner_fields = inner_valobj.type.fields() | |
| assert len(inner_fields) == 1 | |
| inner_field = inner_fields[0] | |
| self._value = inner_valobj[inner_field] | |
| def to_string(self): | |
| return self._value | |
| # 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(printer_base): | |
| 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(printer_base): | |
| 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(printer_base): | |
| 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(printer_base): | |
| 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 num_children(self): | |
| result = self._size | |
| if self._show_values: | |
| result *= 2 | |
| return result | |
| def display_hint(self): | |
| return "map" if self._show_values else "array" |