Skip to content

Add LLDB providers for BTreeMap and BTreeSet #140130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
11 changes: 11 additions & 0 deletions src/etc/lldb_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def summary_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> str:
if rust_type == RustType.STD_HASH_SET:
return SizeSummaryProvider(valobj, _dict)

if rust_type == RustType.STD_BTREE_MAP:
return SizeSummaryProvider(valobj, _dict)
if rust_type == RustType.STD_BTREE_SET:
return SizeSummaryProvider(valobj, _dict)

if rust_type == RustType.STD_RC:
return StdRcSummaryProvider(valobj, _dict)
if rust_type == RustType.STD_ARC:
Expand Down Expand Up @@ -105,6 +110,12 @@ def synthetic_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object:
else:
return StdOldHashMapSyntheticProvider(hash_map, _dict, show_values=False)

if rust_type == RustType.STD_BTREE_MAP:
return StdBTreeMapSyntheticProvider(valobj, _dict)
if rust_type == RustType.STD_BTREE_SET:
btree_map = valobj.GetChildAtIndex(0)
return StdBTreeMapSyntheticProvider(btree_map, _dict, show_values=False)

if rust_type == RustType.STD_RC:
return StdRcSyntheticProvider(valobj, _dict)
if rust_type == RustType.STD_ARC:
Expand Down
124 changes: 124 additions & 0 deletions src/etc/lldb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,130 @@ def has_children(self) -> bool:
return True


def children_of_node(node_ptr: SBValue, height: int):
def get_edges(node: SBValue) -> SBValue:
# BTreeMap implementation does ad-hoc polymorphism between LeafNode and InternalNode
# with raw pointers.
# https://github.com/rust-lang/rust/issues/90520#issuecomment-2211103129
# Implementing this the same way as the GDB provider with type casting fails
# because LLDB does not find the target type for some reason.
# Therefore, we manually do the pointer arithmetic to get the edges array
# and handle it as raw pointers later instead of MaybeUninit<NonNull<LeafNode<K,V>>>.
# We can do that because InternalNode is repr(C).
leaf_ptr_type = node.GetType()
# Array has a constant length of 2 * B
edges_arr_type = leaf_ptr_type.GetArrayType(12)
node_addr = node.unsigned
leaf_size = leaf_ptr_type.GetPointeeType().size
edges_addr = node_addr + leaf_size
return node.CreateValueFromAddress("edges", edges_addr, edges_arr_type)

def unwrap_item_from_array_of_maybe_uninit(arr: SBValue, index: int) -> SBValue:
element = arr.GetChildAtIndex(index)
return element.GetChildMemberWithName("value").GetChildMemberWithName("value")

if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
# BACKCOMPAT: rust 1.49
node_ptr = node_ptr.GetChildMemberWithName("ptr")

if not node_ptr.type.IsPointerType():
# After the first recursion, this method is called with a raw pointer type directly
# instead of NonNull<T>
node_ptr = unwrap_unique_or_non_null(node_ptr)

leaf = node_ptr.Dereference()
keys = leaf.GetChildMemberWithName("keys")
vals = leaf.GetChildMemberWithName("vals")
length = leaf.GetChildMemberWithName("len").unsigned
edges = get_edges(node_ptr) if height > 0 else None

for i in range(length + 1):
if height > 0:
child_ptr = edges.GetChildAtIndex(i)
yield from children_of_node(child_ptr, height - 1)
if i < length:
# Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
key_type_size = keys.type.size
val_type_size = vals.type.size
key = (
unwrap_item_from_array_of_maybe_uninit(keys, i)
if key_type_size > 0
else node_ptr.EvaluateExpression("()")
)
val = (
unwrap_item_from_array_of_maybe_uninit(vals, i)
if val_type_size > 0
else node_ptr.EvaluateExpression("()")
)
yield key, val


class StdBTreeMapSyntheticProvider:
def __init__(self, valobj: SBValue, _dict: LLDBOpaque, show_values: bool = True):
self.valobj = valobj
self._dict = _dict
self.show_values = show_values

def num_children(self) -> int:
return self.size

def get_child_index(self, name: str) -> int:
index = name.lstrip("[").rstrip("]")
if index.isdigit():
return int(index)
else:
return -1

def get_child_at_index(self, index: int) -> SBValue:
key, value = self.items[index]
if self.show_values:
data = key.GetData()
assert data.Append(value.GetData()), "Failed to create key value pair"
return self.valobj.CreateValueFromData("[%s]" % index, data, self.pair_type)
return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.type)

def update(self) -> bool:
self.size = self.valobj.GetChildMemberWithName("length").unsigned
self.items = []

# Determine the type for the tuple (Key, Value)
# - get_template_args helper breaks on console because type is shown as
# `core::marker::PhantomData<(&str, &str) *>`
# - Type lookup after get_template_args helper fails with codelldb for unclear reasons
# - Native `template_args[0]` from LLDB fails with codelldb and just says `T` if printed
# on console
marker = self.valobj.GetChildMemberWithName("_marker")
marker_type = marker.GetType()
box = marker_type.GetTemplateArgumentType(0)
self.pair_type = box.GetPointeeType()

if self.size == 0:
return

root = self.valobj.GetChildMemberWithName("root")

if root.type.name.startswith("core::option::Option<"):
synthetic_children = root.children[0]
current_variant = synthetic_children.GetChildMemberWithName("$variant$")
root = current_variant.GetChildMemberWithName(
"value"
).GetChildMemberWithName("__0")

height = root.GetChildMemberWithName("height")
node_ptr = root.GetChildMemberWithName("node")

self.items = [
(key, value) for key, value in children_of_node(node_ptr, height.unsigned)
]

assert len(self.items) == self.size

return False

def has_children(self) -> bool:
return True


def StdRcSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned()
weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned()
Expand Down
3 changes: 3 additions & 0 deletions src/tools/compiletest/src/runtest/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ impl DebuggerCommands {
}

/// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
/// Note that this matching is lazy, i.e. matches as little as possible and thus might leave
/// stuff unparsed, failing the check.
/// This is different to usual regex or global matching that is typically eager.
fn check_single_line(line: &str, check_line: &str) -> bool {
// Allow check lines to leave parts unspecified (e.g., uninitialized
// bits in the wrong case of an enum) with the notation "[...]".
Expand Down
27 changes: 27 additions & 0 deletions tests/debuginfo/pretty-std-collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,33 @@

// lldb-command:run

// lldb-command: v btree_set
// 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 }

// lldb-command: v empty_btree_set
// lldb-check:[...] size=0

// lldb-command: v btree_map
// 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 } }

// lldb-command: v option_btree_map
// lldb-check:[...] size=2 { [0] = { 0 = false 1 = [...] } [1] = { 0 = true 1 = [...]
// (The matching here is lazy, so we cannot add braces at the end because they would match
// intermediate braces and fail because not the entire input was consumed.)

// lldb-command: v nasty_btree_map
// 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 } } }
// (Does not print out the type name in lldb)

// lldb-command: v zst_key_btree_map
// lldb-check:[...] size=1 { [0] = { 1 = 1 } }

// lldb-command: v zst_val_btree_map
// lldb-check:[...] size=1 { [0] = { 0 = 1 } }

// lldb-command: v zst_key_val_btree_map
// lldb-check:[...] size=1 { [0] = {} }

// lldb-command:v vec_deque
// lldb-check:[...] size=3 { [0] = 5 [1] = 3 [2] = 7 }

Expand Down
Loading