用windbg调试QT程序或者分析QT程序的dump是一件痛苦的事情,因为windbg缺少对QT基础数据的展示能力,比如:

int main(int argc, char *argv[])
{
    QString str = "hello world";
    QList<QString> str_list;
    str_list.append(str);
    QMap<int, QString> str_map;
    str_map[11] = str;
}

这份代码使用windbg查看str,str_list或者str_map简直是一件折磨的事情。

0:000> dv
           argc = 0n1
           argv = 0x02cc5e00
            str = class QString
       str_list = class QList<QString>
        str_map = class QMap<int,QString>
              a = class QApplication
              w = class QMsgTest
0:000> dx str
str                 [Type: QString]
    [+0x000] d                : 0x2cc5d80 [Type: QString::Data *]
0:000> dx str_list
str_list                 [Type: QList<QString>]
    [+0x000] p                [Type: QListData]
    [+0x000] d                : 0x2cc7258 [Type: QListData::Data *]
0:000> dx str_map
str_map                 [Type: QMap<int,QString>]
    [+0x000] d                : 0x2cc72b8 [Type: QMapData *]
    [+0x000] e                : 0x2cc72b8 [Type: QMapData::Node *]

可以看到,windbg只会告诉你类型,根本不会给你展示数据本身,如果要查看数据还得自己来算,以最复杂的QMap为例:

0:000> ?? str_map.d->forward[0]
struct QMapData * 0x031470f8
   +0x000 backward         : 0x03147068 QMapData
   +0x004 forward          : [12] 0x03147068 QMapData
   +0x034 ref              : QBasicAtomicInt
   +0x038 topLevel         : 0n-17891602
   +0x03c size             : 0n-17891602
   +0x040 randomBits       : 0xfeeefeee
   +0x044 insertInOrder    : 0y0
   +0x044 sharable         : 0y1
   +0x044 strictAlignment  : 0y1
   +0x044 reserved         : 0y11111110111011101111111011101 (0x1fdddfdd)

0:000> ?? sizeof(@!"qtmsgtest!QMapPayloadNode<int,QString>")-sizeof(qtmsgtest!QMapData::Node*)
unsigned int 8

0:000> dt qtmsgtest!QMapNode<int,QString> (0x31470f8-8)
   +0x000 key              : 0n11
   +0x004 value            : QString
   +0x008 backward         : 0x03147068 QMapData::Node
   +0x00c forward          : [1] 0x03147068 QMapData::Node

0:000> ?? ((qtmsgtest!QString*)0x31470f4)->d
struct QString::Data * 0x03145b30
   +0x000 ref              : QBasicAtomicInt
   +0x004 alloc            : 0n11
   +0x008 size             : 0n11
   +0x00c data             : 0x03145b42  -> 0x68
   +0x010 clean            : 0y0
   +0x010 simpletext       : 0y0
   +0x010 righttoleft      : 0y0
   +0x010 asciiCache       : 0y0
   +0x010 capacity         : 0y0
   +0x010 reserved         : 0y11001101110 (0x66e)
   +0x012 array            : [1] 0x68

0:000> du @@C++(((qtmsgtest!QString*)0x31470f4)->d->data)
03145b42  "hello world"

你们看,为了获取key=11、value=”hello world”需要这么折腾一通,如果数据多了那不得抓狂。

不过幸运了是windbg现在支持natvis了,简单的说就是通过natvis里的配置自动解析和符号匹配的数据结构。接下来要做的就是找一个好用的qt的natvis了。我这里找了一个qt配合vs2012里的qt4.natvis,让我们看看加载后的效果:

0:000> .nvload qt4.natvis
Successfully loaded visualizers in "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\Visualizers\qt4.natvis"
0:000> dv
           argc = 0n1
           argv = 0x02cc5e00
            str = hello world
       str_list = { size = 1 }
        str_map = { size = 1 }
              a = class QApplication
              w = class QMsgTest
0:000> dx str
str                 : hello world [Type: QString]
    [<Raw View>]     [Type: QString]
    [size]           : 11 [Type: int]
    [referenced]     : 3 [Type: long]
    [0]              : 0x68 [Type: unsigned short]
    [1]              : 0x65 [Type: unsigned short]
    [2]              : 0x6c [Type: unsigned short]
    [3]              : 0x6c [Type: unsigned short]
    [4]              : 0x6f [Type: unsigned short]
    [5]              : 0x20 [Type: unsigned short]
    [6]              : 0x77 [Type: unsigned short]
    [7]              : 0x6f [Type: unsigned short]
    [8]              : 0x72 [Type: unsigned short]
    [9]              : 0x6c [Type: unsigned short]
    [10]             : 0x64 [Type: unsigned short]
0:000> dx str_list
str_list                 : { size = 1 } [Type: QList<QString>]
    [<Raw View>]     [Type: QList<QString>]
    [referenced]     : 1 [Type: long]
    [0x0]            : hello world [Type: QString]
0:000> dx str_map
str_map                 : { size = 1 } [Type: QMap<int,QString>]
    [<Raw View>]     [Type: QMap<int,QString>]
    [referenced]     : 1 [Type: long]
    [0x0]            : 0x2cc7340 : (11, hello world) [Type: QMapNode<int,QString> *]

看到了么,无论是str,str_list还是str_map都直接打印出了内部的数据,无需大费周章的折腾,这简直是windbg爱好者调试qt程序的神器。

最后附上qt4.natvis的地址:https://gist.github.com/gregseth/9bcd0112f8492fa7bfe7