Skip to content

debuginfo: Support for std::collections::Hash* in windows debuggers. #66597

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

Merged
merged 1 commit into from
Nov 23, 2019
Merged
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
1 change: 1 addition & 0 deletions src/bootstrap/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ impl Step for DebuggerScripts {
cp_debugger_script("natvis/intrinsic.natvis");
cp_debugger_script("natvis/liballoc.natvis");
cp_debugger_script("natvis/libcore.natvis");
cp_debugger_script("natvis/libstd.natvis");
} else {
cp_debugger_script("debugger_pretty_printers_common.py");

Expand Down
102 changes: 102 additions & 0 deletions src/etc/natvis/libstd.natvis
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<!--
std::collection::Hash* container visualizers

Current std impls:
std::collections::hash::set::HashSet<K, S> is implemented in terms of...
std::collections::hash::map::HashMap<K, V, S> is implemented in terms of...
hashbrown::map::HashMap<K, V, S> is implemented in terms of...
hashbrown::raw::RawTable<(K, V)>

Ideally, we'd teach rustc to scan dependencies/crates for .natvis files so
the bulk of this could live alongside the hashbrown crate implementation,
and std would just forward using e.g. <ExpandedItem>base</ExpandedItem>.

However, Given that std...Hash*Set* is currently implemented in terms of
hashbrown...Hash*Map*, which would visualize poorly, we want to customize the
look/feel at the std type level *anyways*...

References:
https://github.com/rust-lang/rust/blob/master/src/libstd/collections/hash/map.rs
https://github.com/rust-lang/rust/blob/master/src/libstd/collections/hash/set.rs
https://github.com/rust-lang/hashbrown/blob/master/src/map.rs
https://github.com/rust-lang/hashbrown/blob/master/src/set.rs
https://github.com/rust-lang/hashbrown/blob/master/src/raw/mod.rs
-->

<Type Name="std::collections::hash::map::HashMap&lt;*,*,*&gt;">
<DisplayString>{{ size={base.table.items} }}</DisplayString>
<Expand>
<Item Name="[size]">base.table.items</Item>
<Item Name="[capacity]">base.table.items + base.table.growth_left</Item>

<CustomListItems>
<Variable Name="i" InitialValue="0" />
<Variable Name="n" InitialValue="base.table.items" />
<Size>base.table.items</Size>
<Loop>
<Break Condition="n == 0" />
<If Condition="(base.table.ctrl.pointer[i] &amp; 0x80) == 0">
<!-- Bucket is populated -->
<Exec>n--</Exec>
<Item Name="{base.table.data.pointer[i].__0}">base.table.data.pointer[i].__1</Item>
</If>
<Exec>i++</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>

<Type Name="std::collections::hash::set::HashSet&lt;*,*&gt;">
<DisplayString>{{ size={map.base.table.items} }}</DisplayString>
<Expand>
<Item Name="[size]">map.base.table.items</Item>
<Item Name="[capacity]">map.base.table.items + map.base.table.growth_left</Item>

<CustomListItems>
<Variable Name="i" InitialValue="0" />
<Variable Name="n" InitialValue="map.base.table.items" />
<Size>map.base.table.items</Size>
<Loop>
<Break Condition="n == 0" />
<If Condition="(map.base.table.ctrl.pointer[i] &amp; 0x80) == 0">
<!-- Bucket is populated -->
<Exec>n--</Exec>
<Item>map.base.table.data.pointer[i].__0</Item>
</If>
<Exec>i++</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>

<Type Name="hashbrown::raw::RawTable&lt;*&gt;">
<!-- RawTable has a nice and simple layout.
items Number of *populated* values in the RawTable (less than the size of ctrl.pointer / data.pointer)
growth_left Remaining capacity before growth
ctrl.pointer[i] & 0x80 Indicates the bucket is empty / should be skipped / doesn't count towards items.
data.pointer[i] The (K,V) tuple, if not empty.
-->
<DisplayString>{{ size={items} }}</DisplayString>
<Expand>
<Item Name="[size]">items</Item>
<Item Name="[capacity]">items + growth_left</Item>

<CustomListItems>
<Variable Name="i" InitialValue="0" />
<Variable Name="n" InitialValue="items" />
<Size>items</Size>
<Loop>
<Break Condition="n == 0" />
<If Condition="(ctrl.pointer[i] &amp; 0x80) == 0">
<!-- Bucket is populated -->
<Exec>n--</Exec>
<Item>data.pointer[i]</Item>
</If>
<Exec>i++</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>
97 changes: 97 additions & 0 deletions src/test/debuginfo/pretty-std-collections-hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// cdb-only
// compile-flags:-g

// === CDB TESTS ==================================================================================

// cdb-command: g

// cdb-command: dx hash_set,d
// cdb-check:hash_set,d [...] : { size=15 } [Type: [...]::HashSet<u64, [...]>]
// cdb-check: [size] : 15 [Type: [...]]
// cdb-check: [capacity] : [...]
// cdb-check: [[...]] [...] : 0 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 1 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 2 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 3 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 4 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 5 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 6 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 7 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 8 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 9 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 10 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 11 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 12 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 13 [Type: unsigned __int64]
// cdb-command: dx hash_set,d
// cdb-check: [[...]] [...] : 14 [Type: unsigned __int64]

// cdb-command: dx hash_map,d
// cdb-check:hash_map,d [...] : { size=15 } [Type: [...]::HashMap<u64, u64, [...]>]
// cdb-check: [size] : 15 [Type: [...]]
// cdb-check: [capacity] : [...]
// cdb-check: ["0x0"] : 0 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x1"] : 1 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x2"] : 2 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x3"] : 3 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x4"] : 4 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x5"] : 5 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x6"] : 6 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x7"] : 7 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x8"] : 8 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0x9"] : 9 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0xa"] : 10 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0xb"] : 11 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0xc"] : 12 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0xd"] : 13 [Type: unsigned __int64]
// cdb-command: dx hash_map,d
// cdb-check: ["0xe"] : 14 [Type: unsigned __int64]

#![allow(unused_variables)]
use std::collections::HashSet;
use std::collections::HashMap;


fn main() {
// HashSet
let mut hash_set = HashSet::new();
for i in 0..15 {
hash_set.insert(i as u64);
}

// HashMap
let mut hash_map = HashMap::new();
for i in 0..15 {
hash_map.insert(i as u64, i as u64);
}

zzz(); // #break
}

fn zzz() { () }