如果我们无法解决错误,该怎么办?
我们是否曾在代码中遇到错误,无法解决?我希望我不是唯一一个经历过这种经历的人。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
存在一些错误类别,这些错误很难找到:
- 与时间相关的错误(例如,在进程间通信期间发生)
- 与内存有关的错误(我猜大多数人都知道合适的示例!!!)
- 与事件相关的错误(难以调试,因为遇到的每个断点都使IDE成为鼠标释放/焦点事件的目标...)
- 与操作系统有关的错误
- 硬件相关的错误(在发行机上发生,但在开发机上不发生)
- ...
老实说,我有时会无法自行修复此类错误……调试了几个小时(有时甚至是几天)后,我感到非常沮丧。
在这种情况下,我们会做什么(除了向他人寻求帮助(这并不总是可能的)之外)?
你
- 用铅笔和纸代替调试器
- 面对另一件事,稍后再返回此错误
- ...
请告诉我!
解决方案
我以前遇到过这个问题,我相信每个人都有,我之前曾全力以赴,这根本不可能找到,但是它一直崩溃,当代码中存在某种错误时,我要做的就是坐下来并一点一点地专注于代码的每一部分,直到我找到它,这很困难并且需要耐心,但这是在这种情况下我们可以做的所有事情。
希望这可以帮助。
一些有用的东西:
1)稍事休息,从另一个角度处理bug。
2)通过跟踪和日志记录变得更加积极。
3)让另一双眼睛看着它。
4)通常的不得已的方法是通过更改发生错误的基本条件来找出使错误无关的方法
5)粉碎并弄碎东西。 (仅减轻压力!)
对于与内存相关的错误,我发现Ants Profiler的"内存分析"选项对查找错误有很大帮助。
使用更多创新的方法来跟踪错误。
在可重现的机器上使用远程调试。
使用配置文件工具。
向应用程序引入更多日志记录。
老实说,我无法回忆起无法修复的错误。这可能会导致很多重构,或者可能要花一些时间,但是我从来没有摆脱过一个无法摆脱的重构。如果要花超过一个小时的时间来查找它,那么它总是总是非常愚蠢和小巧,就像经过:
应该过去是;
一样,等等。
在python中,如果我使用的不是我的编辑器,或者可能是别人的代码,则在vim中使用retab!
,或者粘贴到诸如pastie的东西中以检查缩进(如果我没有vim可用) )。
如果不是崩溃者/交易破坏者,那么我继续前进,然后重新看一眼。
哦,我们永远不会有太多的日志记录。
我发现详细的日志记录对于捕获生产机器上"间歇性"的这类错误至关重要,但由于环境和机器负载的差异,我们无法在开发机器上进行复制。
因此,当我碰到其中一个并且无法在合理的时间内重现/修复它时,我会用日志消息使代码的潜在区域饱和,并使其在生产中运行,直到再次发生为止,并希望我将在其中获得足够的信息。记录以指示我正确的方向。
在生产环境中可能会或者可能不会使用的其他技术是启用"远程调试",以便我们实际上可以从生产环境中运行的进程中调试代码,如果可以随意调试,则非常方便。
如果所有其他方法均失败,请不要直接解决。用重构的方式重写问题区域代码。
摘录自《 The Cryptonomicon》:
"直觉就像闪电一样,只持续一秒钟。通常是因为一个艰难的破译折磨着一个人,当一个人脑海中回顾了已经尝试过的无果实验。分钟前几天的劳动无法揭示。"
我添加了尽可能多的调试(写入日志文件,消息框等)并进行测试。
我认为这不是我们发现的最严重的错误。最糟糕的是我们无法确定性地或者在测试环境中复制的内容。
离开一段时间再回到问题上,这是我做过并且已经听到的一种常见方法。
错误的重现性也可能是一个因素,因为如果错误仅发生在数十亿次的程序运行中,则可以认为通过破坏其他东西来修复该错误可忽略不计。
还有一个问题是确定错误的位置,该错误是否在某些配置下才能在服务器上发生,而不是在运行IIS 5.0的本地XP Pro计算机上发生。其他一些错误可能涉及不得不更改我的计算机的分辨率,这可能会令人讨厌地尝试重现其他人报告的错误。
我们忽略了"在另一个操作系统下发生"类别的错误,以便在PC上的IE和Firefox上运行良好的网页可能看起来像在Mac上的Safari上的废话。为了弄清楚这个问题或者它的优先级如此之低,我是否会在尝试将CSS用作服务器和将Mac放在地板的小隔间中排成一排或者两排的Mac时解决CSS问题?席卷地毯?或者,如果某个错误是Linux上的错误,并且我附近没有任何Linux机器,那我该怎么办?
很抱歉留下一些问题,但有时这些问题对我来说似乎是困难的问题。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。..........。。。。。。。。。。。。。。。。。。。。。
创建导致该错误的自动化方法。要修复的最严重的错误是要花费数小时才能重现的错误。
除了调试器之外,我还使用了日志记录和老式的纸笔。有时,我发现了一些非常棘手的错误,例如在调试模式下可以正常运行但在发布模式下会中断的代码。我什至偶尔重写了完美的代码,无论出于何种原因,代码都无法可靠地工作,并认为可靠要比优雅好。
有时,我会尝试重新定义其他人将错误称为真正的功能,但这很少起作用!
当无法解决错误时,我也会士气低落。通常,当我碰到一个有虫子的墙时,我会记下我的发现并停止进行研究。我会跳到另一个更容易解决的错误,然后回到该错误。这样,我将在解决该错误方面有了新的思路和态度。有时,在错误上花费太多时间时,我们可能会倾向于使事情变得过于复杂。休息一下,有助于打破墙。
温迪
首先,它是可复制的吗?如果是的话,那是一个巨大的加分。我希望错误总是/永远不会发生……间歇性的麻烦是麻烦的。
这将取决于问题,但是在我的商店中,我们通常会标记出这样的问题,即2个头(或者3个或者4个)比1个头好。
有时,该错误甚至不会出现在MY代码中,但通常是。出现问题的原因是第三方库是罪魁祸首,或者特定平台上的特定实现是造成这些臭味的原因。
我将使用任何东西至少跟踪它:调试器,跟踪输出等等。
通常,如果我可以将其隔离到类或者模块中,那么我将编写一个测试工具来复制真实世界并尝试在其中复制它。通常,我通常首先编写测试代码,但有时会存在尚未进行测试的遗留代码(或者其他开发人员的代码)。
我通常会与团队和白板大声讨论设计和问题,所有不清楚的地方。一旦我们将其作为一个整体来讨论,解决方案通常就会浮出水面。
我就是做这个的。
我通常请其他人看一下代码。在解释代码应该做什么的同时,有时我会在交谈时看到该错误。
当一个bug很难解决时,我会坐下来工作,直到找出并解决问题为止。有趣的是,有时候捕获一个神秘的错误比使所有事情都顺利进行更令人愉悦。而且,解决错误后的轻松感和感觉,除此以外,还有很多其他事情(明显的除外)可以胜过它。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
我通常会努力解决。但是,如果在合理的时间范围内无法做到这一点,我会在睡眠时将其留给脑细胞解决;)有时它会起作用...
我有一个错误,该错误每隔几个月就会在客户网站上显示一次。它通常发生在凌晨3点,直到第二天凌晨客户到达他们的站点时才发现。通常,当他们发现它时,他们希望一切立即开始工作,因此我们的支持人员通常只需要重新启动计算机即可。多年来一直让我发疯。它永远不会在我的测试机上或者在质量检查实验室中发生,仅在某些客户站点上会发生。随着时间的流逝,我已经
- 重构了一些我认为是导致它的代码
- 在似乎崩溃的地方添加了更多的调试打印输出
- 重定向了stdout,以便下次看到它时可以"
kill -3
"进程 - 给予了支持,一些新工具可以转储数据库锁的当前状态等。
- 添加了诊断程序,使其在发生时更加明显
几个月没有发生这种情况,我已经手指交叉了,这次可能已经修复了,但是我没有指望它。
我做了很多不同的事情:
- 抛弃我所有的假设,从头开始。请记住,存在一个错误是因为看起来正确的东西实际上是错误的。即使我们绝对确定是正确的那些行,函数或者类也可能是错误的。除非我们可以说服自己正确无误,否则我们无法假设任何事情都是正确的。
- 继续发表印刷声明和断言声明,以消除事物并允许我改革新的假设。
- 如果问题是控制流问题,请在调试器中单步执行代码。不要单步执行功能。介入其中,并仔细研究其执行的所有细节,以确认它们工作正常。确认参数并返回值。
- 如果怀疑某个行,函数或者类,但是我不能就地证明它,那么编写一个小的测试用例,它可以完成我们认为问题构造所能完成的工作。这可能会找到问题所在,或者提供一些有关下一步要看的见解。
- 停下一天。令人惊奇的是,大脑将在一夜之间进行哪种离线处理。通常,答案或者关键见解会在第二天出现时,而我却在不加思索地进行淋浴或者驾车之类的事情。
如果不是很关键,请不要修复它,我们将花费太多时间!
保持错误打开。在可能的情况下对其进行评论/处理。以后可能会偶然(或者被其他人)修复它!
我曾经考虑过在我最近经常访问的这个名为StackOverflow的网站上寻求帮助...
有时需要一些横向思考,但是每个错误都是可以修复的。有时我们需要离开并沉迷于其中,有时最好请别人快速浏览一下(他们可能会看到我们所没有的东西),但是大多数情况下,这是关于尝试不同的事情,唤起以往的经验。可能会令人沮丧,但是当我们修复它时会听到嗡嗡声,这是无与伦比的!
这里有很多很棒的答案。过去对我有用的一件事是问"当出现此问题时,我应该怎么做才能使其完全显而易见?"。
例如,如果问题是数据结构中的值损坏,请尝试构建可以定期运行的一致性检查例程。还可以考虑通过记录每个更改的一组功能来实现对共享数据的所有访问。
或者,如果问题是"随机"内存覆盖,请使用替代malloc()/ free()实现来捕获对"空闲"内存的写入(例如电围栏或者dmalloc)。
有人提到了自动触发错误的过程。如果可以的话,这是格力。在这种情况下,即使是随机执行该程序的例程也可能会有所帮助。
在找到解决方案之前,我肯定已经连续工作了4-5天。我花了好几个小时分散在很长一段时间内,其他错误也已经在bug跟踪器中呆了几个月了。我认为这种错误在任何复杂的软件项目中都是不可避免的。
一些适合我的东西:
- 使用日志记录在程序流中进行二进制搜索
- 使用
Trace
语句和DbgView一起搜索在发布模式下显示的错误 - 寻找一种无需更改代码即可重现该错误的替代方法
- (根据逻辑工作,但是...)更改代码,以便更容易再现错误(更容易实现故障条件)
- 睡上它,明天再换一双新的眼睛:)
在我看来,最严重的错误是并发错误,当插入日志记录时,该错误会消失。
严重地?我按这个顺序做事。
- 睡觉
- 询问同事
- 重写,以便该区域不受影响。
- 问问
- 向第三方图书馆供应商索取支持票。
我曾经在一家销售客户机/服务器应用程序的公司工作,该应用程序基本上是文件传输和同步工具。客户端和服务器都是我们设计的自定义应用程序。
我们有一个持续存在的错误,很难在实验室中复制。我们的服务器每盒只能处理一定数量的传入客户端连接,因此我们的许多客户会将多个服务器"群集"在一起以处理大量用户。群集的后端数据位于它们都共享的文件服务器上。在此群集配置中,存在一个在负载下会发生的错误,在该错误中,我们将在涉及后端文件之一的文件共享调用中获得低级文件系统错误代码。没有人能在实验室中可靠地重复此操作,即使他们无法缩小正在发生的事情。
(我忘记了确切的错误,可能是" 59 ERROR_UNEXP_NET_ERR"或者" 65 ERROR_NETWORK_ACCESS_DENIED"。我记得它甚至不是我们应该能够从调用的API中获得的记录的错误代码之一,通常是对文件部分的锁定或者解锁调用)。
因为它涉及服务器和后端文件存储之间的通信,而且我是"网络传输"人员,所以我需要负责研究它。许多其他人都没有运气就看着它。
我遇到的一件很确定的事情是,我知道在代码中哪里检测到错误,但是不知道如何处理。因此,我需要找到根本原因。因此,我设置了一个适当的硬件环境来复制它,然后我对服务器软件进行了自定义构建,从而对有问题的代码部分进行了检测。
检测方法如下:我添加了一个用于测试麻烦的错误代码的测试,并让它调用一段代码以在发生错误时将UDP数据包发送到预定的网络地址。 UDP数据包中包含一个唯一的字符串以进行键入。
然后,我在网络上设置一个数据包嗅探工具。 (当时我正在使用Microsoft Network Monitor)。我将其放置在发送此UDP数据包以及群集服务器与文件服务器之间的所有通信都可以"看到"该数据包的位置。
大多数好的嗅探器都有一种模式,我们可以捕获它,直到看到特定的流量,然后停止。我打开了该模式并将其设置为寻找我的代码将发送的UDP数据包。目标是最终在错误发生之前捕获所有文件服务器流量的数据包。大概是来回发送UDP数据包的系统的最后一个网络数据包,将是正在发生的事情的重要线索。
我设置了"压力测试"配置,并在周末回家。
当我星期一回来时,瞧,我有了我的数据。连续运行多个小时后,嗅探器已按预期停止,并进行了捕获。在研究捕获之后,我发现服务器和文件服务器之间的服务器消息块或者SMB(aka CIFS aka SAMBA)连接实际上在TCP级别上超时,原因是服务器上的负载过大。由于Microsoft的所有内容都是高度分层的,因此它将作为"意外"错误渗透到文件共享堆栈中,而不是返回更清晰的错误代码,说"嘿,我们在TCP级别上丢失了连接"。
我对Windows的TCP设置进行了更多研究,发现我们使用的Windows版本(在那个时代大概是NT 4)的默认设置并不是太宽泛。它只允许TCP连接和繁荣中的极少数故障,我们已经死了。一旦我们失去与文件服务器的SMB连接,所有文件锁都将被吐司,并且无法恢复。
因此,我最后写了一个用户手册附录,解释了如何在Windows中更改TCP设置,以使群集服务器更能承受高负载情况。就是这样。该错误的修复是代码零变更,只是一些有关如何正确配置操作系统以供本产品使用的添加文档。
我们学到了什么?
- 准备运行代码的更改版本以调查问题
- 考虑使用非传统工具解决问题(嗅探器)
- 并非所有错误修复都需要更改代码
- 有时我们可以在家喝啤酒时诊断出一个错误
"在这种情况下,我们会做什么(除了向他人寻求帮助(这并不总是可能的)之外)?"
什么时候不能寻求帮助?
在Stack Overflow上,总有其他人可以向同事,老板,朋友寻求帮助。
了解何时寻求帮助不应令人沮丧!
这里有很多很好的技巧。
我绝对不同意的一个概念就是更改代码,希望它消失了。首先,我们可能会引入新的错误。其次,我们可以轻松地进行更改,以隐藏该错误,而仅在下一个补丁程序中重新出现该错误。
内存损坏错误尤其有可能随着其消失而神奇地消失。但是,内存损坏错误无法修复,仅是内存的非致命区域被破坏了。
1)尝试使用其他调试器。例如,我越来越多地使用WinDbg。在调试器中加载程序时,应用程序的内存布局将略有变化。也许其他调试器会导致错误的显示方式略有不同。
2)如果我们在不知道问题到底是什么的情况下使用更改代码,则如果该错误消失了,则必须返回并了解为什么更改会修复该错误。否则,我们可能只是在隐藏该错误。
3)与其他人讨论该错误,也许他们看到了同一问题的不同版本(即,其他重新创建它的方法)
4)记录。
在找到解决方案之前,我花了数周或者数月的时间才发现错误,但最终所有错误都得到了修复。除了经典的非调试器错误跟踪技术(例如禁用系统的各个部分,直到获得最小的测试用例)之外,我还使用了以下技术:
- 寻找更好的调试工具。一个新的观点有很长的路要走。 Xdebug是我开始在PHP中使用的东西,只是因为我没有取得进展的性能错误。
- 研究错误所在的技术。这有助于调试Outlook加载项。它有没有道理的随机错误,并且谷歌搜索突然出现了问题。通过研究Outlook加载项最佳实践,COM和MAPI编程,我们对可能出现的问题有了更清晰的了解,并想到了尝试修复错误的新方法,这些错误最终得以修复。
- 试图加剧这个问题。如果存在仅偶尔发生的问题,我将尝试找到使之不断发生的方法。这有助于跟踪IE下Web应用程序中的错误,并缩小Flash插件中的崩溃错误。
- 当所有其他方法都失败时,我已重写了导致从头开始出现问题的子系统。这可能需要几天甚至几周的时间,但是如果我们陷在错误中而无法解决它,而客户也不会拒绝,那么我们还能做什么?这并不总是可以解决问题,但是如果不能解决问题,通常可以更清楚地了解出了什么问题。
我注意到这些错误中的一些共同之处使我坚持了好几个星期:
- 向第三方寻求帮助很少会有帮助,通常,等待其他人来度过一天不是一个好主意。
- 故障几乎总是在某些第三方封闭源技术内造成的,尤其是在使用晦涩难懂的零件时。当尝试使用客户端证书时,IE有讨厌的错误。 Flash不能很好地处理随机生成的绘图指令(其中有些是荒谬的)。当我们尝试从代码动态更改表单布局时,Outlook不喜欢它。这些天来,我学会了尊重专有技术的"舒适区"。
我给它更多时间。我曾经有一个错误(在一个个人项目中),但我无法弄清楚。我尝试了所有可以想到的调试方法,包括Google,都没有成功。六个月后,我回来了,并在一个小时左右的时间内发现了该错误。这不是一件简单的事情(在Swing的内部一直在进行一些显然没有记载的事情),但是我只是以一种以前从未有过的方式看过它。
如果错误如此微妙以至于需要超过三天的时间才能发现,那么我通常会更改设计,因为三年后才不再要求提供软件的要点进行调试,因此组件之间的交互越容易,更好。
这就是我今天所做的...
我调试硬件/软件交互,并且通常情况下日志记录(仪器)会更改或者隐藏错误。因此,测试是"全速"进行的。我将这些错误称为"蟑螂",因为它们会避开我可以照在它们身上的任何光线。
所以我必须要:
查找导致该错误的事务。通过日志记录列出硬件交互(此测试通过,但说明了流程)。
仪器在bug之前和之后的打印状态会发生变化。
当硬件锁定时,我现在要解决的错误当然是最坏的情况。硬件包括CPU,因此就像在光线充足的房间中一样,然后电源就会停电,并且漆黑。
我对内存有一个特殊的后门视图,但是当然这也被锁定了。我尝试了重新启动电源,以期使内存保持非易失性足够长的时间以重新启用后门。没有这种运气。不过这是可能的。
我非常仔细地写下了我描述该错误的所有步骤(有效的方法,失败的方法等)。发送给具有类似硬件的开发人员,以验证它不是我还是我的硬件。
我花了几个小时休息一下,以使此信息稳定下来,看看其他地方是否有灯泡点亮。
没有回覆,这个错误是我解决的问题...
这种HW SW交互是一个循环,需要进行一些设置,然后进入轮询循环,该循环在事务完成时读取。应该发生许多事务。哪个交易失败?它是第一个吗(表示我可以调试事务,但硬件中没有噪音)。它总是第N个交易吗?是什么使第N个与第一个或者第(N-1)个不同。 SW是单线程的,并且可以预测。没有抢占,没有允许中断。
该软件以前曾工作过,有什么新功能?所有的硬件都是新的。在这种情况下,所有的芯片都是新的,因为它是ASIC。甚至嵌入式CPU都是新的和定制的,因此ISA是新的。
所以我怀疑一切,我是盲人。我得偷偷摸摸这只蟑螂。
我只启用了日志,该日志报告了SW轮询HW进行完成的次数。这样,第一笔交易就可以快速运行,我了解到我在紧密的轮询循环中经常接触硬件的频率。测试通过。我知道它是第N次交易,我记录了所有交易的最高轮询次数(也许是无意义的数据)。
修改完所有内容后,我必须将其放回原处,以验证错误是否仍然存在。毕竟地球已经旋转了,太阳风还没有那么强;)
看着所有签到处,看到承包商改变了一些重要的设置参数,没有任何解释。这些(外包)人员仍在评估中。这无济于事。
发现轮询循环中没有旋转等待。不利于循环超时,因为没有循环超时取决于CPU速度。增加了旋转等待,仍然没有幸福。
限制事务数量,以查看失败的位置,大约在1000之前。
将硬件设置为运行缓慢,仍然挂起。
讨厌让任何人也读这本书,但这种方法将要等到明天。
没有无法修复的错误,因为没有无法完全重写的错误。
无法修复的错误只是我们不愿意替换的错误。