我正在寻找一种在VS2012中通过NatVis显示UUID的正确方法

时间:2013-07-02 22:07:33

标签: c++ debugging visual-studio-2012 visual-studio-debugging natvis

我正在寻找一种在VS2012中通过NatVis显示UUID的正确方法。我自己的uuid类型在内部使用UUID big-endian,因此转换为(GUID *)不起作用,因为GUID在Windows中使用little-endian。所以我总是看到一个歪曲的uuid。

此外,Natvis中的任何格式说明符看起来都不好,因为在使用十六进制表示法时我无法摆脱输出中的0x。有什么想法吗?

9 个答案:

答案 0 :(得分:5)

这种方法很不错,但它可以完成工作。

首先,您需要在代码中的某处使用虚拟类型来处理以十六进制显示单个字节而不带任何前缀。这感觉就像一个非常脏的黑客,因为我们必须在我们的代码中引入一个额外的类型,只是为了正确的调试可视化。

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

只要调试器能够在我们想要查看uuid的上下文中找到它,这种类型几乎可以放在任何地方。

遗憾的是,下一步几乎同样糟糕。为了能够在没有0x前缀的情况下以十六进制打印字节,我们为hex_dummy引入了一个调试可视化工具,其中包含256个不同的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>
 ...

你明白了。

有了这个,可视化uuid很容易。我使用boost::uuid来测试它:

<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>

您可以通过使用由boost uuid_generator创建的uuid对其进行测试来轻松验证其是否有效:

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

enter image description here

现在这个解决方案不仅非常丑陋,它似乎也需要调试器一段时间才能完成大量的hex_dummy分支,导致鼠标悬停在鼠标悬停时会突然显示延迟uuid在调试时。

我对这个解决方案并不满意,但到目前为止,这是我能想到的最好的解决方案。如果有人在不牺牲最终输出的清晰度的情况下看到任何改进的可能性,我会很高兴听到它们。

编辑:一个小改进 - 通过引入两个虚拟类型而不是一个我至少可以摆脱弹出延迟。这个想法是两个使用单独的假人来打印每个字节的上半字节和下半字节,所以我们必须每个字节做两个16路分支而不是一个256路分支。

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

    struct hex_dummy_high {
        unsigned char c;
    };
}

代理可视化工具:

<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>

最终的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>

答案 1 :(得分:5)

这是ComicSansMS解决方案的更紧凑版本。我使用SHA1结构和可视化工具作为示例。

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>

如果您可以访问代码中定义的字符数组,则可以使用,1sb字符串格式并避免任何分支。将[DLL export/extern/static] const char* hex_chars="0123456789abcdef";添加到natvis命名空间,并将16个条件DisplayStrings替换为一个:

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

据我所知,没有办法以一种适用于静态和DLL构建的方式使用上下文运算符{,,mylib[d].dll}natvis::hex_chars。您可以使用static const char* hex_chars = "...",但这会将字符串添加到包含标题的每个.obj文件中。

如果你知道一个不会导致膨胀的解决方案,请发表评论:)

答案 2 :(得分:2)

您可以尝试我的扩展程序C++ Debugger Visualizers。版本1.0.16使用AddIn dll支持boost :: uuids :: uuid可视化工具。

答案 3 :(得分:1)

这是一个简单的解决方案,在代码中不需要任何额外的伪结构:

<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>

它没有像其他解决方案那样显示UUID,它只是将其显示为4个32位整数的块,但它完成了这项工作:

uuid visualizer

答案 4 :(得分:1)

在搜索并尝试其他几种解决方案之后希望可以在不添加虚拟类型的情况下完成此操作,我最终使用了accepted answer。很遗憾,它是为SHA1类型而非UUID类型提供的。在UUID的情况下,当与微软UUID进行交互时,我必须格外小心地按正确的顺序输出字节,这些是&#34;中端&#34; (即前三组是小端,但最后两组是大端)。 SMBIOS规范也遵循中端排序。

设置与Johan Torp建议的相同 - 这必须在您的项目中的某个位置,我将其保留在我的UUID类型旁边:

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>

假设您的UUID类型有bytes成员:

<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>

答案 5 :(得分:1)

在上面@Xor的答案中用,X}替换格式说明符,xb}似乎在VS2019中提供了没有0x前缀并且不需要辅助结构的结果。

<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>

答案 6 :(得分:1)

对于为 Qt 的 QUuid 类寻找可视化工具的任何人(扩展 Tom Whittock 的回答):

<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>

这会显示带有小写字符的字符串,以便将其与 QUuid::toString 的输出对齐。

此可视化工具适用于 Visual Studio 2017。

答案 7 :(得分:0)

我对内部Uuid类型的解决方案很冗长,但是可以正确显示该值,并且没有任何辅助结构:

widgetEvCall('handlers.openResult', event, this, '/Restaurant_Review-g54774-d10073153-Reviews-The_Grilled_Cheese_Truck-Rapid_City_South_Dakota.html', {type: 'EATERY',element: this,index: 0,section: 1,locationId: '10073153',parentId: '54774',elementType: 'title',selectedId: '10073153'});

结果:

<Type Name="CUuid">
    <DisplayString Condition="c_data.asNumber[0]==0&amp;&amp;c_data.asNumber[1]==0&amp;&amp;c_data.asNumber[2]==0&amp;&amp;c_data.asNumber[3]==0">empty</DisplayString>
    <DisplayString>{(uint16_t)(((c_data.asNumber[0]&amp;0x00FF) &lt;&lt; 8) | ((c_data.asNumber[0]&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)((((c_data.asNumber[0]&gt;&gt;16)&amp;0x00FF) &lt;&lt; 8) | (((c_data.asNumber[0]&gt;&gt;16)&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)(((c_data.asNumber[1]&amp;0x00FF) &lt;&lt; 8) | ((c_data.asNumber[1]&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)((((c_data.asNumber[1]&gt;&gt;16)&amp;0x00FF) &lt;&lt; 8) | (((c_data.asNumber[1]&gt;&gt;16)&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)(((c_data.asNumber[2]&amp;0x00FF) &lt;&lt; 8) | ((c_data.asNumber[2]&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)((((c_data.asNumber[2]&gt;&gt;16)&amp;0x00FF) &lt;&lt; 8) | (((c_data.asNumber[2]&gt;&gt;16)&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)(((c_data.asNumber[3]&amp;0x00FF) &lt;&lt; 8) | ((c_data.asNumber[3]&amp;0xFF00) &gt;&gt;8)),Xb}{(uint16_t)((((c_data.asNumber[3]&gt;&gt;16)&amp;0x00FF) &lt;&lt; 8) | (((c_data.asNumber[3]&gt;&gt;16)&amp;0xFF00) &gt;&gt;8)),Xb}</DisplayString>
</Type>

答案 8 :(得分:0)

至少在 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>

假设 Guid 类型有一个 m_bytes 成员。其他类型类似,但 nvo 只有字节/字符值 AFAIK 才是真正需要的。

每个元素访问后的显示参数如下:

nvo - 仅数字值(即字节/字符不包括 ascii 表示)

X - 大写十六进制显示

b - 'bare' - 没有前导 0x