软件调试实战

系统性调试方法
  • 13条黄金规则:
  1. 理解需求
  2. 制造失败
  3. 简化测试用例:目的:排除不起作用的因素 ;减少测试用例的运行时间;最重要的是,使测试用例更容易调试。
  4. 读取恰当的错误信息:重点关注首先出现的那些消息。
  5. 检查显而易见的问题
  6. 从解释和中分离出事实
  7. 分而治之:整理一份清单,列出潜在问题以及如何调试它们(调试归根到底是一种试错法trial-and-error);将环境更改和源代码更改区分开(跟踪环境的更改,撤销源代码的更改);放大并治之(内存调试,常规的源代码调试,同步调试)
  8. 工具要与bug匹配:要调试的是出现问题的地方,而不是便于调试的地方;关注那些最有可能找到bug的方面。
  9. 一次只做一项更改:在调试器期间为修改的源代码加注释指明修改的类型和原因是一个好习惯;调试时尽量一次解决一个问题。
  10. 保持审计跟踪:进行手工测试时,要记下做了哪些事情,执行的顺序以及出现了什么结果;让程序创建日志文件并输出状态消息。
  11. 获得全新观点:当陷入僵局是,可以找别人讨论一下。
  12. bug不会自己修复:除非有很充分的理由说明修复很有效,否则最好假设bug依然存在,并且未来还会发作。
  13. 用回归测试(regression test)来检查bug修复。
  • 构建一个好的工具包
  1. 源代码调试器,内存调试器,剖析工具
  2. 当更换编译器版本时,应检查整个调试工具套件是否依然工作。
  3. 在整个系统可运行之前开发单元测试,可以提高软件的可观察性和可控制性。
  4. 单元测试:黑盒:主要验证组件的预期功能,而忽略其实际的实现,具有很好的移植性;白盒:主要测试实现的边界情况以及一些“弄巧成拙的错误”。
  • bug家族
  1. 常见bug:行为是可以预测的。
  2. 偶发性bug:很难定位,找到一个测试用例,可以再现这些bug。可在程序中加入“看门狗”代码捕捉。
  3. Heisenbug:原因:资源争用(共性:未定义执行顺序);非法使用内存(如读取未初始化的变量,野指针,数组越界);优化错误(编译器优化时常出现bug,有时需要关闭优化来调试)。
  4. 隐藏在bug背后的bug:某些情况下,为了避免程序行为的异常,需要修复多个问题。(需要违反“一次只做一项更改”规则)
  5. 秘密bug--调试和机密性(客户无法发送测试用例给我们,我们无法发送有源代码的调试版本给客户):办法:自己尝试再现相同的bug;现场调试;使用安全的连接(远程调试,客户计算机运行可调试的软件,但源代码在调试者的计算机上)。
查找根源---源代码调试器
  1. 大多数编译器优化在调试模式下是禁止的,因此程序会运行较慢。
  2. C/C++程序的栈是一个内存片段,用来存储每个活动的函数调用的stack frame,stack frame由返回地址,函数的参数和局部变量组成。
修复内存问题
  • C/C++中的内存管理---功能强大但很危险
  1. 内存泄漏:在运行时分配但在程序不再需要它时未释放的数据结构。常规调试器很难找到,没有明显的错误语句,而是丢失或者忘记调用某条语句。
  2. 内存管理的错误使用:如多次释放一个内存块,释放一个内存块之后又访问它,释放一个从未分配的内存块,使用delete而不是delete[]来释放数组,malloc和delete混用,new和free混用。
  3. 缓冲区溢出:在已分配内存的外部的内存被改写或破坏。导致内存破坏的语句与触发可见bug的语句之间没有明确的关系。
  4. 未初始化的内存bug:未初始化的变量将包含无法预料的值。
  • 有效的内存调试器:Purify, Insure++, Valgrind, BoundsChecker.
  • 内存调试器可以检测以下bug:内存泄漏,访问已释放的内存,多次释放同一个内存,释放从未分配的内存,混用malloc/free和new/delete,对数组使用delete而不是delete[],数组越界,访问从未分配的内存,读取未初始化的内存,读或者写空指针。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值