虫子狩猎策略?

时间:2009-12-04 16:03:36

标签: debugging language-agnostic

假设您在软件相当复杂的部分的功能测试中发现了一个错误。 它可能源于数据库中的错误/意外数据,中间层代码或前端的某些内容。

精细。我们都去过那里。

你有单元测试写和&运行,调试/记录语句插入,sql语句写入&运行,你想用FireBug检查的东西等等。

让我们说第一步是提出您想要调查的潜在原因列表。

现在你必须决定做什么顺序。

你是:

  1. 根据直觉调查订单中的原因?
  2. 从最快检查到最慢检查的顺序调查原因?
  3. 假设该错误是特定于此功能的,并从大多数特定于功能的代码调查到最少功能的特定代码?
  4. 假设这是别人的错,并从最常见的代码调查到您的特定代码?
  5. 我没有提到的其他东西?
  6. 我有一种感觉,第一种策略是最常用的。也许只是因为我不与许多初级开发人员合作,而更多高级开发人员倾向于拥有体面的直觉。或许我们只是认为我们有不错的直觉,但应该采用更系统的方法。

    有什么想法吗?

14 个答案:

答案 0 :(得分:21)

我发现Rubber Duck Debugging策略效果也很好。

答案 1 :(得分:4)

根据我的经验,最好是与肠道感觉(1)一起使用30分钟左右。

如果没有任何结果,请与其他人谈谈。

与其他人交谈(即使他们不是技术人员)真是太神奇了,可以提供帮助。

答案 2 :(得分:4)

  1. 在调试环境中重现错误。
  2. 在发生错误时检查系统状态,以找到直接明显导致错误发生的状态不一致/不正确/意外的元素。通常,仅仅关注代码和调用堆栈会立即告诉您问题所在。
  3. 将测试添加到可以在正常控制流中创建/变异此状态的所有点。
  4. 将这些测试的失败视为一个新错误,返回第二步。
  5. 冲洗,起泡,重复直至找到问题的最初原因。乏味和机械,但会让你到那里。

    除了......偶尔在步骤3的迭代中的测试不会失败;最常见的是,因为一些不相关的系统破坏内存是导致无效状态的原因,或者因为问题是线程/定时/未初始化的数据依赖并且引入测试改变时序和/或数据布局足以改变或隐藏症状。至少就这张海报来说,这个过程变得更加直观,在用较少侵入的形式取代健全性测试和选择性地禁用模块以排除腐败来源之间交替进行。

答案 3 :(得分:2)

我认为这没关系,只要它是有记录和有条理的。编程中的一个奇怪的小问题是,有时以随机顺序执行操作比花费大量时间试图找出“最佳”方式更有效。

永远不要低估直觉;这是让你头脑清醒的经历。我几乎总是从你可能认为是我的“直觉”感觉开始。我查看错误,并检查我认为可能导致此类问题的步骤。

答案 4 :(得分:2)

我在这种情况下的第一步通常是按顺序检查事项,以便最快地减少要检查的事物的数量。您几乎可以将其视为对错误的二进制搜索:“好吧,POST参数看起来正确,所以我可以在表单提交之前排除所有内容,”等等。

那就是说,如果我有一种强烈的感觉,问题可能出在某个特定的地方,我会先检查一下。

答案 5 :(得分:1)

我倾向于采取直觉和分而治之的方法;隔离大小不断减小的代码块,我认为“这个bug就是”。

如果您不了解或不理解代码库,那么这不起作用 - 如果是这种情况,请找一个做某事的人,并以他们的直觉感受。

答案 6 :(得分:1)

首先,我尝试了解这个错误,然后根据直觉感觉,按照你的建议做所有事情。 这实际上是对特定原因的确定性以及测试的容易程度的权衡。

此外,当我调查一个原因时,我尝试直接添加非常快速的检查,因为我正在检查代码(添加一些临时调试输出语句,添加断言等)

答案 7 :(得分:1)

聆听专家如何在软件工程广播中调试软件:

Dave Thomas谈到software archaeology,其中有一些非常好的调试技巧。

Andreas Zeller出现在episode致力于调试。

答案 8 :(得分:1)

一般来说,我从一系列假设开始,我认为这些假设最有可能是罪魁祸首,然后根据每个假设的容易程度对这个假设进行排序,并从最简单的方法开始。

无论顺序如何,重要的是你对你的假设做了什么。 开始尝试反驳每个假设,而不是验证它,你将覆盖更多基础(见Psychology of Intelligence Analysis,Richards J. Heuer,Jr。,免费PDF)。

答案 9 :(得分:1)

我和@moonshadow在一起,但我会在某种程度上补充说这取决于失败是什么。也就是说,某些类型的失败具有相当广为人知的原因,我将从已知的原因开始

例如,在Windows系统上,“访问冲突”错误几乎总是由于尝试使用或查看(访问)未分配的内存。要找到这种错误的来源,最好查看分配(或不分配)内存的所有位置。

如果已知“问题”是由于数据不良引起的,则修复可能需要更改数据验证或获取,即使错误被追溯到分析。

还有一点,在思考错误时,尝试创建一个用于创建它的小程序通常是值得的。

答案 10 :(得分:0)

我的订单:

  1. 查看1-2个最可能原因的代码(根据直觉选择)。
  2. 如果找不到任何内容,请在调试器中执行代码(如果不可能,请在代码中插入调试/日志记录语句。)
  3. 如果找不到任何内容,请打电话给其他人并与他/她一起重复步骤1和2。

答案 11 :(得分:0)

以下是一些有用的提示:

  • 如果您使用的语言从异常开始生成堆栈跟踪。
  • 如果可以,请获取导致问题的原始数据的副本。
  • 使用一个好的调试器。
  • 如果您有权使用某种语言,那么可以通过允许您在事件发生后快进或快退执行各种语言的ODB之类的内容。
  • 排除不可能的事情,你将得到解决方案!

答案 12 :(得分:0)

我通常这样做:

1)将新的功能测试用例添加到自动回归测试系统。 我通常使用自己的回归测试系统

启动一个软件项目
  • Excel VBA + C库来控制SCSI / IDE接口/设备(13年前),测试报告是Excel speadsheet。
  • TCL期待复杂的网络路由器系统测试。测试报告是网页。 (6年前)
  • 今天我使用Python / Expect。测试报告是XML + python基础XML分析器。

这一切的目标是确保一旦找到任何错误,它就不会再出现在签入代码或生产系统中。此外,更容易重现随机和长期问题。

不要检查任何代码,除非它过夜自动化回归测试

我通常在产品代码与测试代码之间编写1:1的比例。 20k行TCL专家,用于20K行的C ++代码。 (5年前。)例如:

  • C代码将实现安装隧道tcp连接转发代理。
  • TCL测试用例:(a)设置连接以确保数据通过。 (b)使用不同的网络元素设置连接。 (c)执行10次,100次,1000次并检查内存泄漏和系统资源问题等。
  • 对系统中的每个功能执行此操作,可以看出为什么1:1对测试程序进行编码。

我不希望QA团队使用我的测试系统进行自动化测试,因为我的所有签入代码都必须通过测试。在将代码提供给QA团队之前,我通常会进行为期2周的长期回归测试。

运行手动测试用例的QA团队还确保我的程序具有足够的内置诊断信息,以捕获任何未来的错误。目标是有足够的诊断信息来解决95%的错误。 2小时。我能够在上一个项目中做到这一点。 (RBG Networks的视频网络设备。)

2)添加诊断程序(现在的网络基础)以获取所有内部信息。 (现状,日志等)。 > 50%的代码(特别是c / c ++)是诊断代码。

3)为我不理解的故障区域添加更多详细信息日志。

4)分析信息。

5)尝试修复错误。

6)运行过夜/周末回归测试。当我在R& D时,我通常会要求至少5-10个测试系统以24x7连续进行回归测试。这通常有助于ID并在代码命中SQA之前解决内存,资源和长期性能问题。

一旦嵌入式系统不时无法启动进入Linux提示符。我添加了一个测试用例,它一次又一次地用可编程插座对系统进行电源循环,并确保它可以“看到”命令提示符并在一夜之间开始运行测试。我们能够快速识别FPGA代码问题并确保系统在5000次电源循环后始终处于运行状态。添加了一个测试用例,并构建了一个新的Verilog代码签入/ FPGA代码。这个测试用例运行了。这再也不是问题了。

答案 13 :(得分:0)

我建议阅读Debugging By Thinking。

Andreas Zeller还在系统调试研究方面做了一些工作。