关键词:Qt 5、CDB、pdb
平台:Win 7 64位
Qt版本:Qt 5.1.1
IDE:Qt Creator
编译器:msvc2012-x64
目前,最新版本的Qt是Qt 5.1.1。针对Windows NT平台,Qt官网提供了两种安装包:一种使用MinGW作为默认编译器,一种使用MSVC(VS2010或VS2012)作为默认编译器。
MinGW中已附带gcc、gdb等工具。所以如果安装前者,就可以使用gdb作为Qt Creator中的默认调试器;如果安装后者,需要去微软官网额外下载CDB调试器。微软官网提供了下载、安装CDB的指南:
Debugging Tools for Windows
接下来,我们需要在Qt Creator中设置CDB的安装路径。在Qt Creator中选择“工具”->“选项(O)...”,调出“选项”对话框,选择左侧的“构建与运行”,如下图:
在这个选项卡中,您可以配置编译器的类型、调试器的安装位置等。
点击“自动检测”按钮使Qt自动检测CDB安装路径(前提是您已经成功安装了CDB调试器;此外,如果在安装CDB后安装Qt Creator,Qt Creator往往能自动检测到CDB的位置)。如果“自动检测”未成功,您可以手动配置CDB的路径。
接下来,让我们调试一下如下代码:
#include
#include
#include "iconeditor.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
IconEditor editor;
editor.setIconImage(QImage("../06_08_scrollarea/images/help.ico"));
QScrollArea scrollArea;
scrollArea.setWidget(&editor);
scrollArea.viewport()->setBackgroundRole(QPalette::Dark);
scrollArea.viewport()->setAutoFillBackground(true);
scrollArea.setWindowTitle(QObject::tr("Icon Editor"));
scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea.show();
return a.exec();
}
代码中用到的iconeditor.h来自于C++ GUI Programming with Qt 4, Second Edition。
在Debug模式下编译、运行,倒未出错;但在程序退出时,出现了断言失败:
我猜测是因为我把editor对象定义在栈上引起的,所以就又尝试了如下代码:
// 为何在栈上定义IconEditor对象时,程序退出时会引发断言失败?
IconEditor *editor = new IconEditor;
editor->setIconImage(QImage("../06_08_scrollarea/images/help.ico"));
QScrollArea scrollArea;
scrollArea.setWidget(editor);
这次,在Debug模式下退出时未出错。
可是为什么呢?是因为只能把通过动态内存分配获得的对象的指针作为QScrollArea::setWidget的参数呢,还是其他原因?我想,是时候调试一下了。
首先,为了了解QScrollArea::setWidget都做了什么,我在这行代码上加了断点:
scrollArea.setWidget(&editor);
可是,调试时按F11却无法进入setWidget内部。经过一番搜索,发现自己在安装Qt的时候未安装Qt的source component。于是,我重装了Qt,这次勾选了source component:
安装完成之后,Qt的源代码会被安装在Qt\Qt5.1.1\5.1.1\Src目录下。
接下来,我们需要再次打开“选项”对话框配置Qt源码路径。如下图所示:
点击“添加Qt 源码...”按钮,找到Qt源码安装目录并添加进来。这样“源码路径映射”列表中会自动多出三项(对于我来说,新添加的三项是上图中的第1、2、4项,第3项是我为了在调试时能跟进MSVC CRT Library源码而额外添加的)。
那么“源码路径映射”该怎么理解呢?我的理解如下(不保证正确性):
“
我们知道,在使用一些第三方库的时候(比如Qt),库的作者通常会发布Debug版本的动态链接库(如:Qt5Cored.dll)和Release版本的动态链接库(如:Qt5Core.dll)。由于Debug版本的动态链接库额外包含一些调试时所需的信息,所以它们通常都要比相应的Release版本的大。此外,为了方便调试(或逆向工程),库的作者通常会附带提供构建Debug版本库时生成的一个pdb文件。这个pdb文件(相当于一个小型数据库)的作用,微软官网的一份文档中有介绍(这份文档同时介绍了一些Symbol Server相关知识):
Debugging with Symbols (Windows)
如果我们在调用库中一个函数时传递了不合法参数而导致断言失败,我们通常都会看到一个提示出错代码所在文件及所在行的错误对话框——如本文中的第二张图所示。
图二中,出现断言失败的是CRT Library(C运行时库)源码中的dbgdel.cpp文件。安装VS2010/VS2012时,CRT Library通常都会被安装到你的机器上。比如,在我的笔记本上(处理器:AMD64),CRT Library的源代码安装路径如下:
D:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\crt\src
在AMD64子文件夹下,我找到了dbgdel.cpp。
可是,为什么图二中的对话框提示的出错文件所在路径是F:\dd\vctools\crt_bld\self_64_amd64\crt\src呢?
哈,很容易猜到:这是C运行时库的作者在创建C运行时库的动态链接库(如:msvcrt100d.dll、msvcrt110d.dll)时C运行时库源代码所在路径。而这些诸如“出错代码所在文件及行”这样的信息已经被“固化”在pdb文件中了,所以,自然不会和我电脑上的路径一模一样。
同样的,在“添加Qt源码...”后上图中的“源码路径映射”列表中自动多出来的三项中,“源路径”自然是Qt库的作者们在构建二进制的DLL时Qt源码所在路径(其实,这是我猜的),而“目标路径”则是指Qt源码在我们机器上的安装路径。
有了这个“源码路径映射”后,在使用Debug版本的DLL时出现诸如断言失败这样的错误时,调试器就能够根据pdb文件中记录的cpp源码路径来找到我们的机器上相应的cpp文件的路径。这样,我们就可以Step into这个cpp文件,并跟踪调试了。
”
上图中,“源码映射路径”群组框提供了这样的tooltip:
“调试器使用的映射源文件夹在这里输入。
这在使用的源代码树的副本和模块构建的路径不一致时非常有用,比如,在远程调试的时候。”
我想,这印证了我的猜想。
配置好Qt的源代码路径后,我又在列表中加了一项——上图中第三项——以使我能够在调试时可以跟踪进入本机上的C运行时库源代码。
重新开始调试上面的程序,结果发现,我能够跟进C运行时库源码,却无法跟进Qt源码。通过阅读上面提到的那篇微软官网文档,我猜测Qt Creator未找到Qt库相应的pdb文件(实际上,我是在看了Qt Creator的“调试器日志”窗口中的日志信息后才意识到是因为Qt Creator未能成功加载相应pdb文件而无法在Debug模式下跟进Qt源代码的)。幸运的是,在Qt Creator中添加pdb文件的local search path很是简单,如下图:
“Symbol Paths”中的第二项是我手动添加的。该路径下除了Qt库的一些lib文件外,还有相应的pdb文件(比如:Qt5Cored.pdb、Qt5Widgetsd.pdb等)。
重启Qt。嗯,这次终于可以跟进Qt的源代码文件了,这真是喜大普奔啊!!!
PS1:我在WinXP 32位下使用附带MinGW的Qt安装包未出现这么多问题。
PS2:上述代码中的断言失败的真正原因,我还未找到。