你如何修复一个你无法复制的bug?

这个问题说明了一切。 如果您有多个用户报告的错误,但没有logging日志中发生的错误,也不能重复错误,无论您尝试多么困难,您如何解决? 甚至可以吗?

我确信这发生在你身边的很多人身上。 你在这种情况下做了什么,最后的结果是什么?


编辑:我更感兴趣的是一个无法解决的错误,而不是一个无法解决的错误。 无法解决的错误是这样的,至less你知道有一个问题,并有一个起点,在大多数情况下,寻找它。 在一个不能expression的情况下,你会做什么? 你甚至可以做任何事情吗?

这些被称为Heisenbugs 。

语言

不同的编程语言会有自己的错误。

C

添加debugging语句会使问题无法复制,因为debugging语句本身会移动指针(足以避免SEGFAULT)。 指针问题是跟踪和复制的噩梦,但有些debugging器(如GDB和DDD )可以提供帮助。

Java的

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

JavaScript的

一些网页浏览器因内存泄漏而臭名昭着。 在一个浏览器中运行正常的JavaScript代码可能会导致其他浏览器的不正确行为。 使用经过成千上万用户严格testing的第三方库可以避免某些模糊的错误。

环境

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

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

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

  • 发展?
  • testing?
  • 生产?

退出多余的应用程序,终止后台任务,停止所有预定的事件(cron作业),消除插件和卸载浏览器加载项。

联网

由于networking对于如此多的应用程序至关重要

  • 确保稳定的networking连接,包括无线信号。
  • 软件在networking故障后重新连接吗?
  • 是否所有的连接都正确closures以释放文件描述符?
  • 使用机器的人不应该是谁?
  • stream氓设备是否与机器的networking进行交互?
  • 附近是否有可能造成干扰的工厂或无线电塔?
  • 数据包大小和频率是否在标称范围内?
  • 所有networking设备是否适合繁重的带宽使用?

一致性

消除尽可能多的未知数:

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

消除生产,testing和开发之间的所有差异。 使用相同的硬件。 完全按照完全相同的步骤安装电脑。 一致性是关键。

logging

使用大量的日志logging来关联发生的时间事件。 检查日志是否有明显的错误,计时问题等

硬件

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

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

主要是embedded式的:

  • 电源旁路不足?
  • 电路板污染?
  • 坏的焊点/回stream不良?
  • 电源电压超差时CPU不复位?
  • 坏的重置,因为电源轨是由I / O端口供电,并没有完全放电?
  • 闭锁?
  • 浮动input引脚?
  • 逻辑电平上的噪声余量不足(有时是负值)?
  • 时间余量不足(有时是负值)?
  • 锡须?
  • ESD损害?
  • ESD干扰?
  • 芯片勘误表?
  • 接口误用(例如,I2C板外或存在高功率信号)?
  • 比赛条件?
  • 假冒组件?

networking与本地

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

固件

在硬件和软件之间是固件。

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

时间和统计

定时问题很难跟踪:

  • 问题何时发生?
  • 多频繁?
  • 当时还有哪些系统正在运行?
  • 应用程序是否具有时间敏感性(例如,闰秒或闰秒会导致问题)?

收集关于这个问题的硬数值数据。 一开始可能出现的问题可能实际上有一个模式。

更换pipe理层

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

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

图书馆pipe理

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

  • Windows有DLL地狱
  • Unix可以有许多破碎的符号链接。
  • Java库文件可以同样噩梦来解决。

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

Java的

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

使用Maven或Ivy等图书馆pipe理工具。

debugging

编写一个检测方法,在发生错误时触发通知(例如,日志,电子邮件,popup窗口,传呼机哔声)。 使用自动化testing将数据提交到应用程序。 使用随机数据。 使用涵盖已知和可能边缘情况的数据。 最终该错误应该重新出现。

睡觉

值得重申其他人提到的:睡在它上面。 花时间远离问题,完成其他任务(如文档)。 身体远离电脑,锻炼身体。

代码审查

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

宇宙辐射

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

工具

偶尔发生,特别是对于小众工具(例如,微控制器的C语言编译器遇到符号表溢出)。

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

否则,集中您的日志在该地区。 logging大部分内容(稍后可以将其拉出),并让应用程序转储其环境。 例如机器types,VMtypes,使用的编码。

您的应用报告了版本号,内部版本号等? 您需要这个来确定您正在debugging的版本(或不是!)。

如果您可以testing您的应用程序(例如,如果您处于Java世界中,则使用JMX),然后对有问题的区域进行testing。 存储统计数据,如请求+参数,时间等。利用缓冲区来存储最后的'n'个请求/响应/对象版本/任何内容,并在用户报告问题时将其转储出去。

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

我已经对如何触发错误做了最好的解释(即使我不知道这种情况会如何发生),修正了这个错误,并确保如果错误再次出现,我们的通知机制会让未来的开发人员知道我希望知道的事情。 实际上,这意味着当可能引发错误的path被越过时,添加日志事件,并logging相关资源的度量。 当然,确保testing通常能很好地执行代码。

确定要添加的通知是可行性和分类问题。 那么决定开发人员花在bug上的时间有多less呢? 如果不知道错误的重要性,就不能回答。

我得到了很好的结果(没有再次出现,代码更好),不好(花费了太多的时间没有解决问题,错误是否结束了)。 这是估计和问题的优先事项。

有时我只需坐下来学习代码,直到find错误。 试着certificate这个错误是不可能的,在这个过程中你可能会弄清楚你可能会误解的地方。 如果你真的成功地说服你自己是不可能的,那么假设你在某个地方搞砸了。

这可能有助于添加一堆错误检查和断言来确认或否认你的信念/假设。 可能会失败,你永远不会期望。

进行随机更改,直到某些工作:-)

这可能是困难的,有时几乎不可能。 但是我的经验是,如果你花费了足够的时间(如果花费的时间是值得的,是另外一回事),你迟早能够复制和修复这个bug。

一般的build议可能有助于这种情况。

  • 如果可能的话,添加更多日志logging,以便在下次出现该错误时拥有更多数据。
  • 询问用户是否可以复制该错误。 如果是的话,你可以让他们在观察他们的肩膀的同时复制它,并希望找出触发该错误的原因。

修改您认为发生问题的代码,以便在某处logging额外的debugging信息。 当下一次发生的时候,你将有什么需要解决这个问题。

有两种types的错误你不能复制。 你发现的那种,还有别人发现的那种。

如果你发现了这个bug,你应该可以复制它。 如果你不能复制它,那么你根本就没有考虑到导致错误的所有因素。 这就是为什么每当你有一个错误,你应该logging它。 保存日志,获取截图等。如果你不这样做,那么你甚至可以certificate这个bug真的存在? 也许这只是一个错误的记忆?

如果有人发现了一个bug,而且你不能复制它,显然要求他们复制它。 如果他们不能复制它,那么你尝试复制它。 如果无法快速复制,请忽略它。

我知道这听起来很糟糕,但我认为这是有道理的。 复制其他人发现的错误所需的时间非常多。 如果这个错误是真实的,它将会自然而然地发生。 有人,甚至你,会再次碰到它。 如果难以复制,那么也很less,如果再发生几次,可能不会造成太大的损害。

如果你花时间实际工作,修复其他错误并编写新的代码,你可能会更有效率,而不是试图复制一个你甚至不能保证实际存在的神秘错误。 等待它再次自然出现,那么你将能够花费你所有的时间来修复它,而不是浪费你的时间来试图揭示它。

假设你已经添加了所有你认为会有所帮助的日志logging,但是没有…需要注意的两件事情:

  1. 从报告的症状后退。 想想自己..“我想要产生所报告的症状,我需要执行哪些代码,我将如何去做,我将如何去做到这一点? D导致C导致B导致A.接受,如果一个错误是不可重复的,那么正常的方法将无济于事。 我不得不盯着代码长达几个小时,这些思维过程发现了一些错误。 通常情况下,这是一件非常愚蠢的事情。

  2. 记住Bob的第一个debugging法则:如果你找不到东西,那是因为你看错了地方:-)

认为。 硬。 locking自己,承认没有interuptions。

我曾经有一个错误,证据是一个腐败数据库的hex转储。 指针的链条系统地搞砸了。 所有用户的程序和我们的数据库软件都在testing中正确无误地运行。 我盯着它一个星期(这是一个重要的客户),在消除了几十个可能的想法之后,我意识到数据分布在两个物理文件中,并且在链条越过文件边界时发生了损坏。 我意识到,如果备份/恢复操作在临界点失败,那么这两个文件可能会“不同步”,恢复到不同的时间点。 然后,如果你运行了一个客户的程序中的已经腐败的数据,它会产生正确的指针链我看到。 然后,我演示了一系列事件,准确地再现了腐败现象。

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

首先看看你可以使用哪些工具。 例如,在Windows平台上的崩溃转到WinQual,所以如果这是你的情况,你现在有崩溃转储信息。 你可以使用静态分析工具来发现潜在的错误,运行时分析工具,分析工具吗?

然后看看input和输出。 在用户报告错误或输出中出现任何exception情况时,是否有类似的input内容? 编译报告列表并查找模式。

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

要求用户给你一个远程访问他的电脑,并看到自己的一切。 要求用户制作一个关于他如何再现这个错误的小video并发送给你。

当然,这两者并不总是可能的,但如果它们可能会澄清一些事情。 发现错误的常见方式仍然是相同的:分离可能导致错误的部分,试图了解发生了什么,缩小可能导致错误的代码空间。

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

大多数情况下,我会说软件不报告你正确的错误信息,例如,在Java和C#的情况下跟踪每个例外..不抓住所有,但保持一个点,你可以捕获和login。 UI错误很难解决,除非您使用远程桌面工具。 而且大部分时间甚至可能是第三方软件的bug。

如果你在一个真正意义上的大小的应用程序上工作,你可能有一个1000个错误的队列,其中大部分是绝对可重现的。

因此,恐怕我可能会把bug作为WORKSFORME(Bugzilla)来解决,然后修复一些更具体的错误。 或者做任何项目经理决定要做的事情。

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