Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 22 additions & 49 deletions src/etc/gdb_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import gdb.printing
import re

from gdb_providers import *
from rust_types import *
import gdb_providers as providers
from rust_types import RustType, classify_struct, classify_union


_gdb_version_matched = re.search("([0-9]+)\\.([0-9]+)", gdb.VERSION)
Expand Down Expand Up @@ -42,31 +42,6 @@ def check_enum_discriminant(valobj):
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)
Expand Down Expand Up @@ -97,25 +72,23 @@ def __call__(self, valobj):


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)
printer.add(RustType.STD_STRING, providers.StdStringProvider)
printer.add(RustType.STD_OS_STRING, providers.StdOsStringProvider)
printer.add(RustType.STD_STR, providers.StdStrProvider)
printer.add(RustType.STD_SLICE, providers.StdSliceProvider)
printer.add(RustType.STD_VEC, providers.StdVecProvider)
printer.add(RustType.STD_VEC_DEQUE, providers.StdVecDequeProvider)
printer.add(RustType.STD_BTREE_SET, providers.StdBTreeSetProvider)
printer.add(RustType.STD_BTREE_MAP, providers.StdBTreeMapProvider)
printer.add(RustType.STD_HASH_MAP, providers.StdHashMapProvider)
printer.add(RustType.STD_HASH_SET, providers.StdHashMapProvider)
printer.add(RustType.STD_RC, providers.StdRcProvider)
printer.add(
RustType.STD_ARC, lambda valobj: providers.StdRcProvider(valobj, is_atomic=True)
)

printer.add(RustType.STD_CELL, providers.StdCellProvider)
printer.add(RustType.STD_REF, providers.StdRefProvider)
printer.add(RustType.STD_REF_MUT, providers.StdRefProvider)
printer.add(RustType.STD_REF_CELL, providers.StdRefCellProvider)
printer.add(RustType.STD_NONZERO_NUMBER, providers.StdNonZeroNumberProvider)
120 changes: 5 additions & 115 deletions src/etc/gdb_providers.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
from sys import version_info

import gdb

if version_info[0] >= 3:
xrange = range

Comment on lines -5 to -7
Copy link
Copy Markdown
Contributor Author

@clarfonthey clarfonthey Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removes Python 2 support, which was probably broken anyway. Not really possible to say.

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]]

Expand All @@ -33,30 +24,6 @@ def unwrap_scalar_wrappers(wrapper):
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
Comment on lines -36 to -57
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned in other file, but this is older GDB support.



class StdStringProvider(printer_base):
def __init__(self, valobj):
self._valobj = valobj
Expand Down Expand Up @@ -131,7 +98,7 @@ def to_string(self):

def children(self):
return _enumerate_array_elements(
self._data_ptr + index for index in xrange(self._length)
self._data_ptr + index for index in range(self._length)
)

def num_children(self):
Expand All @@ -155,7 +122,7 @@ def to_string(self):

def children(self):
return _enumerate_array_elements(
self._data_ptr + index for index in xrange(self._length)
self._data_ptr + index for index in range(self._length)
)

def num_children(self):
Expand All @@ -171,10 +138,7 @@ def __init__(self, valobj):
self._valobj = valobj
self._head = int(valobj["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]
cap = valobj["buf"]["inner"]["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))
Expand All @@ -186,7 +150,7 @@ def to_string(self):
def children(self):
return _enumerate_array_elements(
(self._data_ptr + ((self._head + index) % self._cap))
for index in xrange(self._size)
for index in range(self._size)
)

def num_children(self):
Expand Down Expand Up @@ -292,17 +256,14 @@ def cast_to_internal(node):
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):
for i in range(length + 1):
if height > 0:
child_ptr = edges[i]["value"]["value"][ZERO_FIELD]
for child in children_of_node(child_ptr, height - 1):
Expand Down Expand Up @@ -367,73 +328,6 @@ 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
Expand Down Expand Up @@ -464,10 +358,6 @@ def __init__(self, valobj, show_values=True):
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"]
Expand Down
Loading
Loading