I am debugging a Qt application in Visual Studio 2013. I have the official Visual Studio plugin from Qt installed. I can see the contents of QString
, but for any other QObject
such as a QDir
or a QFileInfo
object I am not able to see its contents.
Am I doing something wrong or is this simply not possible?
When I unfold a QDir
instance I can only see a member named d_ptr
which references a QDirPrivate
object that I cannot inspect. I can also not call other functions of QDir
or QFileInfo
such as path()
, or filePath()
at runtime from the debugger. When I try that Visual Studio claims that the address of the function has been optimized away.
Is there anyway to debug this without adding dozens of log statements to the program?
UPDATE
After detailing in last answer that it was not possible, today I've found a way to make the debugger show the private (hiding) information.
Note: I'm keeping the old post below because it exposes the whole path I've followed to reach this solution and I think it worth keeping track of mistakes made and wrong conclusions, so nobody else made them again ;).
Suppose you have a QDir d;
variable. You can inspect its inner data by making a manual cast in the Watch window:
(Qt5Cored.dll!QDirPrivate*)d.d_ptr.d
Unfortunately it doesn't show any useful information since you'll face the same problem for inner members (private data not being printed).
However, from this point you can now modify your .natvis file to show the wanted data on hover:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="QDir">
<DisplayString>path={((Qt5Cored.dll!QDirPrivate*)d_ptr.d)->dirEntry.m_filePath}</DisplayString>
</Type>
</AutoVisualizer>
I haven't been able to print this information on the Watch window (if I add a Expand
section to the .natvis file it seems to be omitted), but at least you have this information on debug time.
I suggest you to take a look at the private header files or the Watch window (as described at the beginning of the answer) for the types you want to inspect so you can add the correct data to the .natvis file.
What was wrong about previous analysis?
I think the key point is the manual casting. I mentioned that
dirEntry
is part ofQDirPrivate
, so it is clear that the debugger is not able to inspect it.
It was partially correct. The problem was that the debugger didn't know where to locate QDirPrivate
since it is not an exported symbol of Qt; once we've indicated it explicitly (through the (Qt5Cored.dll!QDirPrivate*)
cast) it was able to inspect it.
ORIGINAL ANSWER
AFAIK this is not possible for those classes. Unfortunately there are some private structures that are not exported and that seem the debugger cannot view.
Note: I'm working with Qt 5.6.1 so I'm going to use its source code from qt.io repository to match my examples.
Let's take QDir
for example: it is defined in qdir.h, which is part of the public API of Qt. In the same file it declares QDirPrivate
but its full definition is in qtdir_p.h which is not part of the public API.
Even if symbols are loaded, somehow the definition of this class remains obscure to the debugger. See example below of the .natvis file I've used:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="QDir">
<DisplayString>Path: {d_func()->dirEntry.m_filePath}</DisplayString>
</Type>
</AutoVisualizer>
But the debugger shows the following when trying to watch QDir d(qApp->applicationDirPath());
:
Error: class "QDirPrivate" has no member "dirEntry"
Error while evaluating 'd_func()->dirEntry.m_filePath'
but dirEntry
is part of QDirPrivate
, so it is clear that the debugger is not able to inspect it. dirEntry
is of type QFileSystemEntry
, defined in qfilesystementry_p.h (another private class).
Finally, if you take a look at the qt5.natvis file from Qt VS Tools and find corresponding classes in the source code you'll see that all the included classes exposes the definition of the structures used by the .natvis file.
UPDATE
The only public API of such classes are methods, unfortunately, calling functions from debugger is not supported. Citing the response in a MSDN forum from a Microsoft Staff:
Calling a function from the debugger is playing with fire. You are likely to deadlock on cross thread dependencies (even if you don't have any explicit cross thread dependencies, there are shared locks for things like memory allocation). This is why the C++ debugger has no support for implicit funceval.
Actually, if you try to invoke any method like QDir::absolutePath()
in the watch window or in the .natvis file you'll see the following error message:
Error: Function QDir::absolutePath has no address, possibly due to compiler optimizations.
NOT SO ELEGANT WORKAROUND
A possible solution would be to use wrapper classes that store those private values. You'd need to replace each object with the wrapper but may help.
Below you'll find a very simple example for QDir
(you'd need to complete with required constructors and to save the information you want). Keep in mind that this type of classes in Qt has been designed to be not extensible, so there are no virtual methods (so be aware of reimplementing some of them and to use the object casted to the base class).
class MyQDir : public QDir {
public:
MyQDir(const QString& path) : QDir(path) {
m_absolutePath = absolutePath();
}
private:
QString m_absolutePath;
};
and following .natvis file to display MyQDir
's path on hover:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="MyQDir">
<DisplayString>Path: {m_absolutePath}</DisplayString>
</Type>
</AutoVisualizer>
Finally, I think the only solution left would be to print the information to console (qDebug()
).
As a side note, this tutorial explains how to write a custom .natvis file. It is for 2015 but I've used it for 2017 flawlessly. I hope it is valid for 2013 too.
I have the following extensions to the qt5.natvis which exposes some of the contents of QDir, QFile and QFileInfo, building on an original suggestion above:
<Type Name="QFileInfoPrivate">
<DisplayString Condition="0 == this"><null></DisplayString>
<DisplayString>{fileEntry}</DisplayString>
<StringView>fileEntry</StringView>
<Expand>
<Item Name="QSharedData">*((Qt5Cored.dll!QSharedData *) this)</Item>
<Item Name="fileEntry">fileEntry</Item>
<!--
<Item Name="metaData">metaData</Item>
<Item Name="fileListsInitialized">fileListsInitialized</Item>
<Item Name="fileEngine">fileEngine</Item>
<Item Name="fileNames">fileNames</Item>
<Item Name="fileOwners">fileOwners</Item>
<Item Name="cachedFlags">cachedFlags</Item>
<Item Name="isDefaultConstructed">isDefaultConstructed</Item>
<Item Name="cache_enabled">cache_enabled</Item>
<Item Name="fileFlags">fileFlags</Item>
<Item Name="fileSize">fileSize</Item>
<Item Name="fileTimes">fileTimes</Item>
-->
</Expand>
</Type>
<Type Name="QFileInfo">
<DisplayString>{*((Qt5Cored.dll!QFileInfoPrivate *) d_ptr.d)}</DisplayString>
<StringView>*((Qt5Cored.dll!QFileInfoPrivate *) d_ptr.d)</StringView>
<Expand>
<Item Name="QFileInfoPrivate">*((Qt5Cored.dll!QFileInfoPrivate *) d_ptr.d)</Item>
</Expand>
</Type>
<Type Name="QFileSystemEntry">
<DisplayString Condition="0 == this"><null></DisplayString>
<DisplayString>{m_filePath}</DisplayString>
<StringView>m_filePath</StringView>
<!--
<Expand>
<Item Name="m_filePath">m_filePath</Item>
<Item Name="m_nativeFilePath">m_nativeFilePath</Item>
<Item Name="m_lastSeparator">m_lastSeparator</Item>
<Item Name="m_firstDotInFileName">m_firstDotInFileName</Item>
<Item Name="m_lastDotInFileName">m_lastDotInFileName</Item>
</Expand>
-->
</Type>
<Type Name="QDirPrivate">
<DisplayString Condition="0 == this"><null></DisplayString>
<DisplayString>{dirEntry}</DisplayString>
<StringView>dirEntry</StringView>
<Expand>
<Item Name="QSharedData">*((Qt5Cored.dll!QSharedData *) this)</Item>
<Item Name="dirEntry">dirEntry</Item>
<Item Name="nameFilters">nameFilters</Item>
<Item Name="absoluteDirEntry">absoluteDirEntry</Item>
<!--
<Item Name="metaData">metaData</Item>
<Item Name="fileListsInitialized">fileListsInitialized</Item>
<Item Name="fileEngine">fileEngine</Item>
<Item Name="files">files</Item>
<Item Name="fileInfos">fileInfos</Item>
<Item Name="sort">sort</Item>
<Item Name="filters">filters</Item>
-->
</Expand>
</Type>
<Type Name="QDir">
<DisplayString>{*((Qt5Cored.dll!QDirPrivate *) d_ptr.d)}</DisplayString>
<StringView>*((Qt5Cored.dll!QDirPrivate *) d_ptr.d)</StringView>
<Expand>
<Item Name="QDirPrivate">*((Qt5Cored.dll!QDirPrivate *) d_ptr.d)</Item>
</Expand>
</Type>
<Type Name="QFilePrivate">
<DisplayString Condition="0 == this"><null></DisplayString>
<DisplayString>{fileName}</DisplayString>
<StringView>fileName</StringView>
<Expand>
<Item Name="QFileDevice">*((Qt5Cored.dll!QFileDevice *) this)</Item>
<Item Name="fileName">fileName</Item>
</Expand>
</Type>
<Type Name="QFile">
<DisplayString>{*((Qt5Cored.dll!QFilePrivate *) d_ptr.d)}</DisplayString>
<StringView>*((Qt5Cored.dll!QFilePrivate *) d_ptr.d)</StringView>
<Expand>
<Item Name="QFilePrivate">*((Qt5Cored.dll!QFilePrivate *) d_ptr.d)</Item>
</Expand>
</Type>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With