12.10 使用 Qt Creator 调试程序

Qt Creator 集成了强大的调试器,提供了丰富多样的调试功能和选项,足以满足开发者 的需要。

12.10.1 调试器引擎

Qt Creator 本身并没有调试器,它必须借助其它的调试器引擎,并为它们提供了一个图 形化的前端界面。表 12-7 示出了在所支持的平台上,Qt Creator 使用的调试器。

表 12-7 Qt Creator 使用的调试器引擎

平台 编译器 调试器引擎
Linux, Unixes, Mac OS gcc GNU Symbolic Debugger (gdb)
Windows/MinGW gcc GNU Symbolic Debugger (gdb)
Windows Microsoft Visual C++ Compiler Debugging Tools for Windows/Microsoft Console Debugger (CDB)

在 Qt Creator 中,你可以使用调试器前端界面逐行单步或逐过程调试程序,设置断点 ,检查堆栈中的内容,查看局部或全局变量的值等等,这些和我们常见的调试器提供的功能并无二致。而上述的原生信息,Qt Creator 会以清晰、简明的方式展现给程序员,这将使得原 本令人生畏的调试工作变得简单而有趣。

除了像堆栈查看器、局部变量和观察器、寄存器查看器等这些主流 IDE 都会提供的功能 外,Qt Creator 还提供了许多的功能以帮助开发者提高效率。由于调试器前端对 Qt 的内部 机制了如指掌,所以当程序出现问题时,它能够明晰描述症状。

表 12-8 示出了这些调试器引擎在单独安装时的一些需要注意的事项。

表 12-8 调试器引擎的相关信息

调试器引擎 注意事项
GDB(X11 平台) 需要 GDB6.8 或以上版本
GDB 或 CDB 可以从 Microsoft Developer Network 上自由下载 CDB,版本 6.10 以上,注意区分 32 位和 64 位版;(Windows 平台) 如果在 Windows 上使用 SDK 方式安装 Qt4 开源版,那么仍将使用 GDB 作为调试器引擎;如果使用 Microsoft Visual C++的编译器编译安装 Qt Creator,那么将使用 CDB 作为调试器引擎,并且默认情 况下,Qt Creator 将会检查%ProgramFiles%\Debugging Tools for Windows 这个路径下是否包含了所有需要的 调试器引擎的头文件。

12.10.2 与调试器交互

在 Debug 模式下时,Qt Creator 提供了许多的锚接窗口来辅助开发者与程序进行交互 。 常见的一些被设置为缺省可见的,不常使用的则缺省被隐藏。你可以依次点击【Debug】 →【View】来配置它们的显隐。

图 12-41 设置常见辅助视图的显隐

如图 12-41 所示,你可以通过点击【Locked】菜单项来锁住或解锁你的锚接窗口的布局 , 就像设置这些锚接窗口的显隐一样。你的锚接窗口的位置将被 Qt Creator“记住”,下次启 动时它将根据上次的记忆来布局。

12.10.3 断点

你可以在断点视图( Breakpoints view )中查看断点。无论你的程序是否在运行和 调试中,断点视图都是默认并且随时可见的 。如图 12-42 所示,你可以在【Breakpoints】视图中查看断点设置的情况。图 12-43 显示了详细的断点信息。

图 12-42 在【Breakpoints】视图中查看断点

图 12-43 断点的详细信息

所谓断点,就是由程序开发者设定的一系列条件 ,但程序以调试方式运行时,一旦符合 引发断点的“条件”,程序便中断执行,此后程序开发者便可以检视程序在运行时的状态 ,继 而控制程序的运行,直至找出问题所在。

在 Qt Creator 中,我们通常可以把断点与源代码文件或者其中的某一行关联起来,也 可以把它放在某个方法的起始处(通常指定义处 )。下面是设置断点的具体“规则”:

  • 在某一行代码设置断点--在代码行行号的左边缘处点击鼠标左键或者按下 F9 键(在 Mac OS X 系统中是 F8 键)
  • 在某一个函数处设置断点—依次点击菜单【Debug】 ->【Set Breakpoint at Function... 】,在其中输入函数的名字

你可以这样去掉一个断点:

  • 在代码编辑器内断点标识处用鼠标左键再次点击
  • 在断点视图中选中某个断点,并按下 Delete 键
  • 在断点视图内点击鼠标右键,在弹出的上下文窗口中选择 【Delete Breakpoint】 小贴士:断点可以在任意时刻设置-在程序开始调试之前和正在调试之时均可。断点的设置 也会作为一部分被当前的会话所保存。

12.10.4 程序的调试运行

要在调试模式下启动运行一个 Qt 应用程序,你可以依次点击菜单项【Debug】 →【Start Debugging】,或者按下 F5 键即可。Qt Creator 将检查程序代码或设置是否有更新,并在必要时重新编译项目,然后调试器将接管并启动程序的运行。

提示:Qt 应用程序在调试模式下启动运行时,往往需要一段时间,从几秒到若干分钟 不等,这取决于你的机器的配置以及程序的复杂程度(比如应用了 QtWebKit 模块的程序可能 要多花费一些时间)。

当程序调试运行未遇到断点时,它与直接运行状态并无区别。开发者可以依次点击菜单 项【Debug】 →【Interrupt】或者直接按下如图所示的调试器状态栏上的 【Interrupt】按 钮来中断程序的运行,这与程序在运行时遇到断点而停下来的效果是一样的 ,如图 12-44 所 示。

图 12-44 调试器状态栏上的【Interrupt】按钮

当程序中断时,Qt Creator 将做如下的事情:

  • 获得程序中断处在堆栈中的地址
  • 获得局部变量的值
  • 检视并更新观察器(Watchers)视图内容

更新 Registers 、Modules 以及 Disassembler 视图 这时我们可以在 Debugger 视图中检视到程序更为详细的状态。

要结束调试状态,可以按下 Shift+F5 键。按下 F10 键可以进入逐行调试状态,按下 F11 键进入逐过程调试状态,按下 F5 键可以使程序运行到下一个断点处,如果后面没有断点了, 程序将完整的运行起来,这种情况仍然是在调试状态下的运行。

12.10.5 堆栈视图(Stack View)

当被调试的程序在断点处中断时,Qt Creator 将在堆栈视图中显示出程序到达断点处之 前所经历的那些函数。这些函数对应到被称作是“堆栈框架节点”,每一个节点对应一个函数。 如图 12-45 所示,Qt Creator 显示了这些函数所在的文件名、在源代码里面的行号。

图 12-45 堆栈视图

有些情况下,不是所有的框架节点都能够准确的对应到源代码中的一个位置 ,因而也就 没有相应的调试信息,这些调试框架将被灰色显示。

当你在堆栈视图里面显示的某一行处使用鼠标左键双击时 ,代码编辑器将跳转至相应的 代码行,Qt Creator 将更新局部变量和观察器视图,就像把断点设置到这个地方而程序正好 在这里中断时的情形一样。

12.10.6 线程视图(Thread View)

当我们调试一个多线程应用程序时,如图 12-46 所示,线程视图(thread view)和调 试器状态栏上的”Thread”组合框(见图 12-47)被用来在不同线程间切换,这时堆栈视图(stack view)也将会随着做出相应的调整。

图 12-46 线程视图

图 12-47 调试器状态栏上的 Thread 组合框

12.10.7 局部变量和观察器视图(Locals and Watchers View)

当程序在调试器的控制下中断时,Qt Creator 将取得堆栈里面的最上层框架节点的相关 信息并把它们显示在局部变量和观察器视图里面。

局部变量和观察器视图通常由一个树形结构组成 ,里面有许多的一级节点,第二级节点 等等层次的数据,比如数据结构和类等信息就不是显示在第一级节点里面的。要查看更为详 细的信息,可以逐级点开这些节点前面的 “+”号。

你也可以在局部变量和观察器中更改变量的内容(比如常见的 int 和 float 值),以界 定你想确定的变量值的限值。这可以通过双击 ”Value”栏,并在可编辑区填入你的新的取 值,然后按下回车键(Enter 或 Return 键),之后再接着调试程序。

小贴士:你对观察器里面项目的设置将被保存到这次会话里面 ,下次打开对话时,这些设置 仍然有效。

12.10.8 模块视图(Modules Views)

默认情况下,模块视图也是不显示的。它的主要作用是使开发者了解程序中用到了那些模块,严格意义上来说,它不应该是在调试模式下才有的功能。一个常见的模块视图如图12-48 所示。

图 12-48 模块视图

12.10.9 反汇编和视图(Disassembler View)和寄存器视图(Registers View)

默认情况下,反汇编视图和寄存器视图是隐藏的。反汇编视图显示了断点处所在的函数的反汇编代码,如图 12-49 所示;寄存器视图显示了当前 CPU 的寄存器的状态,如图 12-50所示,当你需要对程序的底层(与系统硬件接触)进行检视和控制时,这两个视图尤其有用。 当我们使用逐过程调试的方法时,经常会用到它们。

图 12-49 反汇编视图

图 12-50 寄存器视图

12.10.10 程序调试实例

在我们的这个 TextFinder 例子里面,我们要使用 QString 读取一个文本文件,然后再 用一个 QTextEdit 把它的内容显示出来。在其中我们定义了一个 QString 类型的变量 line, 然后在附近设置一个断点,用来查看 line 变量的内容,请大家跟着我的步骤进行调试。

图 12-51 设置断点

首先是设置断点,如图 12-51 所示,将光标移动到选定的位置,按下 F9 键,或者在行号前点击鼠标左键完成断点的设置。然后按下 F5 键,启动调试。

如图 12-52 所示,在调试模式(Debug Mode)下,我们可以在断点视图(Breakpoints View)中查看已经设置的断点情况。要取消断点,可以再次点击 F9 键。

图 12-52 查看断点设置情况

可以在局部变量和观察器视图中查看变量的内容,如图 12-53 所示,显示了 line 等变 量的内容。

图 12-53 查看 line 变量的内容

下面是我们的程序中的槽函数 on_findButton_clicked()的代码,我们将修改代码中的 部分内容,形成一个小的逻辑错误,然后示范调试的步骤。原始正确的代码如下:

QString searchString = ui_lineEdit->text();
QTextDocument *document = ui_textEdit->document();
bool found = false;
if (isFirstTime == false)
    document->undo();
if (searchString == "")
{
    QMessageBox::information(this, tr("Empty Search Field"),
        "The search field is empty. Please enter a word and click Find.");
}
else
{
    QTextCursor highlightCursor(document);
    QTextCursor cursor(document);
    cursor.beginEditBlock();
    QTextCharFormat plainFormat(highlightCursor.charFormat());
    QTextCharFormat colorFormat = plainFormat;
    colorFormat.setForeground(Qt::red);
    while (!highlightCursor.isNull() && !highlightCursor.atEnd())
    {
        highlightCursor = document->find(searchString, highlightCursor,
        QTextDocument::FindWholeWords);
        if (!highlightCursor.isNull())
        {
            found = true;
            highlightCursor.movePosition(QTextCursor::WordRight,
            QTextCursor::KeepAnchor);
            highlightCursor.mergeCharFormat(colorFormat);
        }
    }
    cursor.endEditBlock();
    isFirstTime = false;
    if (found == false)
    {
        QMessageBox::information(this, tr("Word Not Found"),
            "Sorry, the word cannot be found.");
    }
}

我们将第 6 行改为:

if (searchString != "")

大家注意,改动之处是把 比较运算符==变成了!=,这时运行程序,无论你输入任何有效的字符,程序的运行结果总是与你的预期相反。那么就需要设置断点,调试程序了。在第 一行处按下 F9 键,然后按下 F5 键开始调试,如图 12-54 所示,使用鼠标点击调试器工具栏 上的常用按钮或者按下对应的快捷键,执行逐行调试或逐过程调试均可,如图 11-54 所示。 程序单步执行到第 6 行时,你将会发现这个逻辑错误。把它更正过来,再次调试程序即可 。

图 12-54 调试器工具栏

至此,关于 Qt Creator 的使用的介绍就结束了。要想掌握好 Qt Creator,使之成为你 的左膀右臂,就需要多实践,多总结。