Custom Debug Visualizers
When working with custom data structures, the debugger can show too much information layered through how you’ve constructed it. To make it easier to debug, you can create a custom visualizer which will present only the information you want, to make it easier to debug.
Here’s an example of a custom *.natvis file:
<?xml version=”1.0″ encoding=”utf-8″?> <AutoVisualizer xmlns=”http://schemas.microsoft.com/vstudio/debugger/natvis/2010″> <Type Name=”__node”> <DisplayString Condition=”nType==0″>Unknown | {psAName,na} | {psWName,na}</DisplayString> <DisplayString Condition=”nType==1″>String A | {psAName,na} | {psWName,na} | {psAValue,na}</DisplayString> <DisplayString Condition=”nType==2″>Int32 | {psAName,na} | {psWName,na} | {nValue}</DisplayString> <DisplayString Condition=”nType==3″>Double | {psAName,na} | {psWName,na} | {dfValue}</DisplayString> <DisplayString Condition=”nType==4″>Linked List | {psAName,na} | {psWName,na}</DisplayString> <DisplayString Condition=”nType==5″>Hash | {psAName,na} | {psWName,na}</DisplayString> <DisplayString Condition=”nType==6″>Int64 | {psAName,na} | {psWName,na} | {n64Value}</DisplayString> <DisplayString Condition=”nType==8″>Binary Data | {psAName,na} | {psWName,na}</DisplayString> <DisplayString Condition=”nType==9″>String W | {psAName,na} | {psWName,na} | {psWValue,na}</DisplayString> <DisplayString>Uninitialized</DisplayString> <Expand> <Item Name=”Name A”>psAName,na</Item> <Item Name=”Name W”>psWName,na</Item> <Item Name=”Value” Condition=”nType==1″>psAValue,na</Item> <Item Name=”Value” Condition=”nType==2″>nValue</Item> <Item Name=”Value” Condition=”nType==3″>dfValue</Item> <Item Name=”Value” Condition=”nType==6″>n64Value</Item> <Item Name=”Value” Condition=”nType==9″>psWValue,na</Item> <LinkedListItems Condition=”nType==4″> <HeadPointer>pnListHead</HeadPointer> <NextPointer>pnNext</NextPointer> <ValueNode>this</ValueNode> </LinkedListItems> <CustomListItems Condition=”nType==5″> <Variable Name=”bucket” InitialValue=”ppnHashHeads” /> <Variable Name=”bucketCount” InitialValue=”nHashBuckets” /> <Variable Name=”bucketI” InitialValue=”0″ /> <Variable Name=”element” InitialValue=”bucket[0]” /> <Loop> <Break Condition=”bucketI>=bucketCount” /> <Exec>element = bucket[bucketI]</Exec> <Loop> <Break Condition=”element==0″/> <Item>element</Item> <Exec>element = element->pnNext</Exec> </Loop> <Exec>bucketI++</Exec> </Loop> </CustomListItems> <Synthetic Name=”Buckets” Condition=”nType==5″> <Expand> <ArrayItems> <Size>nHashBuckets</Size> <ValuePointer>ppnHashHeads</ValuePointer> </ArrayItems> </Expand> </Synthetic> <Item Name=”Next”>pnNext,na</Item> </Expand> </Type> </AutoVisualizer>
Let’s look at a few elements here:
- The DisplayString commands are the synopsis when the structure is closed in the debugger. In this example we have differing views of what should be shown depending on the state of the object. Curly brackets are used to induce values directly into the description, with the na flag removing pointer addresses to make it easier to read.
- The Expand section gives the full details of what should be shown when you want to expand and see all items in the structure.
- Items and LinkedListItems are predefined sets which allow you to specify what you want shown, without having to describe the how to do it.
- If there is no predefined set, you can write a custom script to do it. This example works for a bucketed hash, so you can see all the objects inside it.
If you’re using a previous version of Visual Studio, you’ll need to modify your autoexp.dat file to achieve the same results.
;node_t* visualizer __node { preview( #switch($e.nType) #case 0(#( ;Unknown ”Unknown | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 1(#( ;String A ”String A | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 2(#( ;Int32 ”Int32 | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 3(#( ;Double ”Double | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 4(#( ;Linked List ”Linked List | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 5(#( ;Hash of name->value ”Hash | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 6(#( ;Int64 ”Int64 | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 8(#( ;Binary Data ”Binary Data | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #case 9(#( ;String W ”String W | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) #default(#( ;other/undefined/unitialized/corrupted ”other/undefined/unitialized/corrupted | ”, [$c.psAName, s], ” | ”, [$c.psWName, su] )) ) children( #switch($e.nType) #case 0(#( ;Unknown Name A: [$c.psAName], Name W: [$c.psWName], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #case 1(#( ;String A Name A: [$c.psAName], Name W: [$c.psWName], Value: [$c.psAValue], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #case 2(#( ;Int32 Name A: [$c.psAName], Name W: [$c.psWName], Value: [$c.nValue], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #case 3(#( ;Double Name A: [$c.psAName], Name W: [$c.psWName], Value: [$c.dfValue], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #case 4(#( ;Linked List Name A: [$c.psAName], Name W: [$c.psWName], List Size: [$c.nListElements], [actual members]: [$e,!], #list( head: $c.pnListHead, size: $c.nListElements, next: pnNext ) )) #case 5(#( ;Hash of name->value Name A: [$c.psAName], Name W: [$c.psWName], Number of Buckets: [$c.nHashBuckets], Number of Elements: [$c.nHashElements], [actual members]: [$e,!], #array( expr: $c.ppnHashHeads[$i], size: $c.nHashBuckets ), #list( head: $c.pnNext, next: pnNext ) )) #case 6(#( ;Int64 Name A: [$c.psAName], Name W: [$c.psWName], Value: [$c.n64Value], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #case 8(#( ;Binary Data Name A: [$c.psAName], Name W: [$c.psWName], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #case 9(#( ;String W Name A: [$c.psAName], Name W: [$c.psWName], Value: [$c.psWValue], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) #default(#( ;other/undefined/unitialized/corrupted Type: [$c.nType], Name A: [$c.psAName], Name W: [$c.psWName], [actual members]: [$e,!], #list( head: $c.pnNext, next: pnNext ) )) ) }
Further Reading: