Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e897bdf

Browse files
borsRalfJung
authored andcommittedMay 19, 2024·
Auto merge of #124500 - VladimirMakaev:lldb-str-formatters, r=Mark-Simulacrum
lldb-formatters: Use StdSliceSyntheticProvider for &str &str has associated summary provider which correctly displays string values in debugger, but while working on #124458 I've noticed that a &str inside an enum displays a blob of memory until a 0 is reached (as a c-string) which makes a very bizarre experience when debugging However there is already StdSliceSyntheticProvider which we use for other slices. This PR enables the same synthetic provider to be used for &str, however the summary provider is still fixed to return the string value I've added a test `debuginfo/strings-and-strs.rs` which prior to this PR would output the following in LLDB: ``` * thread #1, name = 'a', stop reason = breakpoint 1.1 frame #0: 0x0000555555556383 a`strings_and_strs::main::h1d2b5f9227b8767d at strings-and-strs.rs:47:5 44 let plain_str = "Hello"; 45 let str_in_struct = Foo { inner: "Hello" }; 46 let str_in_tuple = ("Hello", "World"); -> 47 zzz(); // #break 48 } 49 50 fn zzz() { (lldb) frame var (alloc::string::String) plain_string = "Hello" { vec = size=5 { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } (&str) plain_str = "Hello" { data_ptr = 0x0000555555557263 "HelloWorld\U00000001gdb_load_rust_pretty_printers.py" length = 5 } (strings_and_strs::Foo) str_in_struct = { inner = "Hello" { data_ptr = 0x0000555555557263 "HelloWorld\U00000001gdb_load_rust_pretty_printers.py" length = 5 } } ((&str, &str)) str_in_tuple = { 0 = "Hello" { data_ptr = 0x0000555555557263 "HelloWorld\U00000001gdb_load_rust_pretty_printers.py" length = 5 } 1 = "World" { data_ptr = 0x0000555555557268 "World\U00000001gdb_load_rust_pretty_printers.py" length = 5 } } ``` After this PR it would look the following way: ``` * thread #1, name = 'a', stop reason = breakpoint 1.1 frame #0: 0x0000555555556383 a`strings_and_strs::main::h1d2b5f9227b8767d at strings-and-strs.rs:47:5 44 let plain_str = "Hello"; 45 let str_in_struct = Foo { inner: "Hello" }; 46 let str_in_tuple = ("Hello", "World"); -> 47 zzz(); // #break 48 } 49 50 fn zzz() { (lldb) frame var (alloc::string::String) plain_string = "Hello" { vec = size=5 { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } (&str) plain_str = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } (strings_and_strs::Foo) str_in_struct = { inner = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } ((&str, &str)) str_in_tuple = { 0 = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } 1 = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } } ```
2 parents bfa3635 + 330ce83 commit e897bdf

File tree

6 files changed

+77
-13
lines changed

6 files changed

+77
-13
lines changed
 

‎library/std/src/sys/pal/unix/alloc.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,9 @@ unsafe impl GlobalAlloc for System {
5959
}
6060

6161
cfg_if::cfg_if! {
62-
// We use posix_memalign wherever possible, but not all targets have that function.
62+
// We use posix_memalign wherever possible, but some targets have very incomplete POSIX coverage
63+
// so we need a fallback for those.
6364
if #[cfg(any(
64-
target_os = "redox",
65-
target_os = "espidf",
6665
target_os = "horizon",
6766
target_os = "vita",
6867
))] {
@@ -74,12 +73,11 @@ cfg_if::cfg_if! {
7473
#[inline]
7574
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
7675
let mut out = ptr::null_mut();
77-
// We prefer posix_memalign over aligned_malloc since with aligned_malloc,
78-
// implementations are making almost arbitrary choices for which alignments are
79-
// "supported", making it hard to use. For instance, some implementations require the
80-
// size to be a multiple of the alignment (wasi emmalloc), while others require the
81-
// alignment to be at least the pointer size (Illumos, macOS) -- which may or may not be
82-
// standards-compliant, but that does not help us.
76+
// We prefer posix_memalign over aligned_malloc since it is more widely available, and
77+
// since with aligned_malloc, implementations are making almost arbitrary choices for
78+
// which alignments are "supported", making it hard to use. For instance, some
79+
// implementations require the size to be a multiple of the alignment (wasi emmalloc),
80+
// while others require the alignment to be at least the pointer size (Illumos, macOS).
8381
// posix_memalign only has one, clear requirement: that the alignment be a multiple of
8482
// `sizeof(void*)`. Since these are all powers of 2, we can just use max.
8583
let align = layout.align().max(crate::mem::size_of::<usize>());

‎src/etc/lldb_lookup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def synthetic_lookup(valobj, dict):
9292
return StdVecSyntheticProvider(valobj, dict)
9393
if rust_type == RustType.STD_VEC_DEQUE:
9494
return StdVecDequeSyntheticProvider(valobj, dict)
95-
if rust_type == RustType.STD_SLICE:
95+
if rust_type == RustType.STD_SLICE or rust_type == RustType.STD_STR:
9696
return StdSliceSyntheticProvider(valobj, dict)
9797

9898
if rust_type == RustType.STD_HASH_MAP:

‎src/etc/lldb_providers.py

+3
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ def StdStrSummaryProvider(valobj, dict):
159159
# logger = Logger.Logger()
160160
# logger >> "[StdStrSummaryProvider] for " + str(valobj.GetName())
161161

162+
# the code below assumes non-synthetic value, this makes sure the assumption holds
163+
valobj = valobj.GetNonSyntheticValue()
164+
162165
length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
163166
if length == 0:
164167
return '""'

‎tests/debuginfo/empty-string.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
// lldb-check:[...] empty_string = "" { vec = size=0 }
2424

2525
// lldb-command:fr v empty_str
26-
// lldb-check:[...] empty_str = "" { data_ptr = [...] length = 0 }
26+
// lldb-check:[...] empty_str = ""
2727

2828
fn main() {
2929
let empty_string = String::new();

‎tests/debuginfo/pretty-slices.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
// lldb-check:(&mut [i32]) mut_slice = size=4 { [0] = 2 [1] = 3 [2] = 5 [3] = 7 }
2828

2929
// lldb-command:v str_slice
30-
// lldb-check:(&str) str_slice = "string slice" { data_ptr = [...] length = 12 }
30+
// lldb-check:(&str) str_slice = "string slice" { [0] = 's' [1] = 't' [2] = 'r' [3] = 'i' [4] = 'n' [5] = 'g' [6] = ' ' [7] = 's' [8] = 'l' [9] = 'i' [10] = 'c' [11] = 'e' }
3131

3232
// lldb-command:v mut_str_slice
33-
// lldb-check:(&mut str) mut_str_slice = "mutable string slice" { data_ptr = [...] length = 20 }
33+
// lldb-check:(&mut str) mut_str_slice = "mutable string slice" { [0] = 'm' [1] = 'u' [2] = 't' [3] = 'a' [4] = 'b' [5] = 'l' [6] = 'e' [7] = ' ' [8] = 's' [9] = 't' [10] = 'r' [11] = 'i' [12] = 'n' [13] = 'g' [14] = ' ' [15] = 's' [16] = 'l' [17] = 'i' [18] = 'c' [19] = 'e' }
3434

3535
fn b() {}
3636

‎tests/debuginfo/strings-and-strs.rs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//@ min-gdb-version: 14.0
2+
//@ min-lldb-version: 1800
3+
4+
//@ compile-flags:-g
5+
6+
// === GDB TESTS ===================================================================================
7+
// gdb-command:run
8+
9+
// gdb-command:print plain_string
10+
// gdbr-check:$1 = alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: core::ptr::non_null::NonNull<u8> {pointer: 0x55555555ab80}, _marker: core::marker::PhantomData<u8>}, cap: alloc::raw_vec::Cap (5), alloc: alloc::alloc::Global}, len: 5}}
11+
12+
// gdb-command:print plain_str
13+
// gdbr-check:$2 = "Hello"
14+
15+
// gdb-command:print str_in_struct
16+
// gdbr-check:$3 = strings_and_strs::Foo {inner: "Hello"}
17+
18+
// gdb-command:print str_in_tuple
19+
// gdbr-check:$4 = ("Hello", "World")
20+
21+
// gdb-command:print str_in_rc
22+
// gdbr-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull<alloc::rc::RcBox<&str>> {pointer: 0x55555555aae0}, phantom: core::marker::PhantomData<alloc::rc::RcBox<&str>>, alloc: alloc::alloc::Global}
23+
24+
25+
// === LLDB TESTS ==================================================================================
26+
// lldb-command:run
27+
// lldb-command:v plain_string
28+
// lldbg-check:(alloc::string::String) plain_string = "Hello" { vec = size=5 { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } }
29+
30+
// lldb-command:v plain_str
31+
// lldbg-check:(&str) plain_str = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' }
32+
33+
// lldb-command:v str_in_struct
34+
// lldbg-check:((&str, &str)) str_in_tuple = { 0 = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } 1 = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } }
35+
36+
// lldb-command:v str_in_tuple
37+
// lldbg-check:((&str, &str)) str_in_tuple = { 0 = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } 1 = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } }
38+
39+
// lldb-command:v str_in_rc
40+
// lldbg-check:(alloc::rc::Rc<&str, alloc::alloc::Global>) str_in_rc = strong=1, weak=0 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } }
41+
42+
43+
#![allow(unused_variables)]
44+
#![feature(omit_gdb_pretty_printer_section)]
45+
#![omit_gdb_pretty_printer_section]
46+
47+
pub struct Foo<'a> {
48+
inner: &'a str,
49+
}
50+
51+
fn main() {
52+
let plain_string = String::from("Hello");
53+
let plain_str = "Hello";
54+
let str_in_struct = Foo { inner: "Hello" };
55+
let str_in_tuple = ("Hello", "World");
56+
57+
let str_in_rc = std::rc::Rc::new("Hello");
58+
zzz(); // #break
59+
}
60+
61+
fn zzz() {
62+
()
63+
}

0 commit comments

Comments
 (0)
Please sign in to comment.