【问题标题】:Bug Hunting Strategies?捕虫策略?
【发布时间】:2009-12-04 16:03:36
【问题描述】:

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

很好。我们都去过那里。

您有要编写和运行的单元测试、要插入的调试/记录语句、要编写和运行的 sql 语句、要使用 FireBug 检查的内容等等。

假设第一步是列出您想要调查的潜在原因。

现在你必须决定做事的顺序。

你:

  1. 根据直觉按顺序调查原因?
  2. 按从最快检查到最慢检查的顺序调查原因?
  3. 假设该错误是特定于该功能的,并从大多数特定于功能的代码到最少的特定于功能的代码进行调查?
  4. 假设是别人的错,并从最通用的代码到您的特定代码进行调查?
  5. 还有什么我没有提到的?

我感觉第一个策略是最常用的。也许只是因为我没有和很多初级开发人员一起工作,而更高级的开发人员往往有不错的直觉。或者也许我们只是认为我们有不错的直觉,但实际上应该使用更系统的方法。

有什么想法吗?

【问题讨论】:

  • 6.谷歌寻找错误或在 stackoverflow 上发帖
  • 一个想法 - 这应该是社区维基。
  • @01,我仍然不完全理解社区 wiki 问题的推理或好处,尽管在 meta 上阅读了它们。似乎我不是唯一一个对此功能感到困惑的人。但我想我会尝试(至少一次)看看会发生什么。
  • 6.从最近更改的代码开始。

标签: debugging language-agnostic


【解决方案1】:

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

【讨论】:

  • 我一直听说计算机科学教授在他的办公室前有一只毛绒玩具熊的故事(可能是杜撰的)。在他的办公时间内,任何想问他问题的学生都必须先问熊;如果学生问熊后仍然不能解决问题,学生可以再问教授。我的妻子正在学习编程——我想我会尝试让我的猫担任这个角色(尤其是聋哑的)。
  • +1。出于同样的原因,我给 Bravex +1。另外我不知道这个词。
  • “橡皮熊调试”听起来有点奇怪
【解决方案2】:

根据我的经验,最好按照直觉 (1) 进行 30 分钟左右。

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

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

【讨论】:

  • 这是一个认知怪癖。我们是语言生物,因此将您的内心直觉转化为声音的活动会迫使您从另一个角度看待问题。
  • +1。我忘了提到和某人谈论它。我经常发现解释的行为可以让你头脑中的一个灯泡亮起来,当场解决问题。
【解决方案3】:
  1. 在调试环境中重现该错误。
  2. 检查错误发生时的系统状态,以发现直接明显导致错误发生的不一致/不正确/意外的状态元素。通常,只需查看代码和调用堆栈即可立即告诉您问题所在。
  3. 将测试添加到可以在正常控制流程中创建/更改此状态的所有点。
  4. 将这些测试的失败视为新错误,返回到第二步。

冲洗,起泡,重复直到找到问题的最初原因。乏味和机械,但会带你到那里。

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

【讨论】:

    【解决方案4】:

    我会说没关系,只要它有文档且有条理。编程中有一个奇怪的小真理,有时以随机顺序做事比花费大量时间试图找出“最佳”方式更有效。

    永远不要低估直觉;那是让您注意的经验。我几乎总是从你可能认为是我的“直觉”的感觉开始。我查看了错误,并检查了我认为可能导致此类问题的步骤。

    【讨论】:

      【解决方案5】:

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

      也就是说,如果我强烈认为问题可能出在某个特定位置,我会先检查一下。

      【讨论】:

        【解决方案6】:

        我倾向于凭直觉和分而治之的方法;在我认为“错误”所在的地方隔离大小减小的代码块。

        如果您不知道或不了解代码库,这将不起作用 - 如果是这种情况,请找一个懂的人,并按照他们的直觉行事。

        【讨论】:

          【解决方案7】:

          首先,我尝试了解错误,然后根据直觉执行您建议的所有操作。 这实际上是您对特定原因的确定程度以及测试的容易程度之间的权衡。

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

          【讨论】:

            【解决方案8】:

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

            Dave Thomas 谈到了software archaeology,它提供了一些非常棒的调试技巧。

            Andreas Zeller 出现在一个专门用于调试的 episode

            【讨论】:

              【解决方案9】:

              一般来说,我从我认为最有可能的罪魁祸首的假设子集开始,然后根据每个假设的反驳难易程度对假设子集进行排序,然后从最简单的假设开始。

              不管顺序如何,重要的是你如何处理你的假设。 开始尝试反驳每个假设,而不是验证它,这样您将覆盖更多领域(请参阅 Richards J. Heuer, Jr. 的 Psychology of Intelligence Analysis,免费 PDF)。

              【讨论】:

                【解决方案10】:

                我支持@moonshadow,但我会补充一点,这在某种程度上取决于失败的原因。也就是说,某些失败的原因是众所周知的,我会从已知原因开始

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

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

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

                【讨论】:

                  【解决方案11】:

                  我的订单:

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

                  【讨论】:

                    【解决方案12】:

                    这里有一些有用的提示:

                    • 如果您使用的语言会生成异常堆栈跟踪,则从那里开始。
                    • 如果可以,请获取导致问题的原始数据的副本。
                    • 使用好的调试器。
                    • 如果您可以访问其中一个,则可以使用各种语言的 ODB 之类的东西,通过允许您在事件发生后快进或倒退执行来提供帮助
                    • 排除不可能,您将获得解决方案!

                    【讨论】:

                      【解决方案13】:

                      我通常这样做:

                      1) 向自动化回归测试系统添加一个新的功能测试用例。 我通常用自己的回归测试系统来启动一个软件项目

                      • 用于控制 SCSI/IDE 接口/设备的 Excel VBA + C 库(13 年前),测试报告为 Excel 电子表格。
                      • TCL Expect 用于复杂网络路由器系统测试。测试报告是网页。 (6 年前)
                      • 今天我使用 Python/Expect。测试报告是 XML + python base XML 分析器。

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

                      不要签入任何代码,除非它经过一夜的自动化回归测试

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

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

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

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

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

                      3) 为我不明白的问题区域添加更多详细日志。

                      4) 分析信息。

                      5) 尝试修复错误。

                      6) 通宵/周末运行回归测试。 当我从事研发工作时,我通常要求至少 5-10 个测试系统以 24x7 连续运行回归测试。这通常有助于在代码到达 SQA 之前识别并解决内存、资源和长期性能问题。

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

                      【讨论】:

                        【解决方案14】:

                        我建议阅读 Debugging By Thinking。

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

                        【讨论】:

                          猜你喜欢
                          • 2011-02-04
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2010-11-21
                          • 1970-01-01
                          • 2019-09-25
                          • 1970-01-01
                          • 2014-07-03
                          相关资源
                          最近更新 更多