HelloWorld
HelloWorld

Reputation: 1863

I am looking for a proper way to display a UUID via NatVis in VS2012

I am looking for a proper way to display a UUID via NatVis in VS2012. My own uuid type uses UUID big-endian internally so a cast to (GUID*) does not work as GUID uses little-endian in Windows. So I always see a misrepresented uuid.

Furthermore any format specifier in Natvis looks not nice because i can't get rid of the 0x in the output when using the hex notation. Any ideas?

Upvotes: 18

Views: 4049

Answers (8)

Roman Schaub
Roman Schaub

Reputation: 126

for anyone looking for a visualizer for Qt's QUuid class (expanding on Tom Whittock's answer):

<Type Name="QUuid">
    <DisplayString>{{{data1,nvoxb}-{data2,nvoxb}-{data3,nvoxb}-{data4[0],nvoxb}{data4[1],nvoxb}-{data4[2],nvoxb}{data4[3],nvoxb}{data4[4],nvoxb}{data4[5],nvoxb}{data4[6],nvoxb}{data4[7],nvoxb}}}</DisplayString>
    <Expand>
      <Item Name="[data1]">data1</Item>
      <Item Name="[data2]">data2</Item>
      <Item Name="[data3]">data3</Item>
      <Item Name="[data4]">data4</Item>
    </Expand>
</Type>

This displays the string with lowercase characters in order to align it with the output of QUuid::toString.

This visualizer works in Visual Studio 2017.

Upvotes: 1

Tom Whittock
Tom Whittock

Reputation: 4230

There's no need for dummy types or anything like that, at least in VS2019.

<Type Name="Guid">
  <DisplayString>{{{m_bytes[0],nvoXb}{m_bytes[1],nvoXb}{m_bytes[2],nvoXb}{m_bytes[3],nvoXb}-{m_bytes[4],nvoXb}{m_bytes[5],nvoXb}-{m_bytes[6],nvoXb}{m_bytes[7],nvoXb}-{m_bytes[8],nvoXb}{m_bytes[9],nvoXb}-{m_bytes[10],nvoXb}{m_bytes[11],nvoXb}{m_bytes[12],nvoXb}{m_bytes[13],nvoXb}{m_bytes[14],nvoXb}{m_bytes[15],nvoXb}}}</DisplayString>
</Type>

assuming the Guid type has an m_bytes member. Other types are similar, but nvo is only really necessary for byte/char values AFAIK.

The display parameters following each element access are as follows:

nvo - numeric value only (i.e. byte/char does not include the ascii representation)

X - capital hexadecimal display

b - 'bare' - no leading 0x

Upvotes: 13

kwaegel
kwaegel

Reputation: 81

Replacing the format specifier ,X} with ,xb} in @Xor's answer above seems to give a result without the 0x prefix in VS2019, and without needing auxiliary structs.

<Type Name="boost::uuids::uuid">
    <DisplayString>{((((uint32_t)data[3] &amp; 0xFF)) + (((uint32_t)data[2] &amp; 0xFF) &lt;&lt; 8) + (((uint32_t)data[1] &amp; 0xFF) &lt;&lt; 16) + (((uint32_t)data[0] &amp; 0xFF) &lt;&lt; 24)),xb} - {((((uint32_t)data[7] &amp; 0xFF)) + (((uint32_t)data[6] &amp; 0xFF) &lt;&lt; 8) + (((uint32_t)data[5] &amp; 0xFF) &lt;&lt; 16) + (((uint32_t)data[4] &amp; 0xFF) &lt;&lt; 24)),xb} - {((((uint32_t)data[11] &amp; 0xFF)) + (((uint32_t)data[10] &amp; 0xFF) &lt;&lt; 8) + (((uint32_t)data[9] &amp; 0xFF) &lt;&lt; 16) + (((uint32_t)data[8] &amp; 0xFF) &lt;&lt; 24)),xb} - {((((uint32_t)data[15] &amp; 0xFF)) + (((uint32_t)data[14] &amp; 0xFF) &lt;&lt; 8) + (((uint32_t)data[13] &amp; 0xFF) &lt;&lt; 16) + (((uint32_t)data[12] &amp; 0xFF) &lt;&lt; 24)),xb}</DisplayString>
</Type>

Upvotes: 4

GaspardP
GaspardP

Reputation: 4832

After searching and attempting several other solutions hoping this could be done without adding dummy types, I ended up using the accepted answer. Unfortunately it is provided for the SHA1 type instead of a UUID type. In the case of UUID I had to be extra careful to output the bytes in proper order when interacting with microsoft UUIDs which are "middle-endian" (i.e. first three groups are little-endian but last two are big-endian). SMBIOS specifications also follow middle-endian ordering.

Setup is identical to what Johan Torp suggests - this must be somewhere in your project, I kept it next to my UUID type:

namespace natvis
{
    struct x4lo { unsigned __int8 v : 4;    unsigned __int8 _ : 4; };
    struct x4hi { unsigned __int8 _ : 4;    unsigned __int8 v : 4; };
    struct x8 { unsigned __int8 _; };
    struct x32 { __int32 _; };
}

In the natvis file:

<Type Name="natvis::x4hi">
  <AlternativeType Name="natvis::x4lo" />
  <DisplayString Condition="v==0">0</DisplayString>
  <DisplayString Condition="v==1">1</DisplayString>
  <DisplayString Condition="v==2">2</DisplayString>
  <DisplayString Condition="v==3">3</DisplayString>
  <DisplayString Condition="v==4">4</DisplayString>
  <DisplayString Condition="v==5">5</DisplayString>
  <DisplayString Condition="v==6">6</DisplayString>
  <DisplayString Condition="v==7">7</DisplayString>
  <DisplayString Condition="v==8">8</DisplayString>
  <DisplayString Condition="v==9">9</DisplayString>
  <DisplayString Condition="v==10">a</DisplayString>
  <DisplayString Condition="v==11">b</DisplayString>
  <DisplayString Condition="v==12">c</DisplayString>
  <DisplayString Condition="v==13">d</DisplayString>
  <DisplayString Condition="v==14">e</DisplayString>
  <DisplayString>f</DisplayString>
</Type>
<Type Name="natvis::x8">
    <DisplayString>{*(natvis::x4hi*)(this)}{*(natvis::x4lo*)(this)}</DisplayString>
</Type>
<Type Name="natvis::x32">
    <DisplayString>{((natvis::x8*)this)[0]}{((natvis::x8*)this)[1]}{((natvis::x8*)this)[2]}{((natvis::x8*)this)[3]}</DisplayString>
</Type>

Assuming your UUID type has a bytes member:

<Type Name="myUUID">
    <!-- Assuming system is in little endian, output as middle-endian UUID -->
    <DisplayString>{(*(natvis::x32*)&amp;bytes[0])}-{(*(natvis::x8*)&amp;bytes[5])}{(*(natvis::x8*)&amp;bytes[4])}-{(*(natvis::x8*)&amp;bytes[7])}{(*(natvis::x8*)&amp;bytes[6])}-{(*(natvis::x8*)&amp;bytes[8])}{(*(natvis::x8*)&amp;bytes[9])}-{(*(natvis::x8*)&amp;bytes[10])}{(*(natvis::x8*)&amp;bytes[11])}{(*(natvis::x8*)&amp;bytes[12])}{(*(natvis::x8*)&amp;bytes[13])}{(*(natvis::x8*)&amp;bytes[14])}{(*(natvis::x8*)&amp;bytes[15])}</DisplayString>
</Type>

Upvotes: 1

Xor
Xor

Reputation: 71

Here a simple solution that does not require any extra dummy structs in code:

<Type Name="boost::uuids::uuid">
   <DisplayString>{((((int32)data[3] &amp; 0xFF)) + (((int32)data[2] &amp; 0xFF) &lt;&lt; 8) + (((int32)data[1] &amp; 0xFF) &lt;&lt; 16) + (((int32)data[0] &amp; 0xFF) &lt;&lt; 24)),X} - {((((int32)data[7] &amp; 0xFF)) + (((int32)data[6] &amp; 0xFF) &lt;&lt; 8) + (((int32)data[5] &amp; 0xFF) &lt;&lt; 16) + (((int32)data[4] &amp; 0xFF) &lt;&lt; 24)),X} - {((((int32)data[11] &amp; 0xFF)) + (((int32)data[10] &amp; 0xFF) &lt;&lt; 8) + (((int32)data[9] &amp; 0xFF) &lt;&lt; 16) + (((int32)data[8] &amp; 0xFF) &lt;&lt; 24)),X} - {((((int32)data[15] &amp; 0xFF)) + (((int32)data[14] &amp; 0xFF) &lt;&lt; 8) + (((int32)data[13] &amp; 0xFF) &lt;&lt; 16) + (((int32)data[12] &amp; 0xFF) &lt;&lt; 24)),X}</DisplayString>
</Type>

It does not show the UUID as nicely as the other solutions, it simply shows it as 4 blocks of 32 bit integers but it does the job:

uuid visualizer

Upvotes: 4

KindDragon
KindDragon

Reputation: 6871

You can try my extension C++ Debugger Visualizers. Version 1.0.16 support boost::uuids::uuid visualizer using AddIn dll.

Upvotes: 1

Johan Torp
Johan Torp

Reputation: 136

Here's a more compact version of ComicSansMS solution. I'm using a SHA1 struct and visualizer as example instead.

struct SHA1 { char hash[20]; };

namespace natvis
{
    struct x4lo { unsigned __int8 v : 4;    unsigned __int8 _ : 4; };
    struct x4hi { unsigned __int8 _ : 4;    unsigned __int8 v : 4; };
    struct x8 { unsigned __int8 _; };
    struct x32 { __int32 _; };
}

natvis

<Type Name="natvis::x4hi">
  <AlternativeType Name="natvis::x4lo" />
  <DisplayString Condition="v==0">0</DisplayString>
  <DisplayString Condition="v==1">1</DisplayString>
  <DisplayString Condition="v==2">2</DisplayString>
  <DisplayString Condition="v==3">3</DisplayString>
  <DisplayString Condition="v==4">4</DisplayString>
  <DisplayString Condition="v==5">5</DisplayString>
  <DisplayString Condition="v==6">6</DisplayString>
  <DisplayString Condition="v==7">7</DisplayString>
  <DisplayString Condition="v==8">8</DisplayString>
  <DisplayString Condition="v==9">9</DisplayString>
  <DisplayString Condition="v==10">a</DisplayString>
  <DisplayString Condition="v==11">b</DisplayString>
  <DisplayString Condition="v==12">c</DisplayString>
  <DisplayString Condition="v==13">d</DisplayString>
  <DisplayString Condition="v==14">e</DisplayString>
  <DisplayString>f</DisplayString>
</Type>
<Type Name="natvis::x8">
    <DisplayString>{*(natvis::x4hi*)(this)}{*(natvis::x4lo*)(this)}</DisplayString>
</Type>
<Type Name="natvis::x32">
    <DisplayString>{((natvis::x8*)this)[0]}{((natvis::x8*)this)[1]}{((natvis::x8*)this)[2]}{((natvis::x8*)this)[3]}</DisplayString>
</Type>
<Type Name="SHA1">
    <DisplayString>{((natvis::x32*)hash)[0]}{((natvis::x32*)hash)[1]}{((natvis::x32*)hash)[2]} {((natvis::x32*)hash)[3]}{((natvis::x32*)hash)[4]}</DisplayString>
</Type>

If you can access a character array defined in code you can use the ,1sb string format and avoid any branching. Add [DLL export/extern/static] const char* hex_chars="0123456789abcdef"; to the natvis namespace and replace the 16 conditional DisplayStrings with a single one:

<Type Name="natvis::x4hi">
  <AlternativeType Name="natvis::x4lo" />
  <DisplayString>{(hex_chars+v),1sb}</DisplayString>
</Type>

To my knowledge there is no way to use the context operator {,,mylib[d].dll}natvis::hex_chars in a way that works with both static and DLL builds. You can use static const char* hex_chars = "..." but that'll add the string to every .obj file that includes the header.

Please leave a comment if you know a solution that doesn't cause bloat :)

Upvotes: 7

ComicSansMS
ComicSansMS

Reputation: 54737

This approach is far from pretty but it gets the job done.

First off, you need a dummy type somewhere in your code that handles the display of a single byte in hex without any prefixes. This feels like a really dirty hack, since we have to introduce an additional type into our code just for proper debug visualization.

namespace dummy {
    struct hex_dummy {
        unsigned char c;
    };
}

This type can be placed pretty much anywhere as long as the debugger is able to find it in the context where we want to look at a uuid.

The next step is unfortunately almost as bad. In order to be able to print a byte in hex without the 0x prefix, we introduce a debug visualizer for hex_dummy with a whopping 256 different DisplayStrings:

<Type Name="dummy::hex_dummy">
    <DisplayString Condition="(c == 0x00)">00</DisplayString>
    <DisplayString Condition="(c == 0x01)">01</DisplayString>
    <DisplayString Condition="(c == 0x02)">02</DisplayString>
    <DisplayString Condition="(c == 0x03)">03</DisplayString>
    <DisplayString Condition="(c == 0x04)">04</DisplayString>
    <DisplayString Condition="(c == 0x05)">05</DisplayString>
    <DisplayString Condition="(c == 0x06)">06</DisplayString>
    <DisplayString Condition="(c == 0x07)">07</DisplayString>
    <DisplayString Condition="(c == 0x08)">08</DisplayString>
    <DisplayString Condition="(c == 0x09)">09</DisplayString>
    <DisplayString Condition="(c == 0x0a)">0A</DisplayString>
    <DisplayString Condition="(c == 0x0b)">0B</DisplayString>
    <DisplayString Condition="(c == 0x0c)">0C</DisplayString>
    <DisplayString Condition="(c == 0x0d)">0D</DisplayString>
    <DisplayString Condition="(c == 0x0e)">0E</DisplayString>
    <DisplayString Condition="(c == 0x0f)">0F</DisplayString>

    <DisplayString Condition="(c == 0x10)">10</DisplayString>
    <DisplayString Condition="(c == 0x11)">11</DisplayString>
 ...

You get the idea.

With that in place, visualizing the uuid is easy. I used boost::uuid for testing this:

<Type Name="boost::uuids::uuid">
    <DisplayString>uuid {*(dummy::hex_dummy*)(&amp;data[0])}{*(dummy::hex_dummy*)(&amp;data[1])}{*(dummy::hex_dummy*)(&amp;data[2])}{*(dummy::hex_dummy*)(&amp;data[3])}-{*(dummy::hex_dummy*)(&amp;data[4])}{*(dummy::hex_dummy*)(&amp;data[5])}-{*(dummy::hex_dummy*)(&amp;data[6])}{*(dummy::hex_dummy*)(&amp;data[7])}-{*(dummy::hex_dummy*)(&amp;data[8])}{*(dummy::hex_dummy*)(&amp;data[9])}-{*(dummy::hex_dummy*)(&amp;data[10])}{*(dummy::hex_dummy*)(&amp;data[11])}{*(dummy::hex_dummy*)(&amp;data[12])}{*(dummy::hex_dummy*)(&amp;data[13])}{*(dummy::hex_dummy*)(&amp;data[14])}{*(dummy::hex_dummy*)(&amp;data[15])}</DisplayString>
</Type>

You can easily verify that it works by testing it with a uuid created by boost's uuid_generator:

boost::uuids::uuid const test_id =
    boost::uuids::string_generator()("{01234567-89AB-CDEF-0123-456789ABCDEF}");

enter image description here

Now this solution is not only abyssmally ugly, it also seems to take the debugger some time to work through the massive hex_dummy branching, resulting in a noticeable delay for the mouseover watch window to pop up when hovering over a uuid while debugging.

I'm far from happy with this solution, but so far it is the best I could come up with. If anyone sees any potential for improvement without sacrificing the clarity of the final output, I would be really glad to hear them.

Edit: A minor improvement - by introducing two dummy types instead of one I could at least get rid of the popup delay. The idea is two use seperate dummies for printing the upper and lower nibble of each byte, so we have to do two 16-way branches per byte instead of one 256-way branch.

namespace dummy {
    struct hex_dummy_low {
        unsigned char c;
    };

    struct hex_dummy_high {
        unsigned char c;
    };
}

The proxy visualizers:

<Type Name="dummy::hex_dummy_low">
    <DisplayString Condition="((c &amp; 0x0f) == 0x00)">0</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x01)">1</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x02)">2</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x03)">3</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x04)">4</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x05)">5</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x06)">6</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x07)">7</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x08)">8</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x09)">9</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x0a)">A</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x0b)">B</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x0c)">C</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x0d)">D</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x0e)">E</DisplayString>
    <DisplayString Condition="((c &amp; 0x0f) == 0x0f)">F</DisplayString>
</Type>

<Type Name="dummy::hex_dummy_high">
    <DisplayString Condition="((c >> 4) == 0x00)">0</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x01)">1</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x02)">2</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x03)">3</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x04)">4</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x05)">5</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x06)">6</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x07)">7</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x08)">8</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x09)">9</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x0a)">A</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x0b)">B</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x0c)">C</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x0d)">D</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x0e)">E</DisplayString>
    <DisplayString Condition="((c >> 4) == 0x0f)">F</DisplayString>
</Type>

And the final uuid visualizer:

<Type Name="boost::uuids::uuid">
    <DisplayString>uuid {*(dummy::hex_dummy_high*)(&amp;data[0])}{*(dummy::hex_dummy_low*)(&amp;data[0])}{*(dummy::hex_dummy_high*)(&amp;data[1])}{*(dummy::hex_dummy_low*)(&amp;data[1])}{*(dummy::hex_dummy_high*)(&amp;data[2])}{*(dummy::hex_dummy_low*)(&amp;data[2])}{*(dummy::hex_dummy_high*)(&amp;data[3])}{*(dummy::hex_dummy_low*)(&amp;data[3])}-{*(dummy::hex_dummy_high*)(&amp;data[4])}{*(dummy::hex_dummy_low*)(&amp;data[4])}{*(dummy::hex_dummy_high*)(&amp;data[5])}{*(dummy::hex_dummy_low*)(&amp;data[5])}-{*(dummy::hex_dummy_high*)(&amp;data[6])}{*(dummy::hex_dummy_low*)(&amp;data[6])}{*(dummy::hex_dummy_high*)(&amp;data[7])}{*(dummy::hex_dummy_low*)(&amp;data[7])}-{*(dummy::hex_dummy_high*)(&amp;data[8])}{*(dummy::hex_dummy_low*)(&amp;data[8])}{*(dummy::hex_dummy_high*)(&amp;data[9])}{*(dummy::hex_dummy_low*)(&amp;data[9])}-{*(dummy::hex_dummy_high*)(&amp;data[10])}{*(dummy::hex_dummy_low*)(&amp;data[10])}{*(dummy::hex_dummy_high*)(&amp;data[11])}{*(dummy::hex_dummy_low*)(&amp;data[11])}{*(dummy::hex_dummy_high*)(&amp;data[12])}{*(dummy::hex_dummy_low*)(&amp;data[12])}{*(dummy::hex_dummy_high*)(&amp;data[13])}{*(dummy::hex_dummy_low*)(&amp;data[13])}{*(dummy::hex_dummy_high*)(&amp;data[14])}{*(dummy::hex_dummy_low*)(&amp;data[14])}{*(dummy::hex_dummy_high*)(&amp;data[15])}{*(dummy::hex_dummy_low*)(&amp;data[15])}</DisplayString>
</Type>

Upvotes: 8

Related Questions