前言
软件调试,是开发过程中必备的技能。通过调试可以发现软件存在的bug,即缺陷。调试可以帮助开发者窥探到软件运行过程中的详细状态,从而为消除bug提供重要信息。调试的英文是debug,前面的de在英文中表示“反对”,“消除”。本文将会介绍日常开发中常用的、也是一名合格的Qt开发人员需要掌握的Qt软件基本调试方法,并结合不同场景加以说明,帮助读者查漏补缺,打牢基本功。
常用调试方法
1. 基本调试方法
1.1 以调试启动
以调试启动,是指软件在启动的同时,调试也随即开始。
在Qt项目中,点击Qt Creator左下角调试按钮,如下所示,即可以调试启动:
在VS中调试启动按钮为:
以调试启动是最常用的调试方法,因为它可以对软件启动到退出的全过程进行调试,所以是调试的首选方法,适用于绝大多数场景。
1.2 附加到进程
附加到进程是另外一种比较常用的调试方法,它是指在软件无调试启动一段时间后,再将调试器挂载到应用程序中进行调试。这种方法通常在以下场景使用:
- 场景1:
如果软件启动速度较慢(启动慢的原因很多,比如加载资源、加载插件等等)。调试本身就会使软件变慢,以调试启动,会导致软件启动速度更慢。而采用附加到进程的调试方法,可以在软件启动后,再调试软件,节省时间。但是如果你需要调试的代码段只在软件启动时刻执行一次,那就无法用此种方法,只能使用以调试启动的方法来调试。 - 场景2:
在软件是由windows批处理启动时,一般需要使用附加到进程的方法调试。
通常使用批处理启动软件,是为了配置软件的启动环境,或进行一些启动前的预处理步骤。因为在开发环境中,启动软件仅仅能够配置命令行参数而已,无法进行复杂的环境配置,所以就无法直接以调试启动的方法调试。可能有的同学说,直接调试批处理文件是否可行,答案是不行,感兴趣的同学可以自己尝试。那么这样一来就只有使用附加到进程的方法调试了。
附加到进程,在Qt Creator中,选择菜单“调试”->“开始调试”->“关联到运行中的程序”,随后在弹出的进程列表中选择需要调试的进程即可,如下图所示:
在VS中,在菜单中选择“调试”->“附加到进程”,在弹出的进程列表中选择需要调试的进程即可,如下图所示:
1.3 启动后再调试
启动后再调试严格来说也是附加到进程。在Qt中,当软件启动后,可以点击下图所示的按钮,直接附加到进程调试:
这里就不再赘述。
2. 远程调试
远程调试用于通过网络对另外一台电脑上运行的软件进行调试。
通常用于以下场景:
- 场景1
假设目标电脑为实际运行环境。且不能安装开发环境,可能的原因有:目标电脑版本较老、性能较差、不具备安装开发环境的条件,抑或是时间紧急,没有时间安装配置开发环境。
但是目标电脑可以接入网络,包括局域网和广域网。这种情况下可以在目标电脑上启动调试服务器;在自己的开发电脑上,在VS中使用源码实现远程调试。
调试服务器,又叫远程调试器,Remote Debugger,本地 VS 安装目录下的找到msvsmon.exe,拷贝到目标电脑上运行。
随后在目标电脑上配置调试服务器,包括监听端口、防火墙等。
在调试电脑上的VS 工程中,打开项目属性,点击调试选项卡,切换到远程 Windows 调试器进行配置。具体的配置过程,限于篇幅这里不再赘述,大家可自行搜索。
需要强调的是,发布的debug版本的软件一定要包含.pdb文件,即调试信息文件,它包含了符号的位置信息,用于定位源码,无pdb文件将无法调试。
3. windbg调试
windbg调试是指使用windbg调试软件在目标电脑上进行调试,这种调试方法的优点是不需要安装开发环境,不需要连接网络。只需要将目标电脑上运行的软件对应的源码,以及和目标电脑对应位数、版本的windbg软件拷贝到目标电脑上即可。
windbg调试时,需要进行源码目录配置等操作。windbg支持单步执行,执行到指定位置、查看变量、附加到进程等诸多功能,基本包含Qt/VS中的调试功能,甚至更加强大。当然,使用windbg调试需要一定的学习成本,因为其只在特定的场景下使用,一般只需要在用的时候根据网络上的教程现学即可。
和上面一样,被调试的软件必须含有.pdb文件,否则无法调试。
4. 仅日志调试
上面说的调试方法,都是使用调试器进行调试,可以看到软件运行时的详细信息。但是上述方法的缺点是必须需要和运行的目标软件建立某种连接。而日志调试不需要建立实质性的连接,日志仅仅为软件运行过程中调试信息的记录文本。日志调试需要先打印出重要的运行信息,在排查软件问题时,通过分析日志记录,推断出软件出现问题的原因。日志调试是一种在软件开发后期最省时省力、成本最低的调试方法。但是实现日志调试是一种从编写软件的第一行代码开始就进行的长期、持续的工作。编写代码时,在重要的位置上打印出重要的信息,是实现日志调试的必要工作。
Qt中提供了日志输出机制。如qDebug()、qInfo()、qCritical()、qFatal(),用于向控制台打印信息,当然也可以重定向输出到文件,后面的文章会加以说明。除此之外还有断言Q_ASSERT,可以在调试版本的软件运行时,在关键位置进行条件校验,校验失败时,弹出对话框提示并指明错误代码位置。
日志调试在软件中是必不可少的存在,任何一个良好的软件都会有日志记录。从另一个角度看,在编码过程中,我们要培养打印日志的好习惯,这也是提高编码水平的重要内容。
调试场景
1. 开发阶段调试
因为开发阶段一般环境准备比较充分,大部分时间使用调试器进行调试,包含上面提到的基本调试方法、远程调试方法、windbg调试方法。使用调试器调试可以直接看到内存变量、程序执行位置、对执行进行控制,可以快速定位问题。
软件调试不是目的,软件在设计和编码阶段要充分考虑各种输入输出情况,对异常的处理分支不能不管不问,不可图懒省事,要对软件质量负责,这是开发者的本职工作,是减少软件缺陷的关键。我们不仅要学习调试技术,还要建立起对软件正确的态度,成为一名合格的工程师。
2. 部署、维护阶段调试
部署维护阶段,因为部署维护时的软件运行环境会发生巨大变化,可能因为种种原因,调试会出现困难。这要求我们掌握并灵活运用多种调试手段,根据实际情况选择合适的调试方法,或者综合运用各种调试方法。在不具备调试条件时,通过协商、沟通创建必要的调试环境,最后解决软件问题,防止因软件问题产生损失,这会给我们带来很大的成就感。
结语
本文对笔者在工作中关于软件调试的一些经验方法进行了系统的总结,希望对想要做出优秀软件的同学有所帮助。
大家如有任何疑问,欢迎评论交流。
本文由微信公众号“Qt未来工程师”原创发布。