如果你无法解决错误,你会怎么做?

时间:2008-09-30 20:27:00

标签: debugging

你的代码中有没有错误,你无法解决?我希望我不是那里唯一一个有这种经历的人......

存在一些非常难以追踪的错误类别:

  • 与时间相关的错误(例如在进程间通信期间发生)
  • 与内存相关的错误(大多数人都知道适当的例子,我猜!!!)
  • 与事件相关的错误(难以调试,因为您遇到的每个断点都会使您的IDE成为鼠标释放/焦点事件的目标......)
  • 依赖于操作系统的错误
  • 硬件相关错误(发生在 发布机器,但没有 开发者机器)
  • ...

说实话,我经常无法自行修复这样的错误......经过数小时(有时甚至是几天)的调试后,我觉得非常士气低落。

在这种情况下你做了什么(除了向其他人寻求帮助之外并不总是可行)?

  • 使用铅笔和纸而不是调试器
  • 面对另一件事并回归 这个bug以后
  • ...

请告诉我!

32 个答案:

答案 0 :(得分:39)

有些有用的东西:

1)休息一下,从不同的角度接近虫子。

2)通过跟踪和记录获得更积极的态度。

3)让另一双眼睛看着它。

4)通常的最后手段是找出一种方法,通过改变发生的基本条件来使虫子无关紧要

5)粉碎并破坏事物。 (仅缓解压力!)

答案 1 :(得分:19)

我曾经为一家销售客户端服务器应用程序的公司工作过,该应用程序基本上是一个文件传输和同步工具。客户端和服务器都是我们设计的自定义应用程序。

我们有一个在实验室中非常难以复制的持久性错误。我们的服务器每个盒子只能处理一定数量的传入客户端连接,因此我们的许多客户会将多个服务器“集群”在一起以处理大量用户群。群集的后端数据位于他们共享的文件服务器上。在此群集配置中,有一个错误会在加载时发生,我们会在涉及其中一个后端文件的文件共享调用上获得低级文件系统错误代码。没有人能够在实验室中可靠地重复这一点,即使他们能够做到这一点,他们也无法缩小所发生的事情。

(我忘了确切的错误,它可能是59 ERROR_UNEXP_NET_ERR或者65 ERROR_NETWORK_ACCESS_DENIED。我记得它甚至不是你应该从API获得的一个记录的错误代码我们正在打电话,这通常是对文件部分的锁定或解锁调用。)

由于它涉及服务器和后端文件存储之间的通信,而我是“网络传输”的人,我的任务是查看它。许多人没有运气地看着它。

我所拥有的一个坚实的东西是我知道代码中的错误被检测到的地方,但不知道如何处理它。所以我需要找到根本原因。因此,我设置了一个适当的硬件环境来复制它,并且我放置了一个自定义构建的服务器软件,用于检测相关代码部分。

仪器如下:我为麻烦的错误代码添加了一个测试,并让它调用一段代码在发生错误时将UDP数据包发送到预定的网络地址。 UDP数据包中包含一个唯一的字符串,用于键入。

然后我在网络上设置了一个数据包嗅探工具。 (当时我正在使用Microsoft Network Monitor)。我将它定位在能够“发送”UDP数据包的时间,以及集群服务器和文件服务器之间的所有通信。

大多数优秀的嗅探器都有一种模式,你可以捕捉它直到它看到一个特定的流量,然后停止。我打开该模式并将其设置为查找我的代码将发送的UDP数据包。目标是在错误发生之前最终获得所有文件服务器流量的数据包捕获。进出UDP数据包的系统的最后一个网络数据包可能是发生了什么的一个重要线索。

我将“压力测试”配置设置为周末回家。

周一回来的时候,我看到了我的数据。经过几个小时的运行后,嗅探器已按预期停止并包含捕获。在研究了捕获之后,我发现我们的服务器和文件服务器之间的Server Message Block or SMB(又名CIFS又名SAMBA)连接实际上是在TCP级别的超时在服务器上极端加载。因为所有微软的东西都是分层的,所以它会通过文件共享堆栈进行备份,作为“意外”错误,而不是返回一个更容易理解的错误代码,说“嘿,你在TCP级别丢失了连接”。

我对TCP settings for Windows进行了一些研究,并且看到我们使用的Windows版本(那个时代可能是NT 4)的默认值都不太慷慨。它只允许在TCP连接和繁荣时出现极少数故障,你已经死了。一旦丢失了与文件服务器的SMB连接,所有文件锁都是吐司,无法恢复。

所以我最后编写了一个附录到用户手册,其中解释了如何更改Windows中的TCP设置,使您的群集服务器更容忍高负载情况。就是这样。该错误的修复是代码中的零更改,只是一些关于如何正确配置操作系统以供本产品使用的其他文档。

我们学到了什么?

  • 准备好运行代码的更改版本以调查问题
  • 考虑使用非传统工具来解决问题(嗅探器)
  • 并非所有错误修复都需要更改代码
  • 有时你可以在家里喝啤酒时诊断出虫子

答案 2 :(得分:12)

我做了很多不同的事情:

  • 抛弃我的所有假设并从头开始。请记住,存在一个错误,因为看似正确的东西实际上是错误的。即使是您绝对确定的那些行或函数或类也可能不正确。直到你能说服自己的正确性,你才能认为一切都是对的。

  • 继续使用印刷语句和断言陈述来消除事情并允许我改革新的假设。

  • 如果问题是控制流问题,请在调试器中逐步执行代码。不要跨越功能。介入它们并完成执行的所有细节,以确认它们正常工作。确认参数并返回值。

  • 如果某个行或函数或类是可疑的,但我无法在原位证明它,那么请编写一个小测试用例,它可以完成您认为构造问题的工作。这可能会找到问题或提供一些见解,了解下一步的目标。

  • 停止一天。令人惊讶的是,你的大脑将在一夜之间做什么样的离线处理。通常,答案或关键洞察力会出现在第二天,而我正在做一些像淋浴或开车一样无知的事情。

答案 3 :(得分:9)

创建一种导致错误的自动方式。要修复的最糟糕的错误是需要数小时才能重现的错误。

答案 4 :(得分:6)

引自“ The Cryptonomicon ”:

“直觉,就像一道闪电,只持续一秒钟。通常是当一个人被一个困难的解密折磨而且当他在脑海中回顾已经尝试过无果实的实验时。突然间,光线突破了几分钟之后发现前几天的劳动无法透露。“

答案 5 :(得分:6)

我经常要求别人看看代码。虽然我在解释代码应该做什么,但我有时会在谈话时看到错误。

当一个bug很难处理时,我会坐下来工作,直到我弄明白并解决问题。有趣的是,有时候捕捉一个神秘的bug比一切顺利运行更令人愉快。当一个错误得到解决时,我感到宽慰和感觉,好吧,没有多少其他东西可以击败它(除了明显的错误)。

答案 6 :(得分:3)

在找到解决方案之前,我肯定有过连续工作4-5天的错误。其他bug已经在bug跟踪器中停留数月,因为我在很长一段时间内分散了几个小时。我认为这种错误在任何复杂的软件项目中都是不可避免的。

一些对我有用的东西:

  • 使用日志记录
  • 对程序流进行二进制搜索
  • 使用Trace语句和DbgView搜索在发布模式下显示的错误
  • 找到另一种方法来重现错误而不更改代码
  • (违背逻辑,但......)更改代码,以便更容易重现错误(更容易实现失败条件)
  • 睡觉,然后用一双新鲜的眼睛再试一次:)

我认为最糟糕的错误是并发错误,在插入日志记录时会消失。

答案 7 :(得分:3)

如果其他所有方法都失败了,请不要直接解决。用更重构的方式重写问题区域代码。

答案 8 :(得分:2)

真的?我按此顺序做事。

  1. 上床睡觉
  2. 问一位同事
  3. 重写以使该区域不受影响。
  4. 问SO
  5. 向第三方图书馆供应商提出支持请求。

答案 9 :(得分:2)

这里有很多很棒的答案。过去对我有用的一件事就是问“在发生这个问题时我能做些什么才能让它变得非常明显?”。

例如,如果问题是数据结构中的值已损坏,请尝试构建可以定期运行的一致性检查例程。还要考虑通过记录每个更改的一组函数来实现对共享数据的所有访问。

或者,如果问题是“随机”内存覆盖,请使用替换malloc()/ free()实现来捕获写入“空闲”内存(如电栅栏或dmalloc)。

其他人提到自动触发错误的过程。如果你能做的话,这是非常好的。即使有一个随机练习程序的例程也可能有助于这些情况。

答案 10 :(得分:2)

“你在这种情况下做了什么(除了向别人寻求帮助之外并不总是可行)?”

什么时候无法寻求帮助?

总有其他人可以求助 - 你的同事,你的老板,Stack Overflow的朋友等等。

了解何时寻求帮助不应该令人沮丧!

答案 11 :(得分:2)

这里有很多好的提示。

我绝对不同意的一个是改变代码的概念,希望它会消失。首先,你可能会引入新的bug。几秒钟,您可以轻松地更改内容以隐藏错误,只是让它再次使用下一个补丁重新浮出水面。

内存破坏错误特别可能随着它们的出现而神奇地消失。但是,内存损坏错误并没有得到解决,只有非致命的内存区域才会被破坏。

1)尝试使用其他调试器。例如,我越来越多地使用WinDbg。在调试器中加载程序时,应用程序的内存布局会略有变化。也许一个不同的调试器会导致错误略有不同。

2)如果您在不确切知道问题的情况下使用更改代码,那么如果错误消失,您必须返回并理解为何更改修复了错误。否则,你可能只是隐藏了这个bug。

3)与其他人讨论这个错误,也许他们已经看到了相同问题的不同版本(即重新创建它的其他方式)

4)记录。

答案 12 :(得分:2)

在发现解决方案之前,我有几周或几个月的错误,但最终所有错误都得到修复。除了经典的非调试器错误跟踪技术,比如在获得最小测试用例之前禁用系统的各个部分,我已经使用了这些技术:

  • 寻找更好的调试工具。新观点有很长的路要走。 Xdebug是我开始在PHP中使用的东西,因为我没有取得进展的性能错误。

  • 研究错误所在的技术。这有助于调试Outlook加载项。它有随机错误没有任何意义,谷歌搜索出现了zilch。通过研究Outlook插件最佳实践,COM和MAPI编程,我们可以更清楚地了解可能出现的问题,并想到尝试修复错误的新事物,最终确实修复了这些错误。

  • 试图加剧这个问题。如果有一个问题只是偶尔发生,我会试着找到让它不断发生的方法。这有助于追踪IE下的网络应用程序中的错误,并缩小了Flash插件中的崩溃错误。

  • 当其他所有方法都失败时,我已经重写了从头开始导致问题的子系统。这可能需要几天甚至几周,但是如果你遇到了一个bug而无法解决它,而且客户也不会拒绝答案,你还能做些什么呢?这并不总能解决问题,但如果没有,你通常可以更清楚地了解出现了什么问题。

我注意到这些错误中的一些共性,我被困了几个星期:

  • 向第三方寻求帮助很少有帮助,等待其他人来拯救这一天通常不是一个好主意。

  • 几乎所有的第三方闭源技术都存在故障,特别是在使用模糊部件时。 IE尝试使用客户端证书时有一些令人讨厌的错误。 Flash没有很好地处理随机生成的绘图指令(其中一些是无意义的)。当您尝试从代码动态更改表单布局时,Outlook不喜欢它。这些天我学会了尊重专有技术的“舒适区”。

答案 13 :(得分:2)

我给它更多时间。我曾经有过一个我无法弄清楚的错误(在个人项目中)。我尝试了所有我能想到的调试方法,包括谷歌,没有成功。六个月后,我回来后在一个小时左右的时间内发现了这个漏洞。这不是一件简单的事情(显然没有文件记录在Swing内部),但我只是以我以前没有的方式看过它。

答案 14 :(得分:1)

当无法解决错误时,我也有点士气低落。通常当我遇到有虫子的墙时,我会记下我的发现并停止工作。我会跳到另一个更容易解决的bug,然后回到bug。通过这样做,我将有一个新的头脑和态度来处理这个bug。有时,当你在bug上花费太多时间时,你可能会有过于复杂化的倾向。休息一下,有助于破墙。

RWendi

答案 15 :(得分:1)

首先,它是否可重复?如果是的话,这是一个巨大的优势。我希望永远/永远不会发生错误......它是间歇性的错误的。

这将取决于问题,但在我的商店,我们通常会标记团队这样的问题,确定2个头(或3个或4个)优于1个。

偶尔这个bug甚至不会出现在我的代码中,但它通常是。有些问题是第三方库是罪魁祸首或特定平台上的特定实现是原因 - 那些很臭。

我将使用任何东西,至少跟踪它:调试器,跟踪输出,等等。

通常情况下,如果我可以将它隔离到类或模块,我会编写一个测试工具来复制现实世界并尝试在那里复制它。我通常首先编写我的测试代码,但有时候遗留代码(或其他开发人员的代码)存在,而且还没有测试。

我通常会通过团队和白板大声说出设计和问题,但不清楚。一旦我们将其作为一个群体进行讨论,解决方案通常会浮出水面。

这就是我的工作。

答案 16 :(得分:1)

我通常会尽力解决它。但是,如果对于合理的时间窗口来说这是不可能的,我会留下一段时间让脑筋在我睡觉的时候解决它;)有时候它会起作用......

答案 17 :(得分:1)

我尽可能多地添加调试(写入日志文件,消息框等),然后测试。

我认为这不是你能找到的最糟糕的错误。最糟糕的是你无法确定地或在测试环境中重现的那些。

答案 18 :(得分:1)

我考虑过在这个名为StackOverflow的网站上寻求帮助,这是我最近经常光顾的......

答案 19 :(得分:1)

老实说,我不记得一个我无法解决的错误。它可能会导致很多重构,或者可能需要一段时间,但我从未有过一个我无法摆脱的重构。如果我需要花费一个多小时的时间来跟踪它,那么它几乎总是真的愚蠢而且很小,就像正好看过:应该是;的那样,等

在python中,如果我使用的是不属于我的编辑器,或者可能是别人的代码,我在vim中使用retab!,或粘贴到像pastie这样的东西来检查缩进(如果我不是有vim可用)。

如果它不是破坏者/交易破坏者,那么我继续前进并带着一双新鲜的眼睛回来。

哦,你可以从不永远有太多的日志记录。

答案 20 :(得分:1)

这就是我今天所做的......

我调试HW / SW交互,通常情况下日志记录(检测)会更改或隐藏错误。因此,测试是“快速”进行的。我把这些虫子称为“蟑螂”,因为它们远离我可以照耀它们的任何光线。

所以我必须:

找到导致错误的事务。通过记录列出HW交互(此测试通过,但它说明了流程)。

仪器在打印状态变化的bug之前和之后。

我现在正在解决的错误当然是最糟糕的情况,因为硬件锁定了。硬件包括CPU,所以它就像在一个光线充足的房间里,然后电源出现故障,它的音高变黑。

我对内存有一个特殊的后门视图,但当然这也被锁定了。我试过电源循环,希望内存能保持足够长的时间以便重新启用后门。没有这样的运气。这是可能的。

我非常仔细地写了我所经历的所有步骤来描述这个错误(什么有效,什么失败等)。发送给具有类似硬件的开发人员,以验证它不是我或我的硬件。

我花了几个小时休息让这些信息解决,看看是否有任何灯泡在其他地方点亮。

没有回复,这个bug是我要解决的......

此HW SW交互是一个循环,然后某些设置进入轮询循环,该循环在事务完成时读取。应该发生许多交易。哪个交易失败了?它是第一个(表示我可以调试事务而不是HW中的某些噪声)。它总是第N次交易吗?是什么使第N个与第一个或第(N-1)个不同。 SW是单线程的,并且可以预测。没有抢占,没有启用中断。

此软件以前有用,有什么新内容?所有硬件都是新的。在这种情况下,所有的硅都是新的,因为它是一个ASIC。即使是嵌入式CPU也是新的和定制的,所以ISA是新的。

所以我怀疑一切,我是盲目的。我不得不偷偷摸摸这只蟑螂。

我只启用了报告SW轮询HW完成次数的日志。通过这种方式,第一个事务以高速运行,我知道在紧密的轮询循环中我经常触摸HW的频率。测试通过。我知道它的第N次交易,我记录了所有交易的最高民意调查数量(也许是无意义的数据)。

修改任何东西之后,我必须把它恢复原来验证bug仍然存在的方式。在所有地球都旋转并且太阳风不那么强烈之后;)

看着所有签到,看到承包商改变了一些重要的设置参数,没有任何解释。这些(外包)人员仍在评估中。这无济于事。

发现轮询循环中没有spinwait。循环超时不好,没有它,超时取决于CPU速度。添加了spinwait,仍然没有幸福。

限制在1000之前的某个地方查看失败的交易数量。

设置硬件运行速度较慢,仍然挂起。

讨厌留下任何读这篇文章的人,但这种诽谤必须等到明天。

答案 21 :(得分:1)

之前我遇到过这个问题,我相信每个人都有,我之前已经放弃了,它根本无法找到,但它仍然崩溃,当代码中出现某种错误时,我所做的就是只是坐下来,一点一点地集中精力研究每一段代码,直到找到它为止,这很难,需要耐心但是在这种情况下你只能做到这一点。

希望这有帮助。

答案 22 :(得分:0)

如果错误是如此微妙以至于需要超过三天的时间才能找到,那么我通常会改变设计,因为交付软件的要点是在三年后没有被调用来调试它,因此交互更容易组件之间越好。

答案 23 :(得分:0)

没有无法解决的错误,因为没有错误无法通过完全重写来修复。

不可修复的错误只是您不愿意替换的错误。

答案 24 :(得分:0)

有时需要一点横向思考,但每个bug都是可以修复的。有时候你需要离开它并睡在它上面,有时候让别人快速看一下是好的(他们可能会看到你没有的东西),但主要是关于尝试不同的东西,调用以前的经验。这可能令人沮丧,但是当你修复它时,你得到的嗡嗡声就像其他人一样!

答案 25 :(得分:0)

如果它不重要,不要修理它,你只会花太多时间!

保持错误。尽可能评论/工作。它可能会在以后被偶然(或其他人)修复!

答案 26 :(得分:0)

我在客户网站上每隔几个月出现一个错误。它通常发生在凌晨3点,直到第二天早上客户到达他们的现场才发现。通常当他们发现它时,他们希望一切都能立即发挥作用,因此我们的支持人员通常只需重新启动计算机。这让我疯了多年。它从未发生在我的测试机器或QA实验室中,仅在某些客户站点上发生。随着时间的推移,我已经

  • 重构了我认为导致它的一些代码
  • 在看起来崩溃的地方添加了更多调试打印输出
  • 重定向stdout以便下次我看到它时可以“kill -3”进程
  • 给予支持一些新工具来转储当前的数据库锁等状态。
  • 添加了诊断功能,以便在发生时更加明显

在几个月内没有发生过,而且我的手指交叉了,这次我可能已经修好了,但我不指望它。

答案 27 :(得分:0)

除了调试器,我还使用了日志和旧式纸和铅笔。有时我发现了很多很难的错误,比如在调试模式下运行良好的代码,但在发布模式下中断。我甚至偶尔会重写完美的代码,无论出于何种原因,它都不能可靠地工作,认为最好是可靠而不是优雅。

我有时会尝试重新定义其他人称之为错误的实际功能,但很少有效!

答案 28 :(得分:0)

离开一段时间然后回到问题是我做过和听过的一种常见方法。

如果错误只发生在一个程序的无数次运行中的错误中,那么错误地再现错误也是一个因素,通过破坏其他东西可以将其视为可以忽略不计的收益。

还有一个问题是确定错误的位置,是否在某些配置中使它发生在服务器上,而不是运行IIS 5.0的本地XP Pro机器上。其他一些错误可能涉及到必须更改我的机器的分辨率,这可能会令人烦恼,试图重现其他人报告的错误。

你遗漏了“在另一个O / S下发生”类别的错误,因此在Mac上的IE和Firefox中的网页可能看起来像Mac上的Safari上的垃圾。在尝试使用我的机器作为服务器来修复CSS问题时我是否已经弄脏了?为了查看此问题或者它是如此低的优先级,我在地板的隔间中使用了一行或两行的Mac在地毯下扫过?或者,如果Linux上存在错误并且我附近没有任何Linux机器,我该怎么办?

我很遗憾地离开了一些问题但这些似乎对我来说似乎很难。

答案 29 :(得分:0)

我发现详细的日志记录对于捕获生产机器上“间歇性”的这类错误至关重要,但是由于环境和机器负载的不同,您无法在开发机器上重现这些错误。 / p>

因此当我点击其中一个并且无法在合理的时间内重现/修复它时,我会使用日志消息使代码的潜在区域饱和并让它在生产中运行直到它再次发生 - 并且希望我会有日志中有足够的信息指出我正确的方向。

在生产环境中可能使用或不可用的其他技术是启用“远程调试”,这样您就可以从生产盒上运行的进程中实际调试代码 - 如果可以摆动它,这非常方便。

答案 30 :(得分:0)

使用更有创意的方法来跟踪错误。

在可重现的机器上使用远程调试。

使用分析工具。

向应用程序引入更多日志记录。

答案 31 :(得分:0)

对于与内存相关的错误,我发现Ants Profiler的内存分析选项帮助我找到了很多bug。