Structured data, such as objects of class, struct, or union types, is displayed in the Locals and Expressions view as part of a tree. To access sub-structures of the objects, expand the tree nodes. The sub-structures are presented in their in-memory order, unless the Sort Members of Classes and Structs Alphabetically option from the context menu is selected.
Similarly, pointers are displayed as a tree item with a single child item representing the target of the pointer. In case the context menu item Dereference Pointers Automatically is selected, the pointer and the target are combined into a single entry, showing the name and the type of the pointer and the value of the target.
This standard representation is good enough for the examination of simple structures, but it does usually not give enough insight into more complex structures, such as QObjects or associative containers. These items are internally represented by a complex arrangement of pointers, often highly optimized, with part of the data not directly accessible through either sub-structures or pointers.
To give the user simple access also to these items, Qt Creator employs so-called Debugging Helpers. Debugging Helpers come in two varieties, compiled, and Python based, depending on the selected kit.
By default, Debugging Helpers are automatically and transparently used. To disable them, select Tools > Options > Debugger > Locals & Expressions, and deselect the Use Debugging Helper check box.
Qt Creator ships with Debugging Helpers for about 80 of the most popular Qt classes, Standard C++ containers and smart pointers, covering the usual needs of a C++ application developer out-of-the-box.
The following sections describe how to extend the debugging helpers to your own data types.
There are two approaches to displaying complex data types. The first and original one is to use debugging helpers based on C++. While it has been superseded on most platforms by the more robust and more flexible second approch that uses Python scripting, it is the only feasible one on Windows/MSVC, Mac OS, and old Linux distributions. Moreover, this approach is automatically chosen as fallback if the Python based approach fails.
During debugging with the C++ based debugging helpers, Qt Creator dynamically loads a helper library in form of a DLL or a shared object into the debugged process. The Qt SDK package already contains a prebuilt debugging helper library. To create your own debugging helper library, select Tools > Options > Build & Run > Qt Versions. As the internal data structures of Qt can change between versions, the debugging helper library is built for each Qt version.
Qt Creator uses GDB builds that enable Python scripting to display information in the Locals and Expressions view. When Python scripting is used, code (Debugging helpers) does not need to be injected into the debugged process to nicely display QStringList or std::map contents, for example.
The code injection caused problems and put an extra stress on the debugged process. You can now easily extend the debugging helpers to other types. No compilation is required, just adding a few lines of Python.
Python scripting vastly reduces the communication overhead compared with the previous solution. However, there are some obstacles:
On platforms featuring a Python-enabled version of the GDB debugger, the data extraction is done by a Python script. This is more robust as the script execution is separated from the debugged process. It is also easier to extend as the script is less dependent on the actual Qt version and does not need compilation.
To extend the shipped Python based debugging helpers for custom types, define one Python function per user defined type in the GDB startup file. By default, the following startup file is used: ~/.gdbinit. To use another file, select Tools > Options > Debugger > GDB and specify a filename in the GDB startup script field.
The function name has to be qdump__NS__Foo, where NS::Foo is the class or class template to be examined. Nested namespaces are possible.
The debugger plugin calls this function whenever you want to display an object of this type. The function is passed the following parameters:
The function has to feed the Dumper object with certain information which is used to build up the object and its children's display in the Locals and Expressions view.
Example:
def qdump__QVector(d, value): d_ptr = value["d"] p_ptr = value["p"] alloc = d_ptr["alloc"] size = d_ptr["size"] check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000) checkRef(d_ptr["ref"]) innerType = templateArgument(value.type, 0) d.putItemCount(size) d.putNumChild(size) if d.isExpanded(): p = gdb.Value(p_ptr["array"]).cast(innerType.pointer()) charPtr = lookupType("char").pointer() d.putField("size", size) with Children(d, size, maxNumChild=2000, childType=innerType, addrBase=p, addrStep=(p+1).cast(charPtr) - p.cast(charPtr)): for i in d.childRange(): d.putSubItem(i, p.dereference()) p += 1
For each line in the Locals and Expressions view, a string like the following needs to be created and channeled to the debugger plugin.
"{iname='some internal name', # optional
addr='object address in memory', # optional
name='contents of the name column', # optional
value='contents of the value column',
type='contents of the type column',
numchild='number of children', # zero/nonzero is sufficient
childtype='default type of children', # optional
childnumchild='default number of grandchildren', # optional
children=[ # only needed if item is expanded in view
{iname='internal name of first child',
},
{iname='internal name of second child',
},
]}"
The value of the iname field is the internal name of the object, constituting a dot-separated list of identifiers, corresponding to the position of the object's representation in the view. If it is not present, is it generated by concatenating the parent object's iname, a dot, and a sequential number.
The value of thename field is displayed in the name column of the view. If it is not specified, a simple number in brackets is used instead.
While in theory, you can build up the entire string above manually, it is easier to employ the Dumper Python class for that purpose. The Dumper Python class contains a complete framework to take care of the iname and addr fields, to handle children of simple types, references, pointers, enums, known and unknown structs as well as some convenience methods to handle common situations.
The member functions of the Dumper class are the following:
with SubItem(self, name): self.putValue(value) self.putAddress(value.address) self.putType("int") self.putNumChild(0)
with SubItem(self, name): self.putValue(value) self.putType("bool") self.putNumChild(0)
with SubItem(self, component): self.putItem(value)
Exceptions raised by nested function calls are caught and all output produced by putItem is replaced by the output of:
except RuntimeError: d.put('value="<invalid>",type="<unknown>",numchild="0",')
The attempt to create child items might lead to errors if data is uninitialized or corrupted. To gracefully recover in such situations, use Children and SubItem Context Managers to create the nested items.
The Children constructor __init__(self, dumper, numChild = 1, childType = None, childNumChild = None, maxNumChild = None, addrBase = None, addrStep = None) uses one mandatory argument and several optional arguments. The mandatory argument refers to the current Dumper object. The optional arguments can be used to specify the number numChild of children, with type childType_ and childNumChild_ grandchildren each. If maxNumChild is specified, only that many children are displayed. This should be used when dumping container contents that might take overly long otherwise. The parameters addrBase and addrStep can be used to reduce the amount of data produced by the child dumpers. Address printing for the nth child item will be suppressed if its address equals with addrBase + n * addrStep.
Example:
d.putNumChild(2) # Annouce children to make the item expandable in the view. if d.isExpanded(): with Children(d): with SubItem(d): d.putName("key") d.putItem(key) with SubItem(d): d.putName("value") d.putItem(value)
Note that this can be written more conveniently as:
d.putNumChild(2) if d.isExpanded(): with Children(d): d.putSubItem("key", key) d.putSubItem("value", value)
The debugging helpers for QML provide you with code completion for custom modules (qmldump) and debugging Qt Quick UI projects (qmlobserver).
You have to build the QML Inspector once for each Qt version that you want to debug with. Select Tools > Options > Build & Run > Qt Versions.
Note: QML Inspector requires Qt 4.7.1 or later.
Qt's bootstrapped applications (such as moc and qmake) are built in a way that is incompatible with the default build of the debugging helpers. To work around this, add dumper.cpp to the compiled sources in the application Makefile.
Choose Tools > Options > Debugger > Debugging Helper > Use debugging helper from custom location, and specify an invalid location, such as /dev/null.