Add tools
This commit is contained in:
@ -0,0 +1,15 @@
|
||||
# Add this folder to the python sys path; GDB Python-interpreter will now find modules in this path
|
||||
import sys
|
||||
from os import path
|
||||
self_dir = path.dirname(path.realpath(__file__))
|
||||
sys.path.append(self_dir)
|
||||
|
||||
# ruff: noqa: E402
|
||||
import gdb
|
||||
import gdb_lookup
|
||||
|
||||
# current_objfile can be none; even with `gdb foo-app`; sourcing this file after gdb init now works
|
||||
try:
|
||||
gdb_lookup.register_printers(gdb.current_objfile())
|
||||
except Exception:
|
||||
gdb_lookup.register_printers(gdb.selected_inferior().progspace)
|
@ -0,0 +1,118 @@
|
||||
import gdb
|
||||
import gdb.printing
|
||||
import re
|
||||
|
||||
from gdb_providers import *
|
||||
from rust_types import *
|
||||
|
||||
|
||||
_gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION)
|
||||
gdb_version = [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else []
|
||||
|
||||
def register_printers(objfile):
|
||||
objfile.pretty_printers.append(printer)
|
||||
|
||||
|
||||
# BACKCOMPAT: rust 1.35
|
||||
def is_hashbrown_hashmap(hash_map):
|
||||
return len(hash_map.type.fields()) == 1
|
||||
|
||||
|
||||
def classify_rust_type(type):
|
||||
type_class = type.code
|
||||
if type_class == gdb.TYPE_CODE_STRUCT:
|
||||
return classify_struct(type.tag, type.fields())
|
||||
if type_class == gdb.TYPE_CODE_UNION:
|
||||
return classify_union(type.fields())
|
||||
|
||||
return RustType.OTHER
|
||||
|
||||
|
||||
def check_enum_discriminant(valobj):
|
||||
content = valobj[valobj.type.fields()[0]]
|
||||
fields = content.type.fields()
|
||||
if len(fields) > 1:
|
||||
discriminant = int(content[fields[0]]) + 1
|
||||
if discriminant > len(fields):
|
||||
# invalid discriminant
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Helper for enum printing that checks the discriminant. Only used in
|
||||
# older gdb.
|
||||
def enum_provider(valobj):
|
||||
if check_enum_discriminant(valobj):
|
||||
return EnumProvider(valobj)
|
||||
return None
|
||||
|
||||
|
||||
# Helper to handle both old and new hash maps.
|
||||
def hashmap_provider(valobj):
|
||||
if is_hashbrown_hashmap(valobj):
|
||||
return StdHashMapProvider(valobj)
|
||||
else:
|
||||
return StdOldHashMapProvider(valobj)
|
||||
|
||||
|
||||
# Helper to handle both old and new hash sets.
|
||||
def hashset_provider(valobj):
|
||||
hash_map = valobj[valobj.type.fields()[0]]
|
||||
if is_hashbrown_hashmap(hash_map):
|
||||
return StdHashMapProvider(valobj, show_values=False)
|
||||
else:
|
||||
return StdOldHashMapProvider(hash_map, show_values=False)
|
||||
|
||||
|
||||
class PrintByRustType(gdb.printing.SubPrettyPrinter):
|
||||
def __init__(self, name, provider):
|
||||
super(PrintByRustType, self).__init__(name)
|
||||
self.provider = provider
|
||||
|
||||
def __call__(self, val):
|
||||
if self.enabled:
|
||||
return self.provider(val)
|
||||
return None
|
||||
|
||||
|
||||
class RustPrettyPrinter(gdb.printing.PrettyPrinter):
|
||||
def __init__(self, name):
|
||||
super(RustPrettyPrinter, self).__init__(name, [])
|
||||
self.type_map = {}
|
||||
|
||||
def add(self, rust_type, provider):
|
||||
# Just use the rust_type as the name.
|
||||
printer = PrintByRustType(rust_type, provider)
|
||||
self.type_map[rust_type] = printer
|
||||
self.subprinters.append(printer)
|
||||
|
||||
def __call__(self, valobj):
|
||||
rust_type = classify_rust_type(valobj.type)
|
||||
if rust_type in self.type_map:
|
||||
return self.type_map[rust_type](valobj)
|
||||
return None
|
||||
|
||||
|
||||
printer = RustPrettyPrinter("rust")
|
||||
# use enum provider only for GDB <7.12
|
||||
if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12):
|
||||
printer.add(RustType.ENUM, enum_provider)
|
||||
printer.add(RustType.STD_STRING, StdStringProvider)
|
||||
printer.add(RustType.STD_OS_STRING, StdOsStringProvider)
|
||||
printer.add(RustType.STD_STR, StdStrProvider)
|
||||
printer.add(RustType.STD_SLICE, StdSliceProvider)
|
||||
printer.add(RustType.STD_VEC, StdVecProvider)
|
||||
printer.add(RustType.STD_VEC_DEQUE, StdVecDequeProvider)
|
||||
printer.add(RustType.STD_BTREE_SET, StdBTreeSetProvider)
|
||||
printer.add(RustType.STD_BTREE_MAP, StdBTreeMapProvider)
|
||||
printer.add(RustType.STD_HASH_MAP, hashmap_provider)
|
||||
printer.add(RustType.STD_HASH_SET, hashset_provider)
|
||||
printer.add(RustType.STD_RC, StdRcProvider)
|
||||
printer.add(RustType.STD_ARC, lambda valobj: StdRcProvider(valobj, is_atomic=True))
|
||||
|
||||
printer.add(RustType.STD_CELL, StdCellProvider)
|
||||
printer.add(RustType.STD_REF, StdRefProvider)
|
||||
printer.add(RustType.STD_REF_MUT, StdRefProvider)
|
||||
printer.add(RustType.STD_REF_CELL, StdRefCellProvider)
|
||||
|
||||
printer.add(RustType.STD_NONZERO_NUMBER, StdNonZeroNumberProvider)
|
@ -0,0 +1,458 @@
|
||||
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]]
|
||||
|
||||
|
||||
# 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"]["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[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(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)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def display_hint():
|
||||
return "array"
|
||||
|
||||
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"]["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(printer_base):
|
||||
def __init__(self, valobj):
|
||||
self._valobj = valobj
|
||||
self._head = int(valobj["head"])
|
||||
self._size = int(valobj["len"])
|
||||
# BACKCOMPAT: rust 1.75
|
||||
cap = valobj["buf"]["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"]["ptr"])
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
@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"]
|
||||
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(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 = list(fields)[0]
|
||||
|
||||
inner_valobj = valobj[field.name]
|
||||
|
||||
inner_fields = inner_valobj.type.fields()
|
||||
assert len(inner_fields) == 1
|
||||
inner_field = list(inner_fields)[0]
|
||||
|
||||
self._value = str(inner_valobj[inner_field.name])
|
||||
|
||||
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 display_hint(self):
|
||||
return "map" if self._show_values else "array"
|
@ -0,0 +1,22 @@
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x ".*" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?str$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?\\[.+\\]$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
|
||||
type category enable Rust
|
@ -0,0 +1,124 @@
|
||||
import lldb
|
||||
|
||||
from lldb_providers import *
|
||||
from rust_types import RustType, classify_struct, classify_union
|
||||
|
||||
|
||||
# BACKCOMPAT: rust 1.35
|
||||
def is_hashbrown_hashmap(hash_map):
|
||||
return len(hash_map.type.fields) == 1
|
||||
|
||||
|
||||
def classify_rust_type(type):
|
||||
type_class = type.GetTypeClass()
|
||||
if type_class == lldb.eTypeClassStruct:
|
||||
return classify_struct(type.name, type.fields)
|
||||
if type_class == lldb.eTypeClassUnion:
|
||||
return classify_union(type.fields)
|
||||
|
||||
return RustType.OTHER
|
||||
|
||||
|
||||
def summary_lookup(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
"""Returns the summary provider for the given value"""
|
||||
rust_type = classify_rust_type(valobj.GetType())
|
||||
|
||||
if rust_type == RustType.STD_STRING:
|
||||
return StdStringSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_OS_STRING:
|
||||
return StdOsStringSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_STR:
|
||||
return StdStrSummaryProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_VEC:
|
||||
return SizeSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_VEC_DEQUE:
|
||||
return SizeSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_SLICE:
|
||||
return SizeSummaryProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_HASH_MAP:
|
||||
return SizeSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_HASH_SET:
|
||||
return SizeSummaryProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_RC:
|
||||
return StdRcSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_ARC:
|
||||
return StdRcSummaryProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_REF:
|
||||
return StdRefSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_REF_MUT:
|
||||
return StdRefSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_REF_CELL:
|
||||
return StdRefSummaryProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_NONZERO_NUMBER:
|
||||
return StdNonZeroNumberSummaryProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_PATHBUF:
|
||||
return StdPathBufSummaryProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_PATH:
|
||||
return StdPathSummaryProvider(valobj, dict)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def synthetic_lookup(valobj, dict):
|
||||
# type: (SBValue, dict) -> object
|
||||
"""Returns the synthetic provider for the given value"""
|
||||
rust_type = classify_rust_type(valobj.GetType())
|
||||
|
||||
if rust_type == RustType.STRUCT:
|
||||
return StructSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STRUCT_VARIANT:
|
||||
return StructSyntheticProvider(valobj, dict, is_variant=True)
|
||||
if rust_type == RustType.TUPLE:
|
||||
return TupleSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.TUPLE_VARIANT:
|
||||
return TupleSyntheticProvider(valobj, dict, is_variant=True)
|
||||
if rust_type == RustType.EMPTY:
|
||||
return EmptySyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.REGULAR_ENUM:
|
||||
discriminant = valobj.GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned()
|
||||
return synthetic_lookup(valobj.GetChildAtIndex(discriminant), dict)
|
||||
if rust_type == RustType.SINGLETON_ENUM:
|
||||
return synthetic_lookup(valobj.GetChildAtIndex(0), dict)
|
||||
if rust_type == RustType.ENUM:
|
||||
return ClangEncodedEnumProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_VEC:
|
||||
return StdVecSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_VEC_DEQUE:
|
||||
return StdVecDequeSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_SLICE:
|
||||
return StdSliceSyntheticProvider(valobj, dict)
|
||||
|
||||
if rust_type == RustType.STD_HASH_MAP:
|
||||
if is_hashbrown_hashmap(valobj):
|
||||
return StdHashMapSyntheticProvider(valobj, dict)
|
||||
else:
|
||||
return StdOldHashMapSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_HASH_SET:
|
||||
hash_map = valobj.GetChildAtIndex(0)
|
||||
if is_hashbrown_hashmap(hash_map):
|
||||
return StdHashMapSyntheticProvider(valobj, dict, show_values=False)
|
||||
else:
|
||||
return StdOldHashMapSyntheticProvider(hash_map, dict, show_values=False)
|
||||
|
||||
if rust_type == RustType.STD_RC:
|
||||
return StdRcSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_ARC:
|
||||
return StdRcSyntheticProvider(valobj, dict, is_atomic=True)
|
||||
|
||||
if rust_type == RustType.STD_CELL:
|
||||
return StdCellSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_REF:
|
||||
return StdRefSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_REF_MUT:
|
||||
return StdRefSyntheticProvider(valobj, dict)
|
||||
if rust_type == RustType.STD_REF_CELL:
|
||||
return StdRefSyntheticProvider(valobj, dict, is_cell=True)
|
||||
|
||||
return DefaultSyntheticProvider(valobj, dict)
|
@ -0,0 +1,835 @@
|
||||
import sys
|
||||
|
||||
from lldb import SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \
|
||||
eBasicTypeUnsignedChar
|
||||
|
||||
# from lldb.formatters import Logger
|
||||
|
||||
####################################################################################################
|
||||
# This file contains two kinds of pretty-printers: summary and synthetic.
|
||||
#
|
||||
# Important classes from LLDB module:
|
||||
# SBValue: the value of a variable, a register, or an expression
|
||||
# SBType: the data type; each SBValue has a corresponding SBType
|
||||
#
|
||||
# Summary provider is a function with the type `(SBValue, dict) -> str`.
|
||||
# The first parameter is the object encapsulating the actual variable being displayed;
|
||||
# The second parameter is an internal support parameter used by LLDB, and you should not touch it.
|
||||
#
|
||||
# Synthetic children is the way to provide a children-based representation of the object's value.
|
||||
# Synthetic provider is a class that implements the following interface:
|
||||
#
|
||||
# class SyntheticChildrenProvider:
|
||||
# def __init__(self, SBValue, dict)
|
||||
# def num_children(self)
|
||||
# def get_child_index(self, str)
|
||||
# def get_child_at_index(self, int)
|
||||
# def update(self)
|
||||
# def has_children(self)
|
||||
# def get_value(self)
|
||||
#
|
||||
#
|
||||
# You can find more information and examples here:
|
||||
# 1. https://lldb.llvm.org/varformats.html
|
||||
# 2. https://lldb.llvm.org/use/python-reference.html
|
||||
# 3. https://lldb.llvm.org/python_reference/lldb.formatters.cpp.libcxx-pysrc.html
|
||||
# 4. https://github.com/llvm-mirror/lldb/tree/master/examples/summaries/cocoa
|
||||
####################################################################################################
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
|
||||
class ValueBuilder:
|
||||
def __init__(self, valobj):
|
||||
# type: (SBValue) -> ValueBuilder
|
||||
self.valobj = valobj
|
||||
process = valobj.GetProcess()
|
||||
self.endianness = process.GetByteOrder()
|
||||
self.pointer_size = process.GetAddressByteSize()
|
||||
|
||||
def from_int(self, name, value):
|
||||
# type: (str, int) -> SBValue
|
||||
type = self.valobj.GetType().GetBasicType(eBasicTypeLong)
|
||||
data = SBData.CreateDataFromSInt64Array(self.endianness, self.pointer_size, [value])
|
||||
return self.valobj.CreateValueFromData(name, data, type)
|
||||
|
||||
def from_uint(self, name, value):
|
||||
# type: (str, int) -> SBValue
|
||||
type = self.valobj.GetType().GetBasicType(eBasicTypeUnsignedLong)
|
||||
data = SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [value])
|
||||
return self.valobj.CreateValueFromData(name, data, type)
|
||||
|
||||
|
||||
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.GetChildMemberWithName("pointer")
|
||||
return ptr if ptr.TypeIsPointerType() else ptr.GetChildAtIndex(0)
|
||||
|
||||
|
||||
class DefaultSyntheticProvider:
|
||||
def __init__(self, valobj, dict):
|
||||
# type: (SBValue, dict) -> DefaultSyntheticProvider
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "Default synthetic provider for " + str(valobj.GetName())
|
||||
self.valobj = valobj
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.valobj.GetNumChildren()
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
return self.valobj.GetIndexOfChildWithName(name)
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
return self.valobj.GetChildAtIndex(index)
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
pass
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return self.valobj.MightHaveChildren()
|
||||
|
||||
|
||||
class EmptySyntheticProvider:
|
||||
def __init__(self, valobj, dict):
|
||||
# type: (SBValue, dict) -> EmptySyntheticProvider
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[EmptySyntheticProvider] for " + str(valobj.GetName())
|
||||
self.valobj = valobj
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return 0
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
return None
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
pass
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return False
|
||||
|
||||
|
||||
def SizeSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
return 'size=' + str(valobj.GetNumChildren())
|
||||
|
||||
|
||||
def vec_to_string(vec):
|
||||
length = vec.GetNumChildren()
|
||||
chars = [vec.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(length)]
|
||||
return bytes(chars).decode(errors='replace') if PY3 else "".join(chr(char) for char in chars)
|
||||
|
||||
|
||||
def StdStringSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdStringSummaryProvider] for " + str(valobj.GetName())
|
||||
vec = valobj.GetChildAtIndex(0)
|
||||
return '"%s"' % vec_to_string(vec)
|
||||
|
||||
|
||||
def StdOsStringSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdOsStringSummaryProvider] for " + str(valobj.GetName())
|
||||
buf = valobj.GetChildAtIndex(0).GetChildAtIndex(0)
|
||||
is_windows = "Wtf8Buf" in buf.type.name
|
||||
vec = buf.GetChildAtIndex(0) if is_windows else buf
|
||||
return '"%s"' % vec_to_string(vec)
|
||||
|
||||
|
||||
def StdStrSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdStrSummaryProvider] for " + str(valobj.GetName())
|
||||
|
||||
length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
|
||||
if length == 0:
|
||||
return '""'
|
||||
|
||||
data_ptr = valobj.GetChildMemberWithName("data_ptr")
|
||||
|
||||
start = data_ptr.GetValueAsUnsigned()
|
||||
error = SBError()
|
||||
process = data_ptr.GetProcess()
|
||||
data = process.ReadMemory(start, length, error)
|
||||
data = data.decode(encoding='UTF-8') if PY3 else data
|
||||
return '"%s"' % data
|
||||
|
||||
|
||||
def StdPathBufSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdPathBufSummaryProvider] for " + str(valobj.GetName())
|
||||
return StdOsStringSummaryProvider(valobj.GetChildMemberWithName("inner"), dict)
|
||||
|
||||
|
||||
def StdPathSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdPathSummaryProvider] for " + str(valobj.GetName())
|
||||
length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
|
||||
if length == 0:
|
||||
return '""'
|
||||
|
||||
data_ptr = valobj.GetChildMemberWithName("data_ptr")
|
||||
|
||||
start = data_ptr.GetValueAsUnsigned()
|
||||
error = SBError()
|
||||
process = data_ptr.GetProcess()
|
||||
data = process.ReadMemory(start, length, error)
|
||||
if PY3:
|
||||
try:
|
||||
data = data.decode(encoding='UTF-8')
|
||||
except UnicodeDecodeError:
|
||||
return '%r' % data
|
||||
return '"%s"' % data
|
||||
|
||||
|
||||
class StructSyntheticProvider:
|
||||
"""Pretty-printer for structs and struct enum variants"""
|
||||
|
||||
def __init__(self, valobj, dict, is_variant=False):
|
||||
# type: (SBValue, dict, bool) -> StructSyntheticProvider
|
||||
# logger = Logger.Logger()
|
||||
self.valobj = valobj
|
||||
self.is_variant = is_variant
|
||||
self.type = valobj.GetType()
|
||||
self.fields = {}
|
||||
|
||||
if is_variant:
|
||||
self.fields_count = self.type.GetNumberOfFields() - 1
|
||||
real_fields = self.type.fields[1:]
|
||||
else:
|
||||
self.fields_count = self.type.GetNumberOfFields()
|
||||
real_fields = self.type.fields
|
||||
|
||||
for number, field in enumerate(real_fields):
|
||||
self.fields[field.name] = number
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.fields_count
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
return self.fields.get(name, -1)
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
if self.is_variant:
|
||||
field = self.type.GetFieldAtIndex(index + 1)
|
||||
else:
|
||||
field = self.type.GetFieldAtIndex(index)
|
||||
return self.valobj.GetChildMemberWithName(field.name)
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
pass
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
class ClangEncodedEnumProvider:
|
||||
"""Pretty-printer for 'clang-encoded' enums support implemented in LLDB"""
|
||||
DISCRIMINANT_MEMBER_NAME = "$discr$"
|
||||
VALUE_MEMBER_NAME = "value"
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
self.valobj = valobj
|
||||
self.update()
|
||||
|
||||
def has_children(self):
|
||||
return True
|
||||
|
||||
def num_children(self):
|
||||
if self.is_default:
|
||||
return 1
|
||||
return 2
|
||||
|
||||
def get_child_index(self, name):
|
||||
if name == ClangEncodedEnumProvider.VALUE_MEMBER_NAME:
|
||||
return 0
|
||||
if name == ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME:
|
||||
return 1
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
if index == 0:
|
||||
return self.variant.GetChildMemberWithName(ClangEncodedEnumProvider.VALUE_MEMBER_NAME)
|
||||
if index == 1:
|
||||
return self.variant.GetChildMemberWithName(
|
||||
ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME)
|
||||
|
||||
|
||||
def update(self):
|
||||
all_variants = self.valobj.GetChildAtIndex(0)
|
||||
index = self._getCurrentVariantIndex(all_variants)
|
||||
self.variant = all_variants.GetChildAtIndex(index)
|
||||
self.is_default = self.variant.GetIndexOfChildWithName(
|
||||
ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME) == -1
|
||||
|
||||
def _getCurrentVariantIndex(self, all_variants):
|
||||
default_index = 0
|
||||
for i in range(all_variants.GetNumChildren()):
|
||||
variant = all_variants.GetChildAtIndex(i)
|
||||
discr = variant.GetChildMemberWithName(
|
||||
ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME)
|
||||
if discr.IsValid():
|
||||
discr_unsigned_value = discr.GetValueAsUnsigned()
|
||||
if variant.GetName() == f"$variant${discr_unsigned_value}":
|
||||
return i
|
||||
else:
|
||||
default_index = i
|
||||
return default_index
|
||||
|
||||
class TupleSyntheticProvider:
|
||||
"""Pretty-printer for tuples and tuple enum variants"""
|
||||
|
||||
def __init__(self, valobj, dict, is_variant=False):
|
||||
# type: (SBValue, dict, bool) -> TupleSyntheticProvider
|
||||
# logger = Logger.Logger()
|
||||
self.valobj = valobj
|
||||
self.is_variant = is_variant
|
||||
self.type = valobj.GetType()
|
||||
|
||||
if is_variant:
|
||||
self.size = self.type.GetNumberOfFields() - 1
|
||||
else:
|
||||
self.size = self.type.GetNumberOfFields()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.size
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
if name.isdigit():
|
||||
return int(name)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
if self.is_variant:
|
||||
field = self.type.GetFieldAtIndex(index + 1)
|
||||
else:
|
||||
field = self.type.GetFieldAtIndex(index)
|
||||
element = self.valobj.GetChildMemberWithName(field.name)
|
||||
return self.valobj.CreateValueFromData(str(index), element.GetData(), element.GetType())
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
pass
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
class StdVecSyntheticProvider:
|
||||
"""Pretty-printer for alloc::vec::Vec<T>
|
||||
|
||||
struct Vec<T> { buf: RawVec<T>, len: usize }
|
||||
rust 1.75: struct RawVec<T> { ptr: Unique<T>, cap: usize, ... }
|
||||
rust 1.76: struct RawVec<T> { ptr: Unique<T>, cap: Cap(usize), ... }
|
||||
rust 1.31.1: struct Unique<T: ?Sized> { pointer: NonZero<*const T>, ... }
|
||||
rust 1.33.0: struct Unique<T: ?Sized> { pointer: *const T, ... }
|
||||
rust 1.62.0: struct Unique<T: ?Sized> { pointer: NonNull<T>, ... }
|
||||
struct NonZero<T>(T)
|
||||
struct NonNull<T> { pointer: *const T }
|
||||
"""
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
# type: (SBValue, dict) -> StdVecSyntheticProvider
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName())
|
||||
self.valobj = valobj
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.length
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
index = name.lstrip('[').rstrip(']')
|
||||
if index.isdigit():
|
||||
return int(index)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
start = self.data_ptr.GetValueAsUnsigned()
|
||||
address = start + index * self.element_type_size
|
||||
element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
|
||||
return element
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned()
|
||||
self.buf = self.valobj.GetChildMemberWithName("buf")
|
||||
|
||||
self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
|
||||
|
||||
self.element_type = self.data_ptr.GetType().GetPointeeType()
|
||||
self.element_type_size = self.element_type.GetByteSize()
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
class StdSliceSyntheticProvider:
|
||||
def __init__(self, valobj, dict):
|
||||
self.valobj = valobj
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.length
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
index = name.lstrip('[').rstrip(']')
|
||||
if index.isdigit():
|
||||
return int(index)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
start = self.data_ptr.GetValueAsUnsigned()
|
||||
address = start + index * self.element_type_size
|
||||
element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
|
||||
return element
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
self.length = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
|
||||
self.data_ptr = self.valobj.GetChildMemberWithName("data_ptr")
|
||||
|
||||
self.element_type = self.data_ptr.GetType().GetPointeeType()
|
||||
self.element_type_size = self.element_type.GetByteSize()
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
class StdVecDequeSyntheticProvider:
|
||||
"""Pretty-printer for alloc::collections::vec_deque::VecDeque<T>
|
||||
|
||||
struct VecDeque<T> { head: usize, len: usize, buf: RawVec<T> }
|
||||
"""
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
# type: (SBValue, dict) -> StdVecDequeSyntheticProvider
|
||||
# logger = Logger.Logger()
|
||||
# logger >> "[StdVecDequeSyntheticProvider] for " + str(valobj.GetName())
|
||||
self.valobj = valobj
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.size
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
index = name.lstrip('[').rstrip(']')
|
||||
if index.isdigit() and int(index) < self.size:
|
||||
return int(index)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
start = self.data_ptr.GetValueAsUnsigned()
|
||||
address = start + ((index + self.head) % self.cap) * self.element_type_size
|
||||
element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
|
||||
return element
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned()
|
||||
self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned()
|
||||
self.buf = self.valobj.GetChildMemberWithName("buf")
|
||||
cap = self.buf.GetChildMemberWithName("cap")
|
||||
if cap.GetType().num_fields == 1:
|
||||
cap = cap.GetChildAtIndex(0)
|
||||
self.cap = cap.GetValueAsUnsigned()
|
||||
|
||||
self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
|
||||
|
||||
self.element_type = self.data_ptr.GetType().GetPointeeType()
|
||||
self.element_type_size = self.element_type.GetByteSize()
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
# BACKCOMPAT: rust 1.35
|
||||
class StdOldHashMapSyntheticProvider:
|
||||
"""Pretty-printer for std::collections::hash::map::HashMap<K, V, S>
|
||||
|
||||
struct HashMap<K, V, S> {..., table: RawTable<K, V>, ... }
|
||||
struct RawTable<K, V> { capacity_mask: usize, size: usize, hashes: TaggedHashUintPtr, ... }
|
||||
"""
|
||||
|
||||
def __init__(self, valobj, dict, show_values=True):
|
||||
# type: (SBValue, dict, bool) -> StdOldHashMapSyntheticProvider
|
||||
self.valobj = valobj
|
||||
self.show_values = show_values
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.size
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
index = name.lstrip('[').rstrip(']')
|
||||
if index.isdigit():
|
||||
return int(index)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
# logger = Logger.Logger()
|
||||
start = self.data_ptr.GetValueAsUnsigned() & ~1
|
||||
|
||||
# See `libstd/collections/hash/table.rs:raw_bucket_at
|
||||
hashes = self.hash_uint_size * self.capacity
|
||||
align = self.pair_type_size
|
||||
# See `libcore/alloc.rs:padding_needed_for`
|
||||
len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~(
|
||||
(align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo
|
||||
# len_rounded_up = ((hashes + align - 1) & ~(align - 1)) - hashes
|
||||
|
||||
pairs_offset = hashes + len_rounded_up
|
||||
pairs_start = start + pairs_offset
|
||||
|
||||
table_index = self.valid_indices[index]
|
||||
idx = table_index & self.capacity_mask
|
||||
address = pairs_start + idx * self.pair_type_size
|
||||
element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
|
||||
if self.show_values:
|
||||
return element
|
||||
else:
|
||||
key = element.GetChildAtIndex(0)
|
||||
return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType())
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
# logger = Logger.Logger()
|
||||
|
||||
self.table = self.valobj.GetChildMemberWithName("table") # type: SBValue
|
||||
self.size = self.table.GetChildMemberWithName("size").GetValueAsUnsigned()
|
||||
self.hashes = self.table.GetChildMemberWithName("hashes")
|
||||
self.hash_uint_type = self.hashes.GetType()
|
||||
self.hash_uint_size = self.hashes.GetType().GetByteSize()
|
||||
self.modulo = 2 ** self.hash_uint_size
|
||||
self.data_ptr = self.hashes.GetChildAtIndex(0).GetChildAtIndex(0)
|
||||
|
||||
self.capacity_mask = self.table.GetChildMemberWithName("capacity_mask").GetValueAsUnsigned()
|
||||
self.capacity = (self.capacity_mask + 1) % self.modulo
|
||||
|
||||
marker = self.table.GetChildMemberWithName("marker").GetType() # type: SBType
|
||||
self.pair_type = marker.template_args[0]
|
||||
self.pair_type_size = self.pair_type.GetByteSize()
|
||||
|
||||
self.valid_indices = []
|
||||
for idx in range(self.capacity):
|
||||
address = self.data_ptr.GetValueAsUnsigned() + idx * self.hash_uint_size
|
||||
hash_uint = self.data_ptr.CreateValueFromAddress("[%s]" % idx, address,
|
||||
self.hash_uint_type)
|
||||
hash_ptr = hash_uint.GetChildAtIndex(0).GetChildAtIndex(0)
|
||||
if hash_ptr.GetValueAsUnsigned() != 0:
|
||||
self.valid_indices.append(idx)
|
||||
|
||||
# logger >> "Valid indices: {}".format(str(self.valid_indices))
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
class StdHashMapSyntheticProvider:
|
||||
"""Pretty-printer for hashbrown's HashMap"""
|
||||
|
||||
def __init__(self, valobj, dict, show_values=True):
|
||||
# type: (SBValue, dict, bool) -> StdHashMapSyntheticProvider
|
||||
self.valobj = valobj
|
||||
self.show_values = show_values
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return self.size
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
index = name.lstrip('[').rstrip(']')
|
||||
if index.isdigit():
|
||||
return int(index)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
pairs_start = self.data_ptr.GetValueAsUnsigned()
|
||||
idx = self.valid_indices[index]
|
||||
if self.new_layout:
|
||||
idx = -(idx + 1)
|
||||
address = pairs_start + idx * self.pair_type_size
|
||||
element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
|
||||
if self.show_values:
|
||||
return element
|
||||
else:
|
||||
key = element.GetChildAtIndex(0)
|
||||
return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType())
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
table = self.table()
|
||||
inner_table = table.GetChildMemberWithName("table")
|
||||
|
||||
capacity = inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1
|
||||
ctrl = inner_table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
|
||||
|
||||
self.size = inner_table.GetChildMemberWithName("items").GetValueAsUnsigned()
|
||||
self.pair_type = table.type.template_args[0]
|
||||
if self.pair_type.IsTypedefType():
|
||||
self.pair_type = self.pair_type.GetTypedefedType()
|
||||
self.pair_type_size = self.pair_type.GetByteSize()
|
||||
|
||||
self.new_layout = not inner_table.GetChildMemberWithName("data").IsValid()
|
||||
if self.new_layout:
|
||||
self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType())
|
||||
else:
|
||||
self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex(0)
|
||||
|
||||
u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar)
|
||||
u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
|
||||
|
||||
self.valid_indices = []
|
||||
for idx in range(capacity):
|
||||
address = ctrl.GetValueAsUnsigned() + idx * u8_type_size
|
||||
value = ctrl.CreateValueFromAddress("ctrl[%s]" % idx, address,
|
||||
u8_type).GetValueAsUnsigned()
|
||||
is_present = value & 128 == 0
|
||||
if is_present:
|
||||
self.valid_indices.append(idx)
|
||||
|
||||
def table(self):
|
||||
# type: () -> SBValue
|
||||
if self.show_values:
|
||||
hashbrown_hashmap = self.valobj.GetChildMemberWithName("base")
|
||||
else:
|
||||
# BACKCOMPAT: rust 1.47
|
||||
# HashSet wraps either std HashMap or hashbrown::HashSet, which both
|
||||
# wrap hashbrown::HashMap, so either way we "unwrap" twice.
|
||||
hashbrown_hashmap = self.valobj.GetChildAtIndex(0).GetChildAtIndex(0)
|
||||
return hashbrown_hashmap.GetChildMemberWithName("table")
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
def StdRcSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned()
|
||||
weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned()
|
||||
return "strong={}, weak={}".format(strong, weak)
|
||||
|
||||
|
||||
class StdRcSyntheticProvider:
|
||||
"""Pretty-printer for alloc::rc::Rc<T> and alloc::sync::Arc<T>
|
||||
|
||||
struct Rc<T> { ptr: NonNull<RcBox<T>>, ... }
|
||||
rust 1.31.1: struct NonNull<T> { pointer: NonZero<*const T> }
|
||||
rust 1.33.0: struct NonNull<T> { pointer: *const T }
|
||||
struct NonZero<T>(T)
|
||||
struct RcBox<T> { strong: Cell<usize>, weak: Cell<usize>, value: T }
|
||||
struct Cell<T> { value: UnsafeCell<T> }
|
||||
struct UnsafeCell<T> { value: T }
|
||||
|
||||
struct Arc<T> { ptr: NonNull<ArcInner<T>>, ... }
|
||||
struct ArcInner<T> { strong: atomic::AtomicUsize, weak: atomic::AtomicUsize, data: T }
|
||||
struct AtomicUsize { v: UnsafeCell<usize> }
|
||||
"""
|
||||
|
||||
def __init__(self, valobj, dict, is_atomic=False):
|
||||
# type: (SBValue, dict, bool) -> StdRcSyntheticProvider
|
||||
self.valobj = valobj
|
||||
|
||||
self.ptr = unwrap_unique_or_non_null(self.valobj.GetChildMemberWithName("ptr"))
|
||||
|
||||
self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value")
|
||||
|
||||
self.strong = self.ptr.GetChildMemberWithName("strong").GetChildAtIndex(
|
||||
0).GetChildMemberWithName("value")
|
||||
self.weak = self.ptr.GetChildMemberWithName("weak").GetChildAtIndex(
|
||||
0).GetChildMemberWithName("value")
|
||||
|
||||
self.value_builder = ValueBuilder(valobj)
|
||||
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
# Actually there are 3 children, but only the `value` should be shown as a child
|
||||
return 1
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
if name == "value":
|
||||
return 0
|
||||
if name == "strong":
|
||||
return 1
|
||||
if name == "weak":
|
||||
return 2
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
if index == 0:
|
||||
return self.value
|
||||
if index == 1:
|
||||
return self.value_builder.from_uint("strong", self.strong_count)
|
||||
if index == 2:
|
||||
return self.value_builder.from_uint("weak", self.weak_count)
|
||||
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
self.strong_count = self.strong.GetValueAsUnsigned()
|
||||
self.weak_count = self.weak.GetValueAsUnsigned() - 1
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
class StdCellSyntheticProvider:
|
||||
"""Pretty-printer for std::cell::Cell"""
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
# type: (SBValue, dict) -> StdCellSyntheticProvider
|
||||
self.valobj = valobj
|
||||
self.value = valobj.GetChildMemberWithName("value").GetChildAtIndex(0)
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
return 1
|
||||
|
||||
def get_child_index(self, name):
|
||||
# type: (str) -> int
|
||||
if name == "value":
|
||||
return 0
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
if index == 0:
|
||||
return self.value
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
pass
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
def StdRefSummaryProvider(valobj, dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
borrow = valobj.GetChildMemberWithName("borrow").GetValueAsSigned()
|
||||
return "borrow={}".format(borrow) if borrow >= 0 else "borrow_mut={}".format(-borrow)
|
||||
|
||||
|
||||
class StdRefSyntheticProvider:
|
||||
"""Pretty-printer for std::cell::Ref, std::cell::RefMut, and std::cell::RefCell"""
|
||||
|
||||
def __init__(self, valobj, dict, is_cell=False):
|
||||
# type: (SBValue, dict, bool) -> StdRefSyntheticProvider
|
||||
self.valobj = valobj
|
||||
|
||||
borrow = valobj.GetChildMemberWithName("borrow")
|
||||
value = valobj.GetChildMemberWithName("value")
|
||||
if is_cell:
|
||||
self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName("value")
|
||||
self.value = value.GetChildMemberWithName("value")
|
||||
else:
|
||||
self.borrow = borrow.GetChildMemberWithName("borrow").GetChildMemberWithName(
|
||||
"value").GetChildMemberWithName("value")
|
||||
self.value = value.Dereference()
|
||||
|
||||
self.value_builder = ValueBuilder(valobj)
|
||||
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
# type: () -> int
|
||||
# Actually there are 2 children, but only the `value` should be shown as a child
|
||||
return 1
|
||||
|
||||
def get_child_index(self, name):
|
||||
if name == "value":
|
||||
return 0
|
||||
if name == "borrow":
|
||||
return 1
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
# type: (int) -> SBValue
|
||||
if index == 0:
|
||||
return self.value
|
||||
if index == 1:
|
||||
return self.value_builder.from_int("borrow", self.borrow_count)
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
# type: () -> None
|
||||
self.borrow_count = self.borrow.GetValueAsSigned()
|
||||
|
||||
def has_children(self):
|
||||
# type: () -> bool
|
||||
return True
|
||||
|
||||
|
||||
def StdNonZeroNumberSummaryProvider(valobj, _dict):
|
||||
# type: (SBValue, dict) -> str
|
||||
inner = valobj.GetChildAtIndex(0)
|
||||
inner_inner = inner.GetChildAtIndex(0)
|
||||
|
||||
# FIXME: Avoid printing as character literal,
|
||||
# see https://github.com/llvm/llvm-project/issues/65076.
|
||||
if inner_inner.GetTypeName() in ['char', 'unsigned char']:
|
||||
return str(inner_inner.GetValueAsSigned())
|
||||
else:
|
||||
return inner_inner.GetValue()
|
@ -0,0 +1,127 @@
|
||||
import re
|
||||
|
||||
|
||||
class RustType(object):
|
||||
OTHER = "Other"
|
||||
STRUCT = "Struct"
|
||||
TUPLE = "Tuple"
|
||||
CSTYLE_VARIANT = "CStyleVariant"
|
||||
TUPLE_VARIANT = "TupleVariant"
|
||||
STRUCT_VARIANT = "StructVariant"
|
||||
ENUM = "Enum"
|
||||
EMPTY = "Empty"
|
||||
SINGLETON_ENUM = "SingletonEnum"
|
||||
REGULAR_ENUM = "RegularEnum"
|
||||
COMPRESSED_ENUM = "CompressedEnum"
|
||||
REGULAR_UNION = "RegularUnion"
|
||||
|
||||
STD_STRING = "StdString"
|
||||
STD_OS_STRING = "StdOsString"
|
||||
STD_STR = "StdStr"
|
||||
STD_SLICE = "StdSlice"
|
||||
STD_VEC = "StdVec"
|
||||
STD_VEC_DEQUE = "StdVecDeque"
|
||||
STD_BTREE_SET = "StdBTreeSet"
|
||||
STD_BTREE_MAP = "StdBTreeMap"
|
||||
STD_HASH_MAP = "StdHashMap"
|
||||
STD_HASH_SET = "StdHashSet"
|
||||
STD_RC = "StdRc"
|
||||
STD_ARC = "StdArc"
|
||||
STD_CELL = "StdCell"
|
||||
STD_REF = "StdRef"
|
||||
STD_REF_MUT = "StdRefMut"
|
||||
STD_REF_CELL = "StdRefCell"
|
||||
STD_NONZERO_NUMBER = "StdNonZeroNumber"
|
||||
STD_PATH = "StdPath"
|
||||
STD_PATHBUF = "StdPathBuf"
|
||||
|
||||
|
||||
STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$")
|
||||
STD_STR_REGEX = re.compile(r"^&(mut )?str$")
|
||||
STD_SLICE_REGEX = re.compile(r"^&(mut )?\[.+\]$")
|
||||
STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::([a-z_]+::)+)OsString$")
|
||||
STD_VEC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Vec<.+>$")
|
||||
STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)VecDeque<.+>$")
|
||||
STD_BTREE_SET_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeSet<.+>$")
|
||||
STD_BTREE_MAP_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeMap<.+>$")
|
||||
STD_HASH_MAP_REGEX = re.compile(r"^(std::collections::([a-z_]+::)+)HashMap<.+>$")
|
||||
STD_HASH_SET_REGEX = re.compile(r"^(std::collections::([a-z_]+::)+)HashSet<.+>$")
|
||||
STD_RC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Rc<.+>$")
|
||||
STD_ARC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Arc<.+>$")
|
||||
STD_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)Cell<.+>$")
|
||||
STD_REF_REGEX = re.compile(r"^(core::([a-z_]+::)+)Ref<.+>$")
|
||||
STD_REF_MUT_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefMut<.+>$")
|
||||
STD_REF_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefCell<.+>$")
|
||||
STD_NONZERO_NUMBER_REGEX = re.compile(r"^(core::([a-z_]+::)+)NonZero<.+>$")
|
||||
STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$")
|
||||
STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$")
|
||||
|
||||
TUPLE_ITEM_REGEX = re.compile(r"__\d+$")
|
||||
|
||||
ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$"
|
||||
ENUM_DISR_FIELD_NAME = "<<variant>>"
|
||||
ENUM_LLDB_ENCODED_VARIANTS = "$variants$"
|
||||
|
||||
STD_TYPE_TO_REGEX = {
|
||||
RustType.STD_STRING: STD_STRING_REGEX,
|
||||
RustType.STD_OS_STRING: STD_OS_STRING_REGEX,
|
||||
RustType.STD_STR: STD_STR_REGEX,
|
||||
RustType.STD_SLICE: STD_SLICE_REGEX,
|
||||
RustType.STD_VEC: STD_VEC_REGEX,
|
||||
RustType.STD_VEC_DEQUE: STD_VEC_DEQUE_REGEX,
|
||||
RustType.STD_HASH_MAP: STD_HASH_MAP_REGEX,
|
||||
RustType.STD_HASH_SET: STD_HASH_SET_REGEX,
|
||||
RustType.STD_BTREE_SET: STD_BTREE_SET_REGEX,
|
||||
RustType.STD_BTREE_MAP: STD_BTREE_MAP_REGEX,
|
||||
RustType.STD_RC: STD_RC_REGEX,
|
||||
RustType.STD_ARC: STD_ARC_REGEX,
|
||||
RustType.STD_REF: STD_REF_REGEX,
|
||||
RustType.STD_REF_MUT: STD_REF_MUT_REGEX,
|
||||
RustType.STD_REF_CELL: STD_REF_CELL_REGEX,
|
||||
RustType.STD_CELL: STD_CELL_REGEX,
|
||||
RustType.STD_NONZERO_NUMBER: STD_NONZERO_NUMBER_REGEX,
|
||||
RustType.STD_PATHBUF: STD_PATHBUF_REGEX,
|
||||
RustType.STD_PATH: STD_PATH_REGEX,
|
||||
}
|
||||
|
||||
def is_tuple_fields(fields):
|
||||
# type: (list) -> bool
|
||||
return all(TUPLE_ITEM_REGEX.match(str(field.name)) for field in fields)
|
||||
|
||||
|
||||
def classify_struct(name, fields):
|
||||
if len(fields) == 0:
|
||||
return RustType.EMPTY
|
||||
|
||||
for ty, regex in STD_TYPE_TO_REGEX.items():
|
||||
if regex.match(name):
|
||||
return ty
|
||||
|
||||
# <<variant>> is emitted by GDB while LLDB(18.1+) emits "$variants$"
|
||||
if (
|
||||
fields[0].name == ENUM_DISR_FIELD_NAME
|
||||
or fields[0].name == ENUM_LLDB_ENCODED_VARIANTS
|
||||
):
|
||||
return RustType.ENUM
|
||||
|
||||
if is_tuple_fields(fields):
|
||||
return RustType.TUPLE
|
||||
|
||||
return RustType.STRUCT
|
||||
|
||||
|
||||
def classify_union(fields):
|
||||
if len(fields) == 0:
|
||||
return RustType.EMPTY
|
||||
|
||||
first_variant_name = fields[0].name
|
||||
if first_variant_name is None:
|
||||
if len(fields) == 1:
|
||||
return RustType.SINGLETON_ENUM
|
||||
else:
|
||||
return RustType.REGULAR_ENUM
|
||||
elif first_variant_name.startswith(ENCODED_ENUM_PREFIX):
|
||||
assert len(fields) == 1
|
||||
return RustType.COMPRESSED_ENUM
|
||||
else:
|
||||
return RustType.REGULAR_UNION
|
Reference in New Issue
Block a user