Implement natvis for fancy pointers (#262)

* Add file for manual natvis testing

* Equip natvis file to allow fancy pointers

* Update docs

* [skip ci] add links to natvis docs
This commit is contained in:
Braden Ganetsky
2024-07-15 12:42:28 -05:00
committed by GitHub
parent 3ad8ac4c34
commit 7ddd562532
6 changed files with 382 additions and 48 deletions

View File

@@ -30,18 +30,50 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</Type>
<Type Name="boost::unordered::detail::grouped_bucket_array&lt;*&gt;" Inheritable="false">
<!--
The expression `&**p` is used so the Intrinsic fails to parse for a fancy pointer type.
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
For a raw pointer, this expression is exactly equivalent to `*p`.
For a fancy pointer, this expression will try to call a user-defined `operator*()`, which is not allowed in Natvis, and it will fail.
-->
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<!--
The casting expression `(xyz_pointer)p` is used so the Intrinsic fails to parse for a fancy pointer type.
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
In this case, `(xyz_pointer)p` is either a no-op for a raw pointer type, or it's an invalid expression.
-->
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer)p) + n">
<Parameter Name="p" Type="bucket_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer*)nullptr)->boost_next(p, n)">
<Parameter Name="p" Type="bucket_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Expand>
<CustomListItems MaxItemsPerView="100">
<Variable Name="size" InitialValue="size_" />
<Variable Name="bucket_index" InitialValue="0" />
<Variable Name="current_bucket" InitialValue="&amp;buckets[bucket_index]" />
<Variable Name="node" InitialValue="current_bucket->next" />
<Variable Name="current_bucket" InitialValue="to_address(&amp;buckets)" />
<Variable Name="node" InitialValue="to_address(&amp;current_bucket-&gt;next)" />
<Loop Condition="bucket_index != size">
<Exec>current_bucket = &amp;buckets[bucket_index]</Exec>
<Exec>node = current_bucket-&gt;next</Exec>
<Exec>current_bucket = next(to_address(&amp;buckets), bucket_index)</Exec>
<Exec>node = to_address(&amp;current_bucket-&gt;next)</Exec>
<Loop Condition="node != nullptr">
<Item>node-&gt;buf.t_</Item>
<Exec>node = node-&gt;next</Exec>
<Exec>node = to_address(&amp;node-&gt;next)</Exec>
</Loop>
<Exec>++bucket_index</Exec>
</Loop>
@@ -54,14 +86,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<CustomListItems MaxItemsPerView="100">
<Variable Name="size" InitialValue="size_" />
<Variable Name="bucket_index" InitialValue="0" />
<Variable Name="current_bucket" InitialValue="&amp;buckets[bucket_index]" />
<Variable Name="node" InitialValue="current_bucket->next" />
<Variable Name="current_bucket" InitialValue="to_address(&amp;buckets)" />
<Variable Name="node" InitialValue="to_address(&amp;current_bucket-&gt;next)" />
<Loop Condition="bucket_index != size">
<Exec>current_bucket = &amp;buckets[bucket_index]</Exec>
<Exec>node = current_bucket-&gt;next</Exec>
<Exec>current_bucket = next(to_address(&amp;buckets), bucket_index)</Exec>
<Exec>node = to_address(&amp;current_bucket-&gt;next)</Exec>
<Loop Condition="node != nullptr">
<Item Name="[{node-&gt;buf.t_.first}]">node-&gt;buf.t_</Item>
<Exec>node = node-&gt;next</Exec>
<Exec>node = to_address(&amp;node-&gt;next)</Exec>
</Loop>
<Exec>++bucket_index</Exec>
</Loop>
@@ -99,29 +131,46 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Type Name="boost::unordered::detail::iterator_detail::iterator&lt;*&gt;" Inheritable="false">
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator&lt;*&gt;" />
<Intrinsic Name="valid" Expression="p != nullptr &amp;&amp; itb.p != nullptr &amp;&amp; itb.pbg != nullptr" />
<DisplayString Condition="valid()">{p-&gt;buf.t_}</DisplayString>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
</Intrinsic>
<Intrinsic Name="valid" Expression="to_address(&amp;p) &amp;&amp; to_address(&amp;itb.p) &amp;&amp; to_address(&amp;itb.pbg)" />
<DisplayString Condition="valid()">{to_address(&amp;p)-&gt;buf.t_}</DisplayString>
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
<Expand>
<ExpandedItem Condition="valid()">p-&gt;buf.t_</ExpandedItem>
<ExpandedItem Condition="valid()">to_address(&amp;p)-&gt;buf.t_</ExpandedItem>
</Expand>
</Type>
<!-- FOA and CFOA helpers -->
<Type Name="boost::unordered::detail::foa::element_type&lt;*&gt;" Priority="Medium" Inheritable="false">
<DisplayString>{*p}</DisplayString>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="pointer*" />
</Intrinsic>
<Intrinsic Name="get" Expression="to_address(&amp;p)" />
<DisplayString>{*get()}</DisplayString>
<Expand>
<ExpandedItem>*p</ExpandedItem>
</Expand>
</Type>
<Type Name="boost::unordered::detail::foa::element_type&lt;std::pair&lt;*&gt;,*&gt;" Priority="MediumHigh" Inheritable="false">
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
<DisplayString>({p-&gt;first}, {p-&gt;second})</DisplayString>
<Expand>
<Item Name="first">p-&gt;first</Item>
<Item Name="second">p-&gt;second</Item>
<ExpandedItem>*get()</ExpandedItem>
</Expand>
</Type>
@@ -227,10 +276,39 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Type Name="boost::unordered::detail::foa::table&lt;*&gt;" Inheritable="false">
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table&lt;*&gt;" />
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer)p) + n">
<Parameter Name="p" Type="arrays_type::value_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer)p) + n">
<Parameter Name="p" Type="unsigned char*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer*)nullptr)->boost_next(p, n)">
<Parameter Name="p" Type="arrays_type::value_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer*)nullptr)->boost_next(p, n)">
<Parameter Name="p" Type="unsigned char*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e">
<Parameter Name="e" Type="value_type*" />
</Intrinsic>
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e-&gt;p">
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e-&gt;get()">
<Parameter Name="e" Type="element_type*" />
</Intrinsic>
@@ -254,8 +332,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Expand>
<Item Name="[stats]" Optional="true">cstats</Item>
<CustomListItems MaxItemsPerView="100">
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(arrays.groups_)" />
<Variable Name="p_" InitialValue="arrays.elements_" />
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(to_address(&amp;arrays.groups_))" />
<Variable Name="p_" InitialValue="to_address(&amp;arrays.elements_)" />
<Variable Name="first_time" InitialValue="true" />
<Variable Name="mask" InitialValue="(int)0" />
<Variable Name="n0" InitialValue="(size_t)0" />
@@ -264,18 +342,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Loop Condition="p_ != nullptr">
<!-- This if block mirrors the condition in the begin() call -->
<If Condition="!first_time || !(arrays.elements_ &amp;&amp; !(arrays.groups_[0].match_occupied() &amp; 0x1))">
<If Condition="!first_time || !(p_ &amp;&amp; !(to_address(&amp;arrays.groups_)[0].match_occupied() &amp; 0x1))">
<Item>*p_</Item>
</If>
<Exec>first_time = false</Exec>
<Exec>n0 = reinterpret_cast&lt;uintptr_t&gt;(pc_) % sizeof(group_type)</Exec>
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
<Exec>mask = (reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied() &gt;&gt; (n0+1)) &lt;&lt; (n0+1)</Exec>
<Loop Condition="mask == 0">
<Exec>pc_ += sizeof(group_type)</Exec>
<Exec>p_ += group_type::N</Exec>
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
<Exec>p_ = next(p_, group_type::N)</Exec>
<Exec>mask = reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied()</Exec>
</Loop>
@@ -284,9 +362,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Exec>p_ = nullptr</Exec>
</If>
<Else>
<Exec>pc_ += (ptrdiff_t)n</Exec>
<Exec>p_ -= (ptrdiff_t)n0</Exec>
<Exec>p_ += (ptrdiff_t)n</Exec>
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
</Else>
</Loop>
@@ -299,8 +376,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Expand>
<Item Name="[stats]" Optional="true">cstats</Item>
<CustomListItems MaxItemsPerView="100">
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(arrays.groups_)" />
<Variable Name="p_" InitialValue="arrays.elements_" />
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(to_address(&amp;arrays.groups_))" />
<Variable Name="p_" InitialValue="to_address(&amp;arrays.elements_)" />
<Variable Name="first_time" InitialValue="true" />
<Variable Name="mask" InitialValue="(int)0" />
<Variable Name="n0" InitialValue="(size_t)0" />
@@ -309,18 +386,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Loop Condition="p_ != nullptr">
<!-- This if block mirrors the condition in the begin() call -->
<If Condition="!first_time || !(arrays.elements_ &amp;&amp; !(arrays.groups_[0].match_occupied() &amp; 0x1))">
<If Condition="!first_time || !(p_ &amp;&amp; !(to_address(&amp;arrays.groups_)[0].match_occupied() &amp; 0x1))">
<Item Name="[{get_value(p_)-&gt;first}]">*p_</Item>
</If>
<Exec>first_time = false</Exec>
<Exec>n0 = reinterpret_cast&lt;uintptr_t&gt;(pc_) % sizeof(group_type)</Exec>
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
<Exec>mask = (reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied() &gt;&gt; (n0+1)) &lt;&lt; (n0+1)</Exec>
<Loop Condition="mask == 0">
<Exec>pc_ += sizeof(group_type)</Exec>
<Exec>p_ += group_type::N</Exec>
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
<Exec>p_ = next(p_, group_type::N)</Exec>
<Exec>mask = reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied()</Exec>
</Loop>
@@ -329,9 +406,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Exec>p_ = nullptr</Exec>
</If>
<Else>
<Exec>pc_ += (ptrdiff_t)n</Exec>
<Exec>p_ -= (ptrdiff_t)n0</Exec>
<Exec>p_ += (ptrdiff_t)n</Exec>
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
</Else>
</Loop>
@@ -369,12 +445,62 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<!-- FOA iterators -->
<Type Name="boost::unordered::detail::foa::table_iterator&lt;*&gt;" Inheritable="false">
<Intrinsic Name="valid" Expression="p_ != nullptr &amp;&amp; pc_ != nullptr" />
<DisplayString Condition="valid()">{*p_}</DisplayString>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="table_element_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="table_element_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="char_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="char_pointer*" />
</Intrinsic>
<Intrinsic Name="valid" Expression="to_address(&amp;p_) != nullptr &amp;&amp; to_address(&amp;pc_) != nullptr" />
<DisplayString Condition="valid()">{*to_address(&amp;p_)}</DisplayString>
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
<Expand>
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
<ExpandedItem Condition="valid()">*to_address(&amp;p_)</ExpandedItem>
</Expand>
</Type>
<!-- Fancy pointer support -->
<!--
To allow your own fancy pointer type to interact with Boost.Unordered Natvis,
add the following intrinsics to your type, with the following conditions.
(Note, this is assuming the presence of a type alias `pointer` for the underlying
raw pointer type, and a type alias `difference_type` for your fancy pointer
difference type. Substitute whichever names are applicable in your case.)
`boost_to_address`
* Takes no parameters
* Returns the raw pointer equivalent to your fancy pointer
`boost_next`
* Parameter 1, an underlying raw pointer of type `pointer`
* Parameter 2, an offset of type `difference_type`
* Returns the raw pointer equivalent to your fancy pointer, as if you did the following operations
1. Convert the incoming raw pointer to your fancy pointer
2. Use operator+= to add the offset to the fancy pointer
3. Convert back to the raw pointer
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above.
Example
```
<Type Name="my_fancy_ptr&lt;*&gt;">
...
<Intrinsic Name="boost_to_address" ReturnType="pointer" Expression="..." />
<Intrinsic Name="boost_next" ReturnType="pointer" Expression="...">
<Parameter Name="ptr" Type="pointer" />
<Parameter Name="offset" Type="difference_type" />
</Intrinsic>
...
</Type>
```
-->
</AutoVisualizer>