Skip to content

Commit 950f608

Browse files
committed
Add LLDB providers for BTreeMap and BTreeSet
Fixes #111868
1 parent c8f9423 commit 950f608

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed

library/alloc/src/collections/btree/node.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<K, V> LeafNode<K, V> {
9393
/// node, allowing code to act on leaf and internal nodes generically without having to even check
9494
/// which of the two a pointer is pointing at. This property is enabled by the use of `repr(C)`.
9595
#[repr(C)]
96-
// gdb_providers.py uses this type name for introspection.
96+
// gdb_providers.py and lldb_providers.py use this type name for introspection.
9797
struct InternalNode<K, V> {
9898
data: LeafNode<K, V>,
9999

src/etc/lldb_lookup.py

+10
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ def summary_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> str:
4242
if rust_type == RustType.STD_HASH_SET:
4343
return SizeSummaryProvider(valobj, _dict)
4444

45+
if rust_type == RustType.STD_BTREE_MAP:
46+
return SizeSummaryProvider(valobj, _dict)
47+
if rust_type == RustType.STD_BTREE_SET:
48+
return SizeSummaryProvider(valobj, _dict)
49+
4550
if rust_type == RustType.STD_RC:
4651
return StdRcSummaryProvider(valobj, _dict)
4752
if rust_type == RustType.STD_ARC:
@@ -105,6 +110,11 @@ def synthetic_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object:
105110
else:
106111
return StdOldHashMapSyntheticProvider(hash_map, _dict, show_values=False)
107112

113+
if rust_type == RustType.STD_BTREE_MAP:
114+
return StdBTreeMapSyntheticProvider(valobj, _dict)
115+
if rust_type == RustType.STD_BTREE_SET:
116+
return StdBTreeMapSyntheticProvider(valobj, _dict, show_values=False)
117+
108118
if rust_type == RustType.STD_RC:
109119
return StdRcSyntheticProvider(valobj, _dict)
110120
if rust_type == RustType.STD_ARC:

src/etc/lldb_providers.py

+123
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,129 @@ def has_children(self) -> bool:
11511151
return True
11521152

11531153

1154+
def children_of_node(node_ptr: SBValue, height: int):
1155+
def cast_to_internal(node: SBValue) -> SBValue:
1156+
# BTreeMap implementation does ad-hoc polymorphism between LeafNode and InternalNode
1157+
# with raw pointers.
1158+
# https://github.com/rust-lang/rust/issues/90520#issuecomment-2211103129
1159+
internal_type_name = node.type.GetPointeeType().name.replace(
1160+
"LeafNode", "InternalNode", 1
1161+
)
1162+
target = node.GetTarget()
1163+
internal_type = target.FindFirstType(internal_type_name)
1164+
return node.Cast(internal_type.GetPointerType())
1165+
1166+
def unwrap_item_from_array_of_maybe_uninit(arr: SBValue, index: int) -> SBValue:
1167+
element = arr.GetChildAtIndex(index)
1168+
return element.GetChildMemberWithName("value").GetChildMemberWithName("value")
1169+
1170+
if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
1171+
# BACKCOMPAT: rust 1.49
1172+
node_ptr = node_ptr.GetChildMemberWithName("ptr")
1173+
node_ptr = unwrap_unique_or_non_null(node_ptr)
1174+
leaf = node_ptr.Dereference()
1175+
keys = leaf.GetChildMemberWithName("keys")
1176+
vals = leaf.GetChildMemberWithName("vals")
1177+
length = leaf.GetChildMemberWithName("len").unsigned
1178+
edges = (
1179+
cast_to_internal(node_ptr).GetChildMemberWithName("edges")
1180+
if height > 0
1181+
else None
1182+
)
1183+
1184+
for i in range(length + 1):
1185+
if height > 0:
1186+
child_ptr = unwrap_item_from_array_of_maybe_uninit(edges, i)
1187+
yield from children_of_node(child_ptr, height - 1)
1188+
if i < length:
1189+
# Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
1190+
key_type_size = keys.type.size
1191+
val_type_size = vals.type.size
1192+
key = (
1193+
unwrap_item_from_array_of_maybe_uninit(keys, i)
1194+
if key_type_size > 0
1195+
else node_ptr.EvaluateExpression("()")
1196+
)
1197+
val = (
1198+
unwrap_item_from_array_of_maybe_uninit(vals, i)
1199+
if val_type_size > 0
1200+
else node_ptr.EvaluateExpression("()")
1201+
)
1202+
yield key, val
1203+
1204+
1205+
def strip_till_parentheses(text: str) -> str:
1206+
start = text.find("(")
1207+
end = text.find(")")
1208+
if start == -1 or end == -1:
1209+
return text
1210+
return text[start : end + 1]
1211+
1212+
1213+
class StdBTreeMapSyntheticProvider:
1214+
def __init__(self, valobj: SBValue, _dict: LLDBOpaque, show_values: bool = True):
1215+
self.valobj = valobj
1216+
self._dict = _dict
1217+
self.show_values = True
1218+
1219+
def num_children(self) -> int:
1220+
return self.size
1221+
1222+
def get_child_index(self, name: str) -> int:
1223+
index = name.lstrip("[").rstrip("]")
1224+
if index.isdigit():
1225+
return int(index)
1226+
else:
1227+
return -1
1228+
1229+
def get_child_at_index(self, index: int) -> SBValue:
1230+
key, value = self.items[index]
1231+
if self.show_values:
1232+
data = key.GetData()
1233+
assert data.Append(value.GetData()), "Failed to create key value pair"
1234+
return self.valobj.CreateValueFromData("[%s]" % index, data, self.pair_type)
1235+
return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.type)
1236+
1237+
def update(self) -> bool:
1238+
self.size = self.valobj.GetChildMemberWithName("length").unsigned
1239+
self.items = []
1240+
1241+
# Determine the type for the tuple (Key, Value)
1242+
# - get_template_args helper breaks on console because type is shown as
1243+
# `core::marker::PhantomData<(&str, &str) *>`
1244+
# - Type lookup after get_template_args helper fails with codelldb for unclear reasons
1245+
# - Native `template_args[0]` from LLDB fails with codelldb and just says `T` if printed
1246+
# on console
1247+
marker_type_name = self.valobj.GetChildMemberWithName("_marker").GetTypeName()
1248+
pair_type_name = strip_till_parentheses(marker_type_name)
1249+
target = self.valobj.GetTarget()
1250+
self.pair_type = target.FindFirstType(pair_type_name)
1251+
1252+
if self.size == 0:
1253+
return
1254+
1255+
root = self.valobj.GetChildMemberWithName("root")
1256+
1257+
if root.type.name.startswith("core::option::Option<"):
1258+
target = self.valobj.GetTarget()
1259+
type_some = target.FindFirstType(get_template_args(root.GetTypeName())[0])
1260+
root = root.Cast(type_some)
1261+
1262+
height = root.GetChildMemberWithName("height")
1263+
node_ptr = root.GetChildMemberWithName("node")
1264+
1265+
self.items = [
1266+
(key, value) for key, value in children_of_node(node_ptr, height.unsigned)
1267+
]
1268+
1269+
assert len(self.items) == self.size
1270+
1271+
return False
1272+
1273+
def has_children(self) -> bool:
1274+
return True
1275+
1276+
11541277
def StdRcSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
11551278
strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned()
11561279
weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned()

0 commit comments

Comments
 (0)