软件调试实战入门(1)
本系列文章是阅读《软件调试实战》人民邮电出版社 Thorsten Grotker、Ulrich Holtmann 、Holger Keding、Markus Wloka(德)著,赵俐译的读书笔记
主要是针对的是C/C++语言的调试问题,调试的工具是GCC和GDB工具,本人用的OS为chakra linux中文版,GCC为4.9.2-1, GDB为7.9-1。软件开发过程中会遇到各种缺陷和错误,软件调试成了许多程序的主要工作,调试质量直接影响着软件的成品质量。调试有时会占用软件开发中的大部分时间,因此调试技巧的提高是非常有必要的。软件调试的特殊性,决定着不能全盘采用理查德.费曼(Richard Feynman)的”写下问题,努力思考、写下答案“的方法进行有效解决。好的调试技巧成了一项必备技巧,但是工科院校中很少或不开程序调试课程。主要包括源码调试、程序链接内存存取、并行处理及性能分析等。
系统性调试方法:
一)、充分利用机会
(1)源代码中使用可调试的方法编写,可以增加输出语句,检测程序的动向,术语“插装”来提高软件的可观察性和控制性。
(2)通过使用宏定义也可以实现程序的插装,宏定义被提供给负责处理源代码和包含头文件的预处理器。
(3)编译器标志,也是代码调试和剖析的必须要素。一些警告信息也需要注意,加上静态检查工具就差不多可以了。
(4)链接时的选项,尽量使用带有调试信息的库,强制将更多测试例程链接到最后的执行文件中,也可以使用可对执行程序进行插装的工作来分析性能和内存的存取。
(5)对可执行文件使用各种测试工具,源代码调试器、剖析工具、内存检查器、甚至是根据OS调用的程序。
二)、构建和测试中的13条黄金规则
(1)理解需求:开始调试和修复错误前,务必要理解需求,可以通过标准文档和规格说明即可。
(2)制造失败:构造测试用例、理解软件失败的所有因素,并一一化解。
(3)简化测试用例:排除次要因素,减少测试用例的运行时间、用例须让程序更容易调试。
(4)读取恰当的错误消息:排除错误时尽量按照错误出现的顺序解决,只有理由特别充足,才能打破这个常规。
(5)检查显而易见的问题:发生问题时:软件是否所有部分运行和启动,是否拥有权限、磁盘空间是否充足、内存是否满了等等。
(6)从解释中分离事实。
(7)分而治之:如果问题很容易解决,则直接解决,否则将问题分解成多个更小的问题,将各个小问题的解决方法整合,进而形成原始问题的解决方案。A:整理清单,列写潜在的问题及如何调试; B:将环境更改和源码更改区分开;C:放大并治之。
(8)工具与BUG匹配:调试容易出现问题的地方而不是容易调试的地方,比如内存调试就很麻烦,但是出的问题也最多。
(9)一次只做一次更改,出现问题即使还原,将问题细化使之更容易解决。
(10)保持开发跟踪:测试时要几下做了哪些事情,执行书须及输出的结果及故障能否重复。
(11)获得全新观点:陷入僵局是,将事实和自己的理论划开界限,和别人讨论,强化理论理解,正确出现新的观点。
(12)BUG不会自己修复:修改语句后,bug可能会消失,若没有确切的证据表明bug彻底消失时,须要假设bug一直存在。
(13)使用回归测试检查bug修复。
三)、构建一个好工具包
在软件大崩溃前,安装并测试一系列完整的测试工具,不但可以解决各种浅显的问题。主要需要以下工具:
(1)源代码调试器
(2)内存调试器
(3)剖析工具
(4)运行并维护单元测试和系统测试
需要保证调试器和编译器的兼容性,保证工具箱的正常运行,每天测试,防止出现bug,主要有:
(1)回归测试
(2)单元测试
(3)系统测试
单元测试主要包括黑盒测试:主要是验证模块的功能是否与预期一致,保证代码的移植性;白盒测试:主要是测试边界及特殊情况,防止一些低级错误。
四)、认清bug
(1)常见bug
(2)偶发性bug
(3)Heisenbug,包括:资源争用、内存非法使用、优化错误等
(4)bug后的bug,需要多个地方修改,记录要详细
(5)秘密bug,主要有:自己尝试再现相同的bug,现场调试及使用安全的连接等。