73

这个问题说明了一切。如果你有多个用户报告的bug,但是日志中没有出现bug的记录,也不能重复出现bug,无论你怎么努力,你如何修复它?或者你可以吗?

我相信这在你们中的许多人身上都发生过。在这种情况下你做了什么,最后的结果是什么?


编辑:我更感兴趣的是对无法找到的错误所做的事情,而不是无法解决的错误。无法解决的错误是这样的,您至少知道存在问题并且在大多数情况下有一个起点来搜索它。万一找不着了,怎么办?你甚至可以做任何事情吗?

4

15 回答 15

87

语言

不同的编程语言会有自己的 bug 风格。

C

添加调试语句可以使问题无法复制,因为调试语句本身将指针移动得足够远以避免 SEGFAULT ---也称为Heisenbugs。指针问题很难跟踪和复制,但调试器可以提供帮助(例如GDBDDD)。

爪哇

具有多个线程的应用程序可能仅通过非常特定的时间或事件序列来显示其错误。在难以复制的情况下,不正确的并发实现可能会导致死锁。

JavaScript

一些 Web 浏览器因内存泄漏而臭名昭著。在一个浏览器中运行良好的 JavaScript 代码可能会导致在另一个浏览器中出现错误行为。使用经过数千名用户严格测试的第三方库有助于避免某些晦涩的错误。

环境

根据应用程序(有错误)运行的环境的复杂性,唯一的办法可能是简化环境。应用程序是否运行:

  • 在服务器上?
  • 在桌面上?
  • 在网络浏览器中?

应用程序在什么环境中产生问题?

  • 发展?
  • 测试?
  • 生产?

退出无关的应用程序、终止后台任务、停止所有计划的事件(cron 作业)、消除插件和卸载浏览器加载项。

联网

由于网络对于许多应用程序至关重要:

  • 确保稳定的网络连接,包括无线信号。
  • 网络故障后软件是否会重新连接?
  • 是否所有连接都正确关闭以释放文件描述符?
  • 人们是否在使用不该使用的机器?
  • 恶意设备是否与机器的网络交互?
  • 附近是否有工厂或无线电塔会造成干扰?
  • 数据包大小和频率是否在标称范围内?
  • 是否正在监视数据包是否丢失
  • 是否所有网络设备都足以使用大量带宽?

一致性

消除尽可能多的未知数:

  • 隔离建筑组件。
  • 删除非必要的或可能有问题(冲突)的元素。
  • 停用不同的应用程序模块。

消除生产、测试​​和开发之间的所有差异。使用相同的硬件。按照完全相同的步骤完美地设置计算机。一致性是关键。

日志记录

使用大量的日志记录来关联事件发生的时间。检查日志是否有任何明显的错误、时间问题等。

硬件

如果软件看起来没问题,请考虑硬件故障:

  • 物理网络连接是否牢固?
  • 是否有松动的电缆?
  • 芯片是否正确就位?
  • 所有电缆的连接是否干净?
  • 工作环境是否干净无尘?
  • 是否有任何隐藏的设备或电缆被啮齿动物或昆虫损坏?
  • 驱动器上有坏块吗?
  • CPU 风扇工作正常吗?
  • 主板可以为所有组件供电吗?(CPU、网卡、显卡、驱动器等)
  • 电磁干扰会是罪魁祸首吗?

主要用于嵌入式:

  • 供应不足绕过?
  • 板子污染?
  • 焊点不良/回流不良?
  • 电源电压超出公差时 CPU 不会复位?
  • 复位不良是因为电源轨从 I/O 端口反向供电并且没有完全放电?
  • 闭锁?
  • 浮动输入引脚?
  • 逻辑电平上的噪声容限不足(有时为负)?
  • 时间裕度不足(有时为负)?
  • 锡须
  • 静电损坏?
  • ESD扰动?
  • 芯片勘误?
  • 接口误用(例如 I2C 板外或存在高功率信号)?
  • 比赛条件?
  • 假冒组件?

网络与本地

当您在本地运行应用程序(即,不通过网络)时会发生什么?其他服务器是否也遇到同样的问题?数据库是远程的吗?可以使用本地数据库吗?

固件

介于硬件和软件之间的是固件。

  • 计算机 BIOS 是否是最新的?
  • BIOS 电池工作正常吗?
  • BIOS 时钟和系统时钟是否同步?

时间和统计

时序问题很难跟踪:

  • 什么时候出现问题?
  • 多频繁?
  • 当时还有哪些其他系统正在运行?
  • 应用程序是否对时间敏感(例如,闰日或闰秒是否会导致问题)?

收集有关该问题的硬数字数据。一个起初可能看起来是随机的问题,实际上可能有一个模式。

更换管理层

有时系统升级后会出现问题。

  • 问题是从什么时候开始的?
  • 环境(硬件和软件)发生了什么变化?
  • 回滚到以前的版本后会发生什么?
  • 有问题的版本和好的版本有什么区别?

图书馆管理

不同的操作系统有不同的方式来分发冲突的库:

  • Windows 有DLL 地狱
  • Unix 可能有许多损坏的符号链接。
  • Java 库文件同样难以解决。

执行操作系统的全新安装,并仅包括您的应用程序所需的支持软件。

爪哇

确保每个库只使用一次。有时应用程序容器的库版本与应用程序本身不同。这可能无法在开发环境中复制。

使用库管理工具,例如MavenIvy

调试

编写当错误发生时触发通知(例如,日志、电子邮件、弹出窗口、寻呼机哔声)的检测方法。使用自动化测试将数据提交到应用程序中。使用随机数据。使用涵盖已知和可能的极端情况的数据。最终,该错误应该再次出现。

睡觉

值得重申的是其他人提到的:睡吧。花时间远离问题,完成其他任务(如文档)。远离电脑并进行一些锻炼。

代码审查

逐行浏览代码,并描述每一行对你自己、同事或橡皮鸭的作用。这可能会导致有关如何重现错误的见解。

宇宙辐射

宇宙射线可以翻转比特。由于现代的内存错误检查,这不像过去的问题那么大。离开地球保护的硬件软件会遇到由于宇宙辐射的随机性而根本无法复制的问题。

工具

有时,尽管很少,编译器会引入错误,特别是对于小众工具(例如,遭受符号表溢出的 C 微控制器编译器)。是否可以使用不同的编译器?工具链中的任何其他工具是否会引入问题?

于 2009-08-12T20:14:07.837 回答
13

如果它是一个 GUI 应用程序,那么观察客户生成错误(或尝试生成错误)是非常宝贵的。毫无疑问,他们正在做一些你从未猜到他们正在做的事情(没有错,只是不同)。

否则,请将您的日志集中在该区域。记录大部分内容(您可以稍后将其取出)并让您的应用程序也转储其环境。例如机器类型、VM 类型、使用的编码。

您的应用程序是否报告版本号、内部版本号等?您需要它来准确确定您正在调试的版本(或不调试!)。

如果您可以检测您的应用程序(例如,如果您在 Java 世界中,则使用 JMX)然后检测相关区域。存储统计信息,例如请求+参数、时间等。利用缓冲区存储最近的“n”个请求/响应/对象版本/任何内容,并在用户报告问题时将它们转储出来。

于 2009-08-12T20:04:51.837 回答
9

如果你不能复制它,你可以修复它,但不知道你已经修复了它。

我已经对错误是如何触发的(即使我不知道这种情况是如何发生的)做出了最好的解释,修复了它,并确保如果错误再次出现,我们的通知机制会让未来的开发人员知道我希望我知道的事情。在实践中,这意味着在跨越可能触发错误的路径时添加日志事件,并记录相关资源的指标。当然,还要确保测试总体上很好地执行了代码。

决定添加哪些通知是一个可行性和分类问题。因此,首先要决定开发人员在错误上花费多少时间。如果不知道错误的重要性,就无法回答。

我有好的结果(没有再次出现,代码更适合它),也有不好的结果(花了太多时间没有解决问题,无论错误最终修复与否)。这就是估计和问题优先级的目的。

于 2009-08-19T00:38:15.130 回答
8

有时我只需要坐下来研究代码,直到找到错误。尝试证明该错误是不可能的,并且在此过程中您可能会找出可能出错的地方。如果你真的成功地说服自己这是不可能的,假设你在某个地方搞砸了。

添加一堆错误检查和断言来确认或否认您的信念/假设可能会有所帮助。某些事情可能会失败,这是您从未预料到的。

于 2009-08-12T22:05:25.507 回答
6

这可能很困难,有时几乎是不可能的。但我的经验是,如果你花足够的时间在它上面,你迟早能够重现和修复错误(如果花的时间值得,那是另一回事)。

在这种情况下可能会有所帮助的一般建议。

  • 如果可能,添加更多日志记录,以便下次出现错误时获得更多数据。
  • 询问用户是否可以复制错误。如果是,您可以让他们在监视他们的同时复制它,并希望找出触发错误的原因。
于 2009-08-12T20:00:58.130 回答
5

进行随机更改,直到某些东西起作用:-)

于 2009-08-20T16:44:47.080 回答
4

假设您已经添加了所有您认为会有所帮助的日志记录,但它并没有……想到两件事:

  1. 从报告的症状向后工作。想想自己......“我想产生报告的症状,我需要执行哪些代码,我将如何获得它,我将如何获得它?” D 导致 C 导致 B 导致 A。接受如果错误不可重现,那么常规方法将无济于事。我不得不盯着代码看了好几个小时,这些思维过程一直在寻找一些错误。通常事实证明这是非常愚蠢的事情。

  2. 记住 Bob 的第一条调试定律:如果你找不到东西,那是因为你找错了地方 :-)

于 2009-08-12T20:14:54.543 回答
4

思考。难的。把自己锁起来,不许打扰。

我曾经有一个错误,证据是损坏数据库的十六进制转储。指针链被系统地搞砸了。所有用户的程序,以及我们的数据库软件,都在测试中完美运行。我盯着它看了一个星期(它是一个重要的客户),在排除了几十个可能的想法之后,我意识到数据分布在两个物理文件中,并且损坏发生在链跨越文件边界的地方。我意识到如果备份/恢复操作在关键点失败,这两个文件最终可能会“不同步”,恢复到不同的时间点。如果您随后在已经损坏的数据上运行客户的程序之一,它将准确地产生我所看到的打结的指针链。然后,我演示了一系列准确再现腐败的事件。

于 2014-08-04T15:26:31.967 回答
3

修改您认为发生问题的代码,以便在某处记录额外的调试信息。下次发生时,您将拥有解决问题所需的一切。

于 2009-08-12T20:04:38.013 回答
3

有两种类型的错误是您无法复制的。你发现的那种,和别人发现的那种。

如果你发现了这个错误,你应该能够复制它。如果您无法复制它,那么您根本没有考虑导致该错误的所有促成因素。这就是为什么每当你有一个错误,你应该记录它。保存日志,获取屏幕截图等。如果你不这样做,那么你怎么能证明这个 bug 真的存在呢?也许这只是一个错误的记忆?

如果其他人发现了一个错误,而您无法复制它,显然请他们复制它。如果他们无法复制它,那么您尝试复制它。如果您不能快速复制它,请忽略它。

我知道这听起来很糟糕,但我认为这是合理的。您复制其他人发现的错误所花费的时间非常长。如果错误是真实的,它自然会再次发生。有人,也许甚至你,会再次偶然发现它。如果难以复制,那也是比较少见的,多出现几次估计也不会造成太大的伤害。

如果您将时间花在实际工作、修复其他错误和编写新代码上,您的工作效率会比您尝试复制一个您甚至无法保证确实存在的神秘错误要高得多。等它自然地再次出现,你就可以把所有的时间都花在修复上,而不是浪费时间试图揭示它。

于 2009-08-12T20:12:16.237 回答
2

讨论问题,阅读代码,通常是相当多的。我们通常成对进行,因为您通常可以很快地通过分析消除可能性。

于 2009-08-12T19:59:18.743 回答
2

首先查看您可以使用哪些工具。例如,Windows 平台上的崩溃转到 WinQual,因此如果您遇到这种情况,您现在可以获得崩溃转储信息。您是否可以发现潜在错误的静态分析工具、运行时分析工具、分析工具?

然后看输入和输出。在用户报告错误的情况下,输入有什么相似之处,或者输出中有什么不合适的地方吗?编制一份报告清单并寻找模式。

最后,正如大卫所说,盯着代码。

于 2009-08-12T22:54:04.740 回答
1

要求用户为您提供远程访问他的计算机并自己查看所有内容。要求用户制作一个关于他如何重现此错误的小视频并将其发送给您。

当然两者并不总是可能的,但如果它们是可能的,它可能会澄清一些事情。查找错误的常用方法仍然相同:分离可能导致错误的部分,尝试了解正在发生的事情,缩小可能导致错误的代码空间。

于 2009-08-15T13:52:30.737 回答
0

有诸如 gotomeeting.com 之类的工具,您可以使用这些工具与用户共享屏幕并观察行为。可能存在许多潜在问题,例如他们机器上安装的软件数量,某些工具实用程序与您的程序冲突。我相信 gotomeeting 不是唯一的解决方案,但可能存在超时问题、互联网速度慢的问题。

大多数时候我会说软件不会报告您正确的错误消息,例如,在 java 和 c# 跟踪每个异常的情况下.. 不要捕获所有但保留一个可以捕获和记录的点。除非您使用远程桌面工具,否则 UI 错误很难解决。大多数情况下,它甚至可能是第三方软件中的错误。

于 2009-08-12T20:05:44.640 回答
-2

如果您在一个真正的大型应用程序上工作,您可能会有 1,000 个错误的队列,其中大多数肯定是可重现的。

因此,恐怕我可能会以 WORKSFORME (Bugzilla) 的形式关闭该错误,然后继续修复一些更具体的错误。或者做项目经理决定做的任何事情。

当然,进行随机更改是一个坏主意,即使它们是本地化的,因为您可能会引入新的错误。

于 2012-08-21T07:13:16.827 回答