2

I would like to write a natvis visualizer for a double linked list. The list does not have a count node stored, and the easy way does not work very well, as the expansion never stops (next is never null, the last item of the list is pointing to the list root).

<Type Name="TListBidir&lt;*&gt;">
    <Expand>
        <LinkedListItems>
            <HeadPointer>next</HeadPointer>
            <NextPointer>next</NextPointer>
            <ValueNode>($T1 *)this</ValueNode>
        </LinkedListItems>
    </Expand>
</Type>

I hoped I will be able to add a Condition attribute the the NextPointer comparing it with the list head, but as the NextPoint is evaluated in the context of the node, I do not know what to compare it with:

<NextPointer Condition="next!=XXXXXXXXX">next</NextPointer>

This is how it looked like with previous (2010) visualizers, using the skip directive, as the #list was handling this automatically:

#list is protected against infinite traversals and will cope gracefully with a circular list. Also, you can use a skip: expression to denote a sentinel node that should not be reported. Although the name implies that the node will be skipped, it actually causes traversal to stop, so if your sentinel node is first you should start traversal after it.

TListBidir<*,*,*>{
    children
    (
      #list(
        head: ((($T2 *)&$c)->next),
        next: next,
        skip : &($c)
      ): (($T1 *)(&$e))
    )
}

How can I explain in the natvis to the debugger it should stop expanding the list once it reaches the root element again?

mr NAE
  • 3,144
  • 1
  • 15
  • 35
Suma
  • 33,181
  • 16
  • 123
  • 191

3 Answers3

4

I had a similar problem, not with a circular list, but with a sentinel node at the end that pointed at itself, and came up with an interesting solution that might be adaptable to your needs: You could use the ternary operator to fake out a real termination. The expressions inside <NextPointer> can be anything you can write in vanilla C, so you can do real computation in there (but sadly, no recursion).

(Note that you're not allowed to put a Condition attribute on <NextPointer>, so the ternary operator is the only way to accomplish conditions there.)

So in my case, the list terminated like this:

<LinkedListItems>
    <HeadPointer>this</HeadPointer>
    <NextPointer>next != this ? next : 0</NextPointer>
    <ValueNode>items</ValueNode>
</LinkedListItems>

In your case, if the nodes each have a pointer to their container, you can use that to compare against the head node:

<LinkedListItems>
    <HeadPointer>container-&gt;head</HeadPointer>
    <NextPointer>next != container-&gt;head ? next : 0</NextPointer>
    <ValueNode>items</ValueNode>
</LinkedListItems>

Or, without the &gt; entities and written as more traditional C, that's equivalent to:

next != container->head ? next : NULL

If you don't have some kind of container back-pointer, though, you're probably out of luck on this, since there's no way by looking at only a single node in a circularly-linked list to answer whether it's effectively the "last" node.

Sean Werkema
  • 5,810
  • 2
  • 38
  • 42
  • Um, don't you have the ternary backwards? Shouldn't it be `next != container->head ? next : 0`? Or perhaps I'm not understanding what you are doing? – Adrian Mar 31 '16 at 23:05
  • Yeah, it was backwards in this version; I'll correct it. The actual code I use is much more complex, because the data structure is much more complex, but after a few additional pointer indirections still loosely boils down to a circularly-linked list. I messed up extracting out the core logic from it for this posting. – Sean Werkema Apr 02 '16 at 23:23
  • I don't know about VS2012, but in VS2013 if the list eventually hits the pointer described in ``, it will stop the list as well, just as if `` pointed at nullptr. – Adrian Apr 04 '16 at 20:14
4

You can do this with a CustomListItems element:

<CustomListItems>
    <Variable Name="orig_head" InitialValue="head"/>
    <Variable Name="iter" InitialValue="first_elem"/>
    <Loop>
        <Break Condition="iter == orig_head || iter == 0"/>
        <Item>*iter</Item>
        <Exec>iter = iter-&gt;next_elem</Exec>
    </Loop>
</CustomListItems>

CustomListItems allows you to save the head in a variable so it can be used while traversing the list. If your head has a different type then the list nodes you will need to cast it to the node type.

Marc Aldorasi
  • 698
  • 7
  • 13
  • This is definitely the right way, since it clearly expresses what you're actually doing to visualize the type without any tricks. There's a little more info in [Microsoft's docs](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022#customlistitems-expansion), but honestly the schema does most of the heavy lifting (which shows up as tooltips and autocomplete in Visual Studio.) – John Neuhaus Aug 23 '23 at 17:07
2

The natvis framework does not currently support circular linked lists without a count provided. If you provide a count, it should work. However, without a count, there is no good way to prevent the expansion from just continuing on forever.