Skip to content

Commit

Permalink
Auto merge of rust-lang#124500 - VladimirMakaev:lldb-str-formatters, …
Browse files Browse the repository at this point in the history
…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 rust-lang#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'
  }
}
```
  • Loading branch information
bors committed May 11, 2024
2 parents ef15976 + 5b2be36 commit e76034c
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/etc/lldb_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def synthetic_lookup(valobj, dict):
return StdVecSyntheticProvider(valobj, dict)
if rust_type == RustType.STD_VEC_DEQUE:
return StdVecDequeSyntheticProvider(valobj, dict)
if rust_type == RustType.STD_SLICE:
if rust_type == RustType.STD_SLICE or rust_type == RustType.STD_STR:
return StdSliceSyntheticProvider(valobj, dict)

if rust_type == RustType.STD_HASH_MAP:
Expand Down
3 changes: 3 additions & 0 deletions src/etc/lldb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ def StdStrSummaryProvider(valobj, dict):
# logger = Logger.Logger()
# logger >> "[StdStrSummaryProvider] for " + str(valobj.GetName())

# the code below assumes non-synthetic value, this makes sure the assumption holds
valobj = valobj.GetNonSyntheticValue()

length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
if length == 0:
return '""'
Expand Down
2 changes: 1 addition & 1 deletion tests/debuginfo/empty-string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
// lldb-check:[...] empty_string = "" { vec = size=0 }

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

fn main() {
let empty_string = String::new();
Expand Down
4 changes: 2 additions & 2 deletions tests/debuginfo/pretty-slices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
// lldb-check:(&mut [i32]) mut_slice = size=4 { [0] = 2 [1] = 3 [2] = 5 [3] = 7 }

// lldb-command:v str_slice
// lldb-check:(&str) str_slice = "string slice" { data_ptr = [...] length = 12 }
// 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' }

// lldb-command:v mut_str_slice
// lldb-check:(&mut str) mut_str_slice = "mutable string slice" { data_ptr = [...] length = 20 }
// 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' }

fn b() {}

Expand Down
62 changes: 62 additions & 0 deletions tests/debuginfo/strings-and-strs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Require a gdb or lldb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2
//@ min-lldb-version: 1800

//@ compile-flags:-g

// === GDB TESTS ===================================================================================
// gdb-command:run

// gdb-command:print plain_string
// gdbr-check:$1 = "Hello"

// gdb-command:print plain_str
// gdbr-check:$2 = "Hello"

// gdb-command:print str_in_struct
// gdbr-check:$3 = strings_and_strs::Foo {inner: "Hello"}

// gdb-command:print str_in_tuple
// gdbr-check:$4 = ("Hello", "World")

// gdb-command:print str_in_rc
// gdbr-check:$5 = Rc(strong=1, weak=0) = {value = "Hello", strong = 1, weak = 0}


// === LLDB TESTS ==================================================================================
// lldb-command:run
// lldb-command:v plain_string
// lldbg-check:(alloc::string::String) plain_string = "Hello" { vec = size=5 { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } }

// lldb-command:v plain_str
// lldbg-check:(&str) plain_str = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' }

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

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

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


#![allow(unused_variables)]

pub struct Foo<'a> {
inner: &'a str,
}

fn main() {
let plain_string = String::from("Hello");
let plain_str = "Hello";
let str_in_struct = Foo { inner: "Hello" };
let str_in_tuple = ("Hello", "World");

let str_in_rc = std::rc::Rc::new("Hello");
zzz(); // #break
}

fn zzz() {
()
}

0 comments on commit e76034c

Please sign in to comment.