在Visual Studio中调试Qt应用程序并检查不透明对象(例如QDir或QFileInfo)

时间:2017-06-26 15:39:48

标签: c++ qt visual-studio-2013

我正在调试Visual Studio 2013中的Qt应用程序。我安装了Qt的官方Visual Studio插件。我可以看到QString的内容,但对于任何其他QObject,例如QDirQFileInfo对象,我无法看到其内容。

我做错了什么或者这根本不可能?

当我展开QDir实例时,我只能看到一个名为d_ptr的成员,该成员引用了我无法检查的QDirPrivate对象。我也无法在运行时从调试器调用QDirQFileInfo的其他函数,例如path()filePath()。当我尝试Visual Studio声称函数的地址已被优化时。

无论如何调试这个而不向程序添加几十个日志语句?

2 个答案:

答案 0 :(得分:5)

<强>更新

在最后一个回答中详细说明了这是不可能的,今天我找到了一种让调试器显示隐私(隐藏)信息的方法。

注意:我保留下面的旧帖子,因为它暴露了我为达到这个解决方案而遵循的整个路径,我认为值得追踪错误和错误的结论,所以没有人别的再做一次;)。

假设您有一个QDir d;变量。您可以通过在Watch窗口中进行手动转换来检查其内部数据:

(Qt5Cored.dll!QDirPrivate*)d.d_ptr.d

Watch window

不幸的是,它没有显示任何有用的信息,因为您将面临内部成员的相同问题(私人数据未被打印)。

但是,从这一点开始,您现在可以修改.natvis文件以在悬停时显示所需数据:

<?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)-&gt;dirEntry.m_filePath}</DisplayString>
  </Type>
</AutoVisualizer>

done!

我还没有能够在Watch窗口中打印此信息(如果我在.natvis文件中添加Expand部分,似乎省略了),但至少你有这方面的信息调试时间。

我建议您查看要检查的类型的私有头文件或Watch窗口(如答案开头所述),这样您就可以将正确的数据添加到.natvis文件中。 / p>

之前的分析有什么问题?

我认为关键点在于手动铸造。我提到了

  

dirEntryQDirPrivate的一部分,因此显然调试器无法检查它。

部分正确。问题是调试器不知道在哪里找到QDirPrivate,因为它不是Qt的导出符号;一旦我们明确指出(通过(Qt5Cored.dll!QDirPrivate*)演员),它就能够检查它。

原始回答

AFAIK这对于那些课程是不可能的。不幸的是,有些私有结构未导出,而且看起来调试器无法查看

注意:我正在使用Qt 5.6.1,因此我将使用qt.io存储库中的源代码来匹配我的示例

让我们以QDir为例:它在qdir.h中定义,它是Qt的公共API的一部分。在同一个文件中,它声明QDirPrivate,但其完整定义位于qtdir_p.h,而不是公共API的一部分。

即使加载了符号,这个类的定义在某种程度上仍然对调试器来说是模糊的。请参阅下面我使用的.natvis文件示例:

<?xml version="1.0" encoding="utf-8"?> 
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="QDir">
    <DisplayString>Path: {d_func()-&gt;dirEntry.m_filePath}</DisplayString>
  </Type>
</AutoVisualizer>

但调试器在尝试观看QDir d(qApp->applicationDirPath());时显示以下内容:

  

错误:课程&#34; QDirPrivate&#34;没有会员&#34; dirEntry&#34;

     

评估&#39; d_func()时出错 - &gt; dirEntry.m_filePath&#39;

dirEntry QDirPrivate的一部分,因此显然调试器无法检查它。 dirEntry的类型为QFileSystemEntry,在qfilesystementry_p.h(另一个私有类)中定义。

最后,如果您查看Qt VS Tools中的qt5.natvis文件并在源代码中找到相应的类,您将看到所有包含的类公开了所使用的结构的定义。 .natvis文件。

<强>更新

此类的唯一公共API是方法,遗憾的是,不支持从调试器调用函数。引用MSDN forum from a Microsoft Staff中的回复:

  

从调试器调用函数正在玩火。您可能会遇到跨线程依赖关系死锁(即使您没有任何明确的跨线程依赖关系,也有内存分配等共享锁)。这就是C ++调试器不支持隐式funceval的原因。

实际上,如果您尝试在监视窗口或.natvis文件中调用QDir::absolutePath()之类的任何方法,您将看到以下错误消息:

  

错误:函数QDir :: absolutePath没有地址,可能是由于编译器优化。

不是优雅的解决方法

一种可能的解决方案是使用存储这些私有值的包装类。您需要使用包装器替换每个对象,但可能有所帮助。

在下面您将找到QDir的一个非常简单的示例(您需要完成所需的构造函数并保存所需的信息)。请记住,Qt中的这类类型被设计为不可扩展,因此没有虚拟方法(因此请注意重新实现其中的一些并使用转换为基类的对象)。

class MyQDir : public QDir {
public:
  MyQDir(const QString& path) : QDir(path) {
    m_absolutePath = absolutePath();
  }

private:
  QString m_absolutePath;
};

并关注.natvis文件,以便在悬停时显示MyQDir的路径:

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

最后,我认为唯一的解决方案是将信息打印到控制台(qDebug())。

作为旁注,this tutorial解释了如何编写自定义.natvis文件。这是2015年,但我在2017年完美地使用它。我希望它也适用于2013年。

答案 1 :(得分:2)

我对qt5.natvis进行了以下扩展,它基于上面的原始建议公开了QDir,QFile和QFileInfo的某些内容:

<Type Name="QFileInfoPrivate">
    <DisplayString Condition="0 == this">&lt;null&gt;</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">&lt;null&gt;</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">&lt;null&gt;</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">&lt;null&gt;</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>