diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index f4ea904b7f50b..f512bf966dba1 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -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: @@ -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: diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 98426e4242398..1c104d9c7a8f2 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -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>>. + # 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 + 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() diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index a4103c5b4a9a4..7d0a47ac90867 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -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 "[...]". diff --git a/tests/debuginfo/pretty-std-collections.rs b/tests/debuginfo/pretty-std-collections.rs index 5e133ee718e2e..f78830162ca60 100644 --- a/tests/debuginfo/pretty-std-collections.rs +++ b/tests/debuginfo/pretty-std-collections.rs @@ -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 }