问题已经说得很清楚了。如果你有一个多个用户报告的错误,但在日志中没有发现该错误的记录,而且无论你如何努力都无法重复出这个错误,你该怎么修复它呢?甚至,你是否能够修复它?
我相信许多人都遇到过这种情况。在这种情况下,你会怎么做,最终结果是什么?
编辑: 我更关心的是对于一个找不到的错误,应该采取什么措施,而不是一个无法解决的错误。无法解决的错误至少表明存在问题,并且在大多数情况下有一个起点来搜索问题。而对于一个找不到的错误,你该怎么办?你还能做些什么吗?
问题已经说得很清楚了。如果你有一个多个用户报告的错误,但在日志中没有发现该错误的记录,而且无论你如何努力都无法重复出这个错误,你该怎么修复它呢?甚至,你是否能够修复它?
我相信许多人都遇到过这种情况。在这种情况下,你会怎么做,最终结果是什么?
编辑: 我更关心的是对于一个找不到的错误,应该采取什么措施,而不是一个无法解决的错误。无法解决的错误至少表明存在问题,并且在大多数情况下有一个起点来搜索问题。而对于一个找不到的错误,你该怎么办?你还能做些什么吗?
不同的编程语言会有它们自己的bug。
添加调试语句可能会使问题变得不可能复制,因为调试语句本身会将指针移动足够远以避免SEGFAULT,也称为Heisenbugs。指针问题很难跟踪和复制,但调试器可以帮助(例如GDB和DDD)。
具有多个线程的应用程序可能只在非常特定的时间或事件序列中显示其错误。不正确的并发实现可能会导致死锁,在难以复制的情况下出现。
某些Web浏览器以内存泄漏而闻名。在一个浏览器中运行良好的JavaScript代码可能会导致在另一个浏览器中产生错误行为。使用经过数千个用户严格测试的第三方库可以避免某些晦涩的错误。
根据应用程序(存在错误的应用程序)运行的环境的复杂性,唯一的解决方法可能是简化环境。应用程序是在以下环境中运行的:
应用程序在哪个环境中产生问题?
退出多余的应用程序,关闭后台任务,停止所有计划事件(cron作业),删除插件并卸载浏览器附加组件。
由于网络对许多应用程序至关重要:
尽可能消除未知因素:
消除生产、测试和开发之间的所有差异。使用相同的硬件。完全按照相同的步骤设置计算机。一致性至关重要。
大量使用日志记录以关联事件发生的时间。检查日志是否存在任何明显的错误、时序问题等。
如果软件看起来没问题,请考虑硬件故障:
而对于嵌入式系统:
在本地运行应用程序(即非跨网络)会发生什么?其他服务器是否遇到相同的问题?数据库是否远程?可以使用本地数据库吗?
硬件和软件之间是固件。
时间问题很难跟踪:
收集有关问题的硬数据。一开始可能看起来是随机的问题,实际上可能有一个模式。
有时,在系统升级后出现问题。
不同的操作系统有不同的解决冲突库的方法:
执行操作系统的全新安装,并只包括应用程序所需的支持软件。
确保每个库仅使用一次。有时应用程序容器与应用程序本身的库版本不同。这可能无法在开发环境中复制。
编写一个检测方法,当出现错误时触发通知(例如日志、电子邮件、弹出窗口、寻呼机哔声)。使用自动化测试向应用程序提交数据。使用随机数据。使用涵盖已知和可能的边缘情况的数据。最终,错误应该再次出现。
值得重申的是,像其他人提到的那样:好好睡一觉吧。远离问题,完成其他任务(如文档编写)。远离电脑,进行一些锻炼。
逐行遍历代码,并描述每行代码的作用,可以对自己、同事或橡皮鸭进行说明。这可能会带来如何复现错误的见解。
宇宙射线可以翻转位。由于现代内存错误检查的存在,这不再是过去的大问题。离开地球保护的硬件软件存在无法复制的问题,因为宇宙辐射具有随机性。
有时,尤其是对于小众工具(例如C微控制器编译器遭受符号表溢出),编译器会引入错误。是否可以使用不同的编译器?工具链中的其他任何工具是否会引入问题?
如果它是一个GUI应用程序,观察客户生成错误的过程(或者尝试生成错误)会非常宝贵。他们肯定会做一些你从未想过的事情(不是错误地做,而是以不同的方式)。
否则,将日志集中在那个区域。记录大部分内容(稍后可以提取),并让您的应用程序转储其环境,例如机器类型、VM类型和使用的编码。
您的应用程序是否报告版本号、构建号等?这样可以确定您正在调试的确切版本(或没有!)。
如果可以为您的应用程序添加工具(例如在Java世界中使用JMX),则应该在相关区域中添加探针。存储统计信息,例如请求+参数、时间戳等。使用缓冲区存储最后的“ n”个请求/响应/对象版本/任何内容,当用户报告问题时转储它们。
如果你无法复制它,那么你可能可以修复它,但是无法知道你是否已经解决了问题。
我已经尽力解释了出现错误的原因(即使我不知道那种情况怎么会发生),修复了错误,并确保如果该错误再次出现,我们的通知机制将让未来的开发人员了解我希望知道的信息。实际上,这意味着在跨越可能引发错误的路径时添加日志事件,并记录相关资源的度量。当然,还要确保测试代码能够全面运行。
决定添加哪些通知是可行性和分类问题。决定在首次遇到该错误时要花费多少开发时间也是如此。这需要知道该错误有多重要才能回答。
我有过好的结果(错误没有再次出现,代码也变得更好了),也有过坏的结果(花费太多时间而没有解决问题,无论错误是否最终得到解决)。这就是估算和问题优先级的用途所在。
有时候,找到并修复bug可能会很困难,甚至几乎不可能。但我的经验是,只要你花足够的时间(是否值得花这么多时间则是另一回事),迟早能够找到并解决它。
以下是一些通用建议,希望能对您解决问题有所帮助:
思考。深入思考。关起门来,不要接受打扰。
我曾经遇到一个错误,证据是一个损坏数据库的十六进制转储。指针链被系统地搞乱了。用户的所有程序和我们的数据库软件在测试中都能正常工作。我凝视着它一个星期(那是一个重要的客户),排除了数十种可能的想法后,我意识到数据分布在两个物理文件中,而损坏发生在链穿越文件边界的位置。我意识到如果备份/还原操作在关键点失败,那么两个文件可能会“失去同步”,恢复到不同的时间点。如果你在已经损坏的数据上运行客户的程序之一,它会产生我看到的纠缠的指针链。然后,我演示了一系列可以精确重现损坏的事件。
尝试随机更改直到有所作为 :-)
从报告的症状开始逆向思考。想想自己...“如果我想产生报告的症状,我需要执行哪些代码,我该如何执行并到达那里?” D导致C,C导致B,B导致A。接受一个事实:如果一个错误无法重现,那么通常的方法是无法帮助的。我曾经花费很多时间盯着代码进行这种思考过程来查找一些错误。通常情况下,结果证明是某些非常愚蠢的东西。
记住Bob的第一条调试定律:如果您找不到某个东西,那是因为您正在错误的地方寻找:-)
讨论问题,阅读代码,通常是大量的代码。通常我们会成对进行,因为你通常可以通过分析快速消除可能性。
在您认为问题出现的代码处进行修改,以便在某个地方记录额外的调试信息。下次出现问题时,您将拥有解决问题所需的一切。