How to write a Qt debugging helper?

Only after you read my post about how Qt Creator interacts with GDB and what Qt pretty printers are, can you have some basic background knowledge about how to write a debugging helper for Qt Creator. What? Official document? Forget it, it is just a piece of junk developers hand over to the boss as part of their job. They won’t let others understand easily what they are doing under the hood and get laid off as a result.

Let’s first check how QString debugger helper is implemented. The debugging helper for QString is in c:\Qt\Qt5.12.1\Tools\QtCreator\share\qtcreator\debugger\qttypes.py:

def qdump__QString(d, value):
    d.putStringValue(value)
    (data, size, alloc) = d.stringData(value)
    d.putNumChild(size)
    displayFormat = d.currentItemFormat()
    if displayFormat == SeparateFormat:
        d.putDisplay('utf16:separate', d.encodeString(value, limit=100000))
    if d.isExpanded():
        d.putArrayData(data, size, d.createType('QChar'))

For this variable:

QString j="abc";

The dumper will eventually generate the following string and send it through standard output to Qt creator:

{iname="watch.1",wname="6a",numchild="3",address="0x68fbd0",type="QString",valueencoded="utf16",value="610062006300",}

Qt Creator gets this string and shows the information contained at the “watch.1″ line(the second line) in the Expressions window: the “Name” column is “j”(wname=”6a”), there is a “>” at the beginning of the line because numchild!=0; the “Value”column shows “abc” because value=”610062006300″ and it is utf16 encoded(valueencoded); the “Type” column shows “QString” as indicated by type. The “address” value is only showed in the pop-up window if you hover mouse over the variable name in the Expressions window.

But the the above JSON-like string was not generated by qdump__QString alone. In fact the QString debugging helper only generates

{iname="watch.1",wname="6a",numchild="3"

The other parts of the string are generated by DumperBase::exitSubItem. The job of qdump__QString  is to fill dumper.currentValue object so that DumperBase::exitSubItem can use the information in the currentValue object  to generate the remaining part of  dumper.output. qdump__QString must do so because the data of a QString  is not in the QString itself but stored in a byte array in heap. QString only stores a pointer to this byte array. The layout of the byte array is:

QtPrivate::RefCount ref;
int size;
uint alloc : 31;
uint capacityReserved : 1;

qptrdiff offset; // in bytes from beginning of header

The string just follows this header structure. qdump__QString uses the pointer in the QString to get the header, then uses the offset to get the start of the string, then reads the string into dumper.currentValue.

With the help of qdump__QString, you can see the string literals of the QString object in the “Value” column of Expressions windows. Without the debugger helper, you can only see the address of the QString object displayed in the “Value” column, and you will have to expand the QString to see its members.

You do not need to write a debugging helper function for every type. putItemX can handle common data types. For example, the following code snippet is to deal with an interger:

if typeobj.code == TypeCodeIntegral:
    #warn('INTEGER: %s %s' % (value.name, value))
    val = value.value()
    self.putNumChild(0)
    self.putValue(val)
    self.putType(typeName)
    return

putNumChild is to add a numchild=“xx” to output. putValue is to fill dumper.currentValue with val. putType is to fill dumper.currentType with typeName. As said before, DumperBase::exitSubItem will use the information contained in dumper.currentValue and dumper.currentType to form the parts of the MI JSON string. For integers, these pieces of information look like:

numchild="0",type="int",value="123".

For class objects, putItemX also has code for it:

self.putType(typeName)
self.putNumChild(1)
self.putEmptyValue()

So, the output would be

numchild="1",type="A",value=""

numchild=”1″ means there is always a “>” before the object name for expanding. The value column shows the address of the object. Sometimes, this is undesirable. We want to show the object’s meaningful content not its address. This requires you to write a debugging helper such as qdump__QString. Another good example for Qt Creator debugging helper is qdump__ProString in creatortypes.py, which is used to display the content of a ProString. ProString is a class for representing a sub-string of a .pro file. It has quite a few members: m_string(the whole content of the .pro file), m_offset(the beginning of the sub-string), m_length(the length of the sub-string in characters), m_file(points to the .pro file), etc. When debugging qmake, we just want to see the string ProString represents, not these member variables.  qdump__ProString reads the sub-string into dumper.currentValue instead of an empty value. The result is that you can see the sub-string in the Value column of the Expression window.

putItemX has complicate logic to handle pointer type. The result is you can see the content the pointer points to, instead of the pointer itself, in the Expression window.

Here you can find a minimal debugging helper example.

 

If you like my content, please consider buying me a coffee. Buy me a coffeeBuy me a coffee Thank you for your support!
Posted in

Leave a Reply