Skip to content

Commit 53ef2e0

Browse files
committed
Add LLDB providers for BTreeMap and BTreeSet
Fixes rust-lang#111868
1 parent c8f9423 commit 53ef2e0

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

src/etc/lldb_lookup.py

+11
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,12 @@ 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+
btree_map = valobj.GetChildAtIndex(0)
117+
return StdBTreeMapSyntheticProvider(btree_map, _dict, show_values=False)
118+
108119
if rust_type == RustType.STD_RC:
109120
return StdRcSyntheticProvider(valobj, _dict)
110121
if rust_type == RustType.STD_ARC:

src/etc/lldb_providers.py

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

11531153

1154+
def children_of_node(node_ptr: SBValue, height: int):
1155+
def get_edges(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+
# Implementing this the same way as the GDB provider with type casting fails
1160+
# because LLDB does not find the target type for some reason.
1161+
# Therefore, we manually do the pointer arithmetic to get the edges array
1162+
# and handle it as raw pointers later instead of MaybeUninit<NonNull<LeafNode<K,V>>>.
1163+
# We can do that because InternalNode is repr(C).
1164+
leaf_ptr_type = node.GetType()
1165+
# Array has a constant length of 2 * B
1166+
edges_arr_type = leaf_ptr_type.GetArrayType(12)
1167+
node_addr = node.unsigned
1168+
leaf_size = leaf_ptr_type.GetPointeeType().size
1169+
edges_addr = node_addr + leaf_size
1170+
return node.CreateValueFromAddress("edges", edges_addr, edges_arr_type)
1171+
1172+
def unwrap_item_from_array_of_maybe_uninit(arr: SBValue, index: int) -> SBValue:
1173+
element = arr.GetChildAtIndex(index)
1174+
return element.GetChildMemberWithName("value").GetChildMemberWithName("value")
1175+
1176+
if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
1177+
# BACKCOMPAT: rust 1.49
1178+
node_ptr = node_ptr.GetChildMemberWithName("ptr")
1179+
1180+
if not node_ptr.type.IsPointerType():
1181+
# After the first recursion, this method is called with a raw pointer type directly
1182+
# instead of NonNull<T>
1183+
node_ptr = unwrap_unique_or_non_null(node_ptr)
1184+
1185+
leaf = node_ptr.Dereference()
1186+
keys = leaf.GetChildMemberWithName("keys")
1187+
vals = leaf.GetChildMemberWithName("vals")
1188+
length = leaf.GetChildMemberWithName("len").unsigned
1189+
edges = get_edges(node_ptr) if height > 0 else None
1190+
1191+
for i in range(length + 1):
1192+
if height > 0:
1193+
child_ptr = edges.GetChildAtIndex(i)
1194+
yield from children_of_node(child_ptr, height - 1)
1195+
if i < length:
1196+
# Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
1197+
key_type_size = keys.type.size
1198+
val_type_size = vals.type.size
1199+
key = (
1200+
unwrap_item_from_array_of_maybe_uninit(keys, i)
1201+
if key_type_size > 0
1202+
else node_ptr.EvaluateExpression("()")
1203+
)
1204+
val = (
1205+
unwrap_item_from_array_of_maybe_uninit(vals, i)
1206+
if val_type_size > 0
1207+
else node_ptr.EvaluateExpression("()")
1208+
)
1209+
yield key, val
1210+
1211+
1212+
class StdBTreeMapSyntheticProvider:
1213+
def __init__(self, valobj: SBValue, _dict: LLDBOpaque, show_values: bool = True):
1214+
self.valobj = valobj
1215+
self._dict = _dict
1216+
self.show_values = show_values
1217+
1218+
def num_children(self) -> int:
1219+
return self.size
1220+
1221+
def get_child_index(self, name: str) -> int:
1222+
index = name.lstrip("[").rstrip("]")
1223+
if index.isdigit():
1224+
return int(index)
1225+
else:
1226+
return -1
1227+
1228+
def get_child_at_index(self, index: int) -> SBValue:
1229+
key, value = self.items[index]
1230+
if self.show_values:
1231+
data = key.GetData()
1232+
assert data.Append(value.GetData()), "Failed to create key value pair"
1233+
return self.valobj.CreateValueFromData("[%s]" % index, data, self.pair_type)
1234+
return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.type)
1235+
1236+
def update(self) -> bool:
1237+
self.size = self.valobj.GetChildMemberWithName("length").unsigned
1238+
self.items = []
1239+
1240+
# Determine the type for the tuple (Key, Value)
1241+
# - get_template_args helper breaks on console because type is shown as
1242+
# `core::marker::PhantomData<(&str, &str) *>`
1243+
# - Type lookup after get_template_args helper fails with codelldb for unclear reasons
1244+
# - Native `template_args[0]` from LLDB fails with codelldb and just says `T` if printed
1245+
# on console
1246+
marker = self.valobj.GetChildMemberWithName("_marker")
1247+
marker_type = marker.GetType()
1248+
box = marker_type.GetTemplateArgumentType(0)
1249+
self.pair_type = box.GetPointeeType()
1250+
1251+
if self.size == 0:
1252+
return
1253+
1254+
root = self.valobj.GetChildMemberWithName("root")
1255+
1256+
if root.type.name.startswith("core::option::Option<"):
1257+
synthetic_children = root.children[0]
1258+
current_variant = synthetic_children.GetChildMemberWithName("$variant$")
1259+
root = current_variant.GetChildMemberWithName(
1260+
"value"
1261+
).GetChildMemberWithName("__0")
1262+
1263+
height = root.GetChildMemberWithName("height")
1264+
node_ptr = root.GetChildMemberWithName("node")
1265+
1266+
self.items = [
1267+
(key, value) for key, value in children_of_node(node_ptr, height.unsigned)
1268+
]
1269+
1270+
assert len(self.items) == self.size
1271+
1272+
return False
1273+
1274+
def has_children(self) -> bool:
1275+
return True
1276+
1277+
11541278
def StdRcSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
11551279
strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned()
11561280
weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned()

src/tools/compiletest/src/runtest/debugger.rs

+3
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ impl DebuggerCommands {
111111
}
112112

113113
/// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
114+
/// Note that this matching is lazy, i.e. matches as little as possible and thus might leave
115+
/// stuff unparsed, failing the check.
116+
/// This is different to usual regex or global matching that is typically eager.
114117
fn check_single_line(line: &str, check_line: &str) -> bool {
115118
// Allow check lines to leave parts unspecified (e.g., uninitialized
116119
// bits in the wrong case of an enum) with the notation "[...]".

tests/debuginfo/pretty-std-collections.rs

+27
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,33 @@
5151

5252
// lldb-command:run
5353

54+
// lldb-command: v btree_set
55+
// lldb-check:[...] size=15 { [0] = 0 [1] = 1 [2] = 2 [3] = 3 [4] = 4 [5] = 5 [6] = 6 [7] = 7 [8] = 8 [9] = 9 [10] = 10 [11] = 11 [12] = 12 [13] = 13 [14] = 14 }
56+
57+
// lldb-command: v empty_btree_set
58+
// lldb-check:[...] size=0
59+
60+
// lldb-command: v btree_map
61+
// lldb-check:[...] size=15 { [0] = { 0 = 0 1 = 0 } [1] = { 0 = 1 1 = 1 } [2] = { 0 = 2 1 = 2 } [3] = { 0 = 3 1 = 3 } [4] = { 0 = 4 1 = 4 } [5] = { 0 = 5 1 = 5 } [6] = { 0 = 6 1 = 6 } [7] = { 0 = 7 1 = 7 } [8] = { 0 = 8 1 = 8 } [9] = { 0 = 9 1 = 9 } [10] = { 0 = 10 1 = 10 } [11] = { 0 = 11 1 = 11 } [12] = { 0 = 12 1 = 12 } [13] = { 0 = 13 1 = 13 } [14] = { 0 = 14 1 = 14 } }
62+
63+
// lldb-command: v option_btree_map
64+
// lldb-check:[...] size=2 { [0] = { 0 = false 1 = [...] } [1] = { 0 = true 1 = [...]
65+
// (The matching here is lazy, so we cannot add braces at the end because they would match
66+
// intermediate braces and fail because not the entire input was consumed.)
67+
68+
// lldb-command: v nasty_btree_map
69+
// lldb-check:[...] size=15 { [0] = { 0 = 0 1 = { 0 = 0 } } [1] = { 0 = 1 1 = { 0 = 1 } } [2] = { 0 = 2 1 = { 0 = 2 } } [3] = { 0 = 3 1 = { 0 = 3 } } [4] = { 0 = 4 1 = { 0 = 4 } } [5] = { 0 = 5 1 = { 0 = 5 } } [6] = { 0 = 6 1 = { 0 = 6 } } [7] = { 0 = 7 1 = { 0 = 7 } } [8] = { 0 = 8 1 = { 0 = 8 } } [9] = { 0 = 9 1 = { 0 = 9 } } [10] = { 0 = 10 1 = { 0 = 10 } } [11] = { 0 = 11 1 = { 0 = 11 } } [12] = { 0 = 12 1 = { 0 = 12 } } [13] = { 0 = 13 1 = { 0 = 13 } } [14] = { 0 = 14 1 = { 0 = 14 } } }
70+
// (Does not print out the type name in lldb)
71+
72+
// lldb-command: v zst_key_btree_map
73+
// lldb-check:[...] size=1 { [0] = { 1 = 1 } }
74+
75+
// lldb-command: v zst_val_btree_map
76+
// lldb-check:[...] size=1 { [0] = { 0 = 1 } }
77+
78+
// lldb-command: v zst_key_val_btree_map
79+
// lldb-check:[...] size=1 { [0] = {} }
80+
5481
// lldb-command:v vec_deque
5582
// lldb-check:[...] size=3 { [0] = 5 [1] = 3 [2] = 7 }
5683

0 commit comments

Comments
 (0)