Skip to content
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

Vec, LinkedList, VecDeque, String, and Option NatVis visualizations #39843

Merged
merged 1 commit into from
Feb 16, 2017

Conversation

AndrewGaspar
Copy link
Contributor

I've added some basic NatVis visualizations for core Rust collections and types. This helps address a need filed in issue #36503. NatVis visualizations are similar to gdb/lldb pretty printers, but for windbg and the Visual Studio debugger on Windows.

For example, Vec without the supplied NatVis looks like this in windbg using the "dx" command:

0:000> dx some_64_bit_vec
some_64_bit_vec                 [Type: collections::vec::Vec<u64>]
    [+0x000] buf              [Type: alloc::raw_vec::RawVec<u64>]
    [+0x010] len              : 0x4 [Type: unsigned __int64]

With the NatVis, the elements of the Vec are displayed:

0:000> dx some_64_bit_vec
some_64_bit_vec                 : { size=0x4 } [Type: collections::vec::Vec<u64>]
    [<Raw View>]     [Type: collections::vec::Vec<u64>]
    [size]           : 0x4 [Type: unsigned __int64]
    [capacity]       : 0x4 [Type: unsigned __int64]
    [0]              : 0x4 [Type: unsigned __int64]
    [1]              : 0x4f [Type: unsigned __int64]
    [2]              : 0x1a [Type: unsigned __int64]
    [3]              : 0x184 [Type: unsigned __int64]

In fact, the vector can be treated as an array by the NatVis expression evaluator:

0:000> dx some_64_bit_vec[2]
some_64_bit_vec[2] : 0x1a [Type: unsigned __int64]

In general, it works with any NatVis command that understands collections, such as NatVis LINQ expressions:

0:000> dx some_64_bit_vec.Select(x => x * 2)
some_64_bit_vec.Select(x => x * 2)                
    [0]              : 0x8
    [1]              : 0x9e
    [2]              : 0x34
    [3]              : 0x308

std::string::String is implemented, as well:

0:000> dv
    hello_world = "Hello, world!"
          empty = ""
            new = ""
0:000> dx hello_world
hello_world                 : "Hello, world!" [Type: collections::string::String]
    [<Raw View>]     [Type: collections::string::String]
    [size]           : 0xd [Type: unsigned __int64]
    [capacity]       : 0xd [Type: unsigned __int64]
    [0]              : 72 'H' [Type: char]
    [1]              : 101 'e' [Type: char]
...
    [12]             : 33 '!' [Type: char]
0:000> dx empty
empty                 : "" [Type: collections::string::String]
    [<Raw View>]     [Type: collections::string::String]
    [size]           : 0x0 [Type: unsigned __int64]
    [capacity]       : 0x0 [Type: unsigned __int64]

VecDeque and LinkedList are also implemented.

My biggest concern is the implementation for Option due to the different layouts it can receive based on whether the sentinel value can be embedded with-in the Some value or must be stored separately.

It seems to work, but my testing isn't exhaustive:

0:000> dv
          three = { Some 3 }
           none = { None }
         no_str = { None }
       some_str = { Some "Hello!" }
0:000> dx three
three                 : { Some 3 } [Type: core::option::Option<i32>]
    [<Raw View>]     [Type: core::option::Option<i32>]
    [size]           : 0x1 [Type: ULONG]
    [value]          : 3 [Type: int]
    [0]              : 3 [Type: int]
0:000> dx none
none                 : { None } [Type: core::option::Option<i32>]
    [<Raw View>]     [Type: core::option::Option<i32>]
    [size]           : 0x0 [Type: ULONG]
    [value]          : 4 [Type: int]
0:000> dx no_str
no_str                 : { None } [Type: core::option::Option<collections::string::String>]
    [<Raw View>]     [Type: core::option::Option<collections::string::String>]
    [size]           : 0x0 [Type: ULONG]
0:000> dx some_str
some_str                 : { Some "Hello!" } [Type: core::option::Option<collections::string::String>]
    [<Raw View>]     [Type: core::option::Option<collections::string::String>]
    [size]           : 0x1 [Type: ULONG]
    [value]          : 0x4673df710 : "Hello!" [Type: collections::string::String *]
    [0]              : "Hello!" [Type: collections::string::String]

For now all of these visualizations work in windbg, but I've only gotten the visualizations in libcore.natvis working in the VS debugger. My priority is windbg, but somebody else may be interested in investigating the issues related to VS.

You can load these visualizations into a windbg sessions using the .nvload command:

0:000> .nvload ..\rust\src\etc\natvis\libcollections.natvis; .nvload ..\rust\src\etc\natvis\libcore.natvis
Successfully loaded visualizers in "..\rust\src\etc\natvis\libcollections.natvis"
Successfully loaded visualizers in "..\rust\src\etc\natvis\libcore.natvis"

There are some issues with the symbols that Rust and LLVM conspire to emit into the PDB that inhibit debugging in windbg generally, and by extension make writing visualizations more difficult. Additionally, there are some bugs in windbg itself that complicate or disable some use of the NatVis visualizations for Rust. Significantly, due to NatVis limitations in windbg around allowable type names, you cannot write a visualization for [T] or str. I'll report separate issues as I isolate them.

In the near term, I hope to fill out these NatVis files with more of Rust's core collections and types. In the long run, I hope that we can ship NatVis files with crates and streamline their deployment when debugging Rust programs on windows.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

</Expand>
</Type>
<Type Name="collections::string::String">
<DisplayString>{*(char**)this,[vec.len]}</DisplayString>
Copy link
Contributor Author

@AndrewGaspar AndrewGaspar Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a windbg bug where referencing any symbol dependent on i8 or u8 causes windbg to be unable to interpret the expression. So, right now this relies on the pointer in the String to be the first member of the structure. #36646 has more details on this bug.

</ArrayItems>
</Expand>
</Type>
<Type Name="core::option::Option&lt;*&gt;" Priority="MediumLow">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the Priority here basically means "if you have problems with a higher priority visualization, come to me." The higher priority visualization relies on the RUST$ENUM$DISR member being present. If not, the code currently assumes that it's a cost-saving Enum where the NIL value is embedded within the type. This check is almost certainly wrong. Right now it's checking "are the first sizeof(PVOID) bytes of this structure 0"?

@brson
Copy link
Contributor

brson commented Feb 15, 2017

Since this is simply configuration files for now, I'm happy to land and let you iterate on windbg support. Thanks for working on it.

@bors r+

cc @vadimcn

@bors
Copy link
Contributor

bors commented Feb 15, 2017

📌 Commit a8b7b28 has been approved by brson

frewsxcv added a commit to frewsxcv/rust that referenced this pull request Feb 15, 2017
Vec, LinkedList, VecDeque, String, and Option NatVis visualizations

I've added some basic [NatVis](https://msdn.microsoft.com/en-us/library/jj620914.aspx) visualizations for core Rust collections and types. This helps address a need filed in issue rust-lang#36503. NatVis visualizations are similar to gdb/lldb pretty printers, but for windbg and the Visual Studio debugger on Windows.

For example, Vec without the supplied NatVis looks like this in windbg using the "dx" command:
```
0:000> dx some_64_bit_vec
some_64_bit_vec                 [Type: collections::vec::Vec<u64>]
    [+0x000] buf              [Type: alloc::raw_vec::RawVec<u64>]
    [+0x010] len              : 0x4 [Type: unsigned __int64]
```

With the NatVis, the elements of the Vec are displayed:
```
0:000> dx some_64_bit_vec
some_64_bit_vec                 : { size=0x4 } [Type: collections::vec::Vec<u64>]
    [<Raw View>]     [Type: collections::vec::Vec<u64>]
    [size]           : 0x4 [Type: unsigned __int64]
    [capacity]       : 0x4 [Type: unsigned __int64]
    [0]              : 0x4 [Type: unsigned __int64]
    [1]              : 0x4f [Type: unsigned __int64]
    [2]              : 0x1a [Type: unsigned __int64]
    [3]              : 0x184 [Type: unsigned __int64]
```

In fact, the vector can be treated as an array by the NatVis expression evaluator:
```
0:000> dx some_64_bit_vec[2]
some_64_bit_vec[2] : 0x1a [Type: unsigned __int64]
```

In general, it works with any NatVis command that understands collections, such as NatVis LINQ expressions:
```
0:000> dx some_64_bit_vec.Select(x => x * 2)
some_64_bit_vec.Select(x => x * 2)
    [0]              : 0x8
    [1]              : 0x9e
    [2]              : 0x34
    [3]              : 0x308
```

std::string::String is implemented, as well:
```
0:000> dv
    hello_world = "Hello, world!"
          empty = ""
            new = ""
0:000> dx hello_world
hello_world                 : "Hello, world!" [Type: collections::string::String]
    [<Raw View>]     [Type: collections::string::String]
    [size]           : 0xd [Type: unsigned __int64]
    [capacity]       : 0xd [Type: unsigned __int64]
    [0]              : 72 'H' [Type: char]
    [1]              : 101 'e' [Type: char]
...
    [12]             : 33 '!' [Type: char]
0:000> dx empty
empty                 : "" [Type: collections::string::String]
    [<Raw View>]     [Type: collections::string::String]
    [size]           : 0x0 [Type: unsigned __int64]
    [capacity]       : 0x0 [Type: unsigned __int64]

```

VecDeque and LinkedList are also implemented.

My biggest concern is the implementation for Option due to the different layouts it can receive based on whether the sentinel value can be embedded with-in the Some value or must be stored separately.

It seems to work, but my testing isn't exhaustive:
```
0:000> dv
          three = { Some 3 }
           none = { None }
         no_str = { None }
       some_str = { Some "Hello!" }
0:000> dx three
three                 : { Some 3 } [Type: core::option::Option<i32>]
    [<Raw View>]     [Type: core::option::Option<i32>]
    [size]           : 0x1 [Type: ULONG]
    [value]          : 3 [Type: int]
    [0]              : 3 [Type: int]
0:000> dx none
none                 : { None } [Type: core::option::Option<i32>]
    [<Raw View>]     [Type: core::option::Option<i32>]
    [size]           : 0x0 [Type: ULONG]
    [value]          : 4 [Type: int]
0:000> dx no_str
no_str                 : { None } [Type: core::option::Option<collections::string::String>]
    [<Raw View>]     [Type: core::option::Option<collections::string::String>]
    [size]           : 0x0 [Type: ULONG]
0:000> dx some_str
some_str                 : { Some "Hello!" } [Type: core::option::Option<collections::string::String>]
    [<Raw View>]     [Type: core::option::Option<collections::string::String>]
    [size]           : 0x1 [Type: ULONG]
    [value]          : 0x4673df710 : "Hello!" [Type: collections::string::String *]
    [0]              : "Hello!" [Type: collections::string::String]
```

For now all of these visualizations work in windbg, but I've only gotten the visualizations in libcore.natvis working in the VS debugger. My priority is windbg, but somebody else may be interested in investigating the issues related to VS.

You can load these visualizations into a windbg sessions using the .nvload command:
```
0:000> .nvload ..\rust\src\etc\natvis\libcollections.natvis; .nvload ..\rust\src\etc\natvis\libcore.natvis
Successfully loaded visualizers in "..\rust\src\etc\natvis\libcollections.natvis"
Successfully loaded visualizers in "..\rust\src\etc\natvis\libcore.natvis"
```

There are some issues with the symbols that Rust and LLVM conspire to emit into the PDB that inhibit debugging in windbg generally, and by extension make writing visualizations more difficult. Additionally, there are some bugs in windbg itself that complicate or disable some use of the NatVis visualizations for Rust. Significantly, due to NatVis limitations in windbg around allowable type names, you cannot write a visualization for [T] or str. I'll report separate issues as I isolate them.

In the near term, I hope to fill out these NatVis files with more of Rust's core collections and types. In the long run, I hope that we can ship NatVis files with crates and streamline their deployment when debugging Rust programs on windows.
bors added a commit that referenced this pull request Feb 16, 2017
Rollup of 11 pull requests

- Successful merges: #39775, #39793, #39804, #39824, #39834, #39837, #39839, #39840, #39843, #39844, #39846
- Failed merges:
frewsxcv added a commit to frewsxcv/rust that referenced this pull request Feb 16, 2017
Vec, LinkedList, VecDeque, String, and Option NatVis visualizations

I've added some basic [NatVis](https://msdn.microsoft.com/en-us/library/jj620914.aspx) visualizations for core Rust collections and types. This helps address a need filed in issue rust-lang#36503. NatVis visualizations are similar to gdb/lldb pretty printers, but for windbg and the Visual Studio debugger on Windows.

For example, Vec without the supplied NatVis looks like this in windbg using the "dx" command:
```
0:000> dx some_64_bit_vec
some_64_bit_vec                 [Type: collections::vec::Vec<u64>]
    [+0x000] buf              [Type: alloc::raw_vec::RawVec<u64>]
    [+0x010] len              : 0x4 [Type: unsigned __int64]
```

With the NatVis, the elements of the Vec are displayed:
```
0:000> dx some_64_bit_vec
some_64_bit_vec                 : { size=0x4 } [Type: collections::vec::Vec<u64>]
    [<Raw View>]     [Type: collections::vec::Vec<u64>]
    [size]           : 0x4 [Type: unsigned __int64]
    [capacity]       : 0x4 [Type: unsigned __int64]
    [0]              : 0x4 [Type: unsigned __int64]
    [1]              : 0x4f [Type: unsigned __int64]
    [2]              : 0x1a [Type: unsigned __int64]
    [3]              : 0x184 [Type: unsigned __int64]
```

In fact, the vector can be treated as an array by the NatVis expression evaluator:
```
0:000> dx some_64_bit_vec[2]
some_64_bit_vec[2] : 0x1a [Type: unsigned __int64]
```

In general, it works with any NatVis command that understands collections, such as NatVis LINQ expressions:
```
0:000> dx some_64_bit_vec.Select(x => x * 2)
some_64_bit_vec.Select(x => x * 2)
    [0]              : 0x8
    [1]              : 0x9e
    [2]              : 0x34
    [3]              : 0x308
```

std::string::String is implemented, as well:
```
0:000> dv
    hello_world = "Hello, world!"
          empty = ""
            new = ""
0:000> dx hello_world
hello_world                 : "Hello, world!" [Type: collections::string::String]
    [<Raw View>]     [Type: collections::string::String]
    [size]           : 0xd [Type: unsigned __int64]
    [capacity]       : 0xd [Type: unsigned __int64]
    [0]              : 72 'H' [Type: char]
    [1]              : 101 'e' [Type: char]
...
    [12]             : 33 '!' [Type: char]
0:000> dx empty
empty                 : "" [Type: collections::string::String]
    [<Raw View>]     [Type: collections::string::String]
    [size]           : 0x0 [Type: unsigned __int64]
    [capacity]       : 0x0 [Type: unsigned __int64]

```

VecDeque and LinkedList are also implemented.

My biggest concern is the implementation for Option due to the different layouts it can receive based on whether the sentinel value can be embedded with-in the Some value or must be stored separately.

It seems to work, but my testing isn't exhaustive:
```
0:000> dv
          three = { Some 3 }
           none = { None }
         no_str = { None }
       some_str = { Some "Hello!" }
0:000> dx three
three                 : { Some 3 } [Type: core::option::Option<i32>]
    [<Raw View>]     [Type: core::option::Option<i32>]
    [size]           : 0x1 [Type: ULONG]
    [value]          : 3 [Type: int]
    [0]              : 3 [Type: int]
0:000> dx none
none                 : { None } [Type: core::option::Option<i32>]
    [<Raw View>]     [Type: core::option::Option<i32>]
    [size]           : 0x0 [Type: ULONG]
    [value]          : 4 [Type: int]
0:000> dx no_str
no_str                 : { None } [Type: core::option::Option<collections::string::String>]
    [<Raw View>]     [Type: core::option::Option<collections::string::String>]
    [size]           : 0x0 [Type: ULONG]
0:000> dx some_str
some_str                 : { Some "Hello!" } [Type: core::option::Option<collections::string::String>]
    [<Raw View>]     [Type: core::option::Option<collections::string::String>]
    [size]           : 0x1 [Type: ULONG]
    [value]          : 0x4673df710 : "Hello!" [Type: collections::string::String *]
    [0]              : "Hello!" [Type: collections::string::String]
```

For now all of these visualizations work in windbg, but I've only gotten the visualizations in libcore.natvis working in the VS debugger. My priority is windbg, but somebody else may be interested in investigating the issues related to VS.

You can load these visualizations into a windbg sessions using the .nvload command:
```
0:000> .nvload ..\rust\src\etc\natvis\libcollections.natvis; .nvload ..\rust\src\etc\natvis\libcore.natvis
Successfully loaded visualizers in "..\rust\src\etc\natvis\libcollections.natvis"
Successfully loaded visualizers in "..\rust\src\etc\natvis\libcore.natvis"
```

There are some issues with the symbols that Rust and LLVM conspire to emit into the PDB that inhibit debugging in windbg generally, and by extension make writing visualizations more difficult. Additionally, there are some bugs in windbg itself that complicate or disable some use of the NatVis visualizations for Rust. Significantly, due to NatVis limitations in windbg around allowable type names, you cannot write a visualization for [T] or str. I'll report separate issues as I isolate them.

In the near term, I hope to fill out these NatVis files with more of Rust's core collections and types. In the long run, I hope that we can ship NatVis files with crates and streamline their deployment when debugging Rust programs on windows.
bors added a commit that referenced this pull request Feb 16, 2017
Rollup of 12 pull requests

- Successful merges: #39775, #39793, #39804, #39834, #39836, #39839, #39840, #39843, #39844, #39846, #39857, #39861
- Failed merges:
@bors bors merged commit a8b7b28 into rust-lang:master Feb 16, 2017
@AndrewGaspar AndrewGaspar deleted the natvis branch February 24, 2017 04:22
@sophiajt
Copy link
Contributor

@AndrewGaspar - are there any steps we need to do to enable this behaviour? With a recent nightly, I don't see it kick in for things like Vec.

@AndrewGaspar
Copy link
Contributor Author

@jonathandturner You have to manually load the visualizations into windbg right now.

From windbg's command window, run ".nvload ./path/to/libcore.natvis; .nvload ./path/to/libcollections.natvis" and that should load the visualizers.

I have a pull request open for the Rust installer to add a rust-windbg command that will automatically load windbg with these visualizations: #39983

@sophiajt
Copy link
Contributor

@AndrewGaspar - these can just be bundled into the pdb ultimately, right?

@AndrewGaspar
Copy link
Contributor Author

@jonathandturner Yeah, that's right. You can use link.exe's /natvis parameter to bundle the .natvis file into the pdb. I've tested it using rustc.exe -C link-args="/natvis:foo", and that seems to work for getting visualizations in both windbg and VS. As a separate issue, VS's natvis interpreter wasn't interpreting some of the visualizations correctly.

@sophiajt
Copy link
Contributor

@AndrewGaspar - yeah I hadn't realised you were already working on this, but this is much further than I got, which is great.

I noticed some issues with things like &str, which vs just doesn't understand as a type name. There's also an outstanding issue with i8 and u8 until we merge in the latest llvm. Did you see other issues?

Yeah, if it's just a simple step, seems like we should be linking in the natvis on the windows side by default...

@AndrewGaspar
Copy link
Contributor Author

@jonathandturner windbg also has issues with &str. However, I think fixing this could be as easy as getting rust to generate that symbol as "*str" instead, though I don't know how feasible that is. Slices have a similar problem, I think?

I've made the windbg team aware of the i8/u8 issue, but I'm not sure if they're actually going to fix it in a future release. There's a fix for rust at least.

I think the right generic way to solve this would be to package .natvis files with cargo crates and then link all of the program's dependencies into its PE. To start, though, we could just always link in the "core" visualizations. Keep in mind that the link.exe /natvis parameter is not documented.

@sophiajt
Copy link
Contributor

Interesting - I haven't cracked open the pdb and tried to change the &str to a *str to see if your theory is true. I suspect we might just want to call it something different (like maybe str_slice). IIRC it's a pointer and a length.

Yeah, I was thinking about linking the core vis. If people wanted to include additional help, yeah, they could bundle it with their crates. Interesting the /natvis flag isn't document. The natvis docs say ".natvis files embedded in a .pdb you are debugging (unless a file of the same name exists in a loaded project)" which makes me surprised that the way to create a .pdb like that uses perhaps less-supported flags.

Cool work, btw. I've been trying out your natvis, which is definitely more full-featured than mine. Would love to see this continue developing to be a much better VS debug story.

@retep998
Copy link
Member

retep998 commented Apr 17, 2017

Your libcollections.natvis apparently has a few non-breaking spaces in it. #41344

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants