什么是你发现和修复的最棘手的错误?
是什么让它很难find? 你是如何追踪的?
不够接近,但也看到
https://stackoverflow.com/questions/175854/what-is-the-funniest-bug-youve-ever-experienced
每当公司首席执行官进入房间时,一台运行在监控摄像机上的jpegparsing器就会崩溃。
100%可重现的错误。
我不是你的孩子!
这就是为什么:
对于那些对JPEG压缩知之甚less的人来说,图像是一种被分解成小块的matrix,然后用魔法编码的。
首席执行官进入房间时,parsing器ch咽,因为他总是有一个方形图案的衬衫,这引发了一些特殊情况的对比和块边界algorithm。
真正的经典。
这并没有发生在我身上,但一位朋友告诉了我。
他不得不debugging一个很less会崩溃的应用程序。 这只会在星期三 – 九月 – 九号之后。 是的,一年有362天,没事的,三天之内就会马上崩溃。
它会将date格式设置为“2008年9月22日星期三”,但是缓冲区是一个字符太短 – 所以如果在每月最长的名称中有一个2位数的DOM,这只会导致问题最长的名字。
这需要了解一些Z-8000汇编程序,我们将在下面解释。
我正在研究embedded式系统(在Z-8000汇编器中)。 公司的另一个部门是在同一个平台上构build一个不同的系统,并且写了一个函数库,我也正在使用这个函数库。 错误是,每次我调用一个函数,程序崩溃。 我检查了所有的input; 他们很好。 它必须是图书馆的一个漏洞 – 除了图书馆已经在全国数千个POS站点使用(并且工作正常)。
现在,Z-8000 CPU有16个16位寄存器R0,R1,R2 … R15,它们也可以作为8个32位寄存器寻址,分别命名为RR0,RR2,RR4..RR14等。从头开始,重构一堆旧的库。 这是非常干净,遵循严格的编程标准。 在每个函数开始时,函数中使用的每个寄存器都被压入堆栈以保存其值。 一切都很整齐 – 他们是完美的。
不过,我研究了库的汇编器列表,并且我注意到了一些有关该函数的奇怪事情—在函数启动时,它有PUSH RR0 / PUSH RR2,最后有POP RR2 / POP R0。 现在,如果你没有遵循这个规则,它会在开始时将4个值压入堆栈,但是最后只能删除其中的3个值。 这是一个灾难的秘诀。 在返回地址需要的栈顶有一个未知的值。 该function不可能工作。
除了,我可以提醒你,它正在工作。 数以千计的机器被称为每天数千次。 它不可能无法工作。
经过一段时间的debugging(在20世纪80年代中期的工具中embedded式系统上的汇编程序并不容易),它总是会在返回时崩溃,因为坏的值将它发送到一个随机地址。 显然,我不得不debugging工作的应用程序,找出为什么它没有失败。
那么请记住,图书馆对于保存寄存器中的值是非常好的,所以一旦你把值存入寄存器,它就会停留在那里。 R1有0000。 当该函数被调用时,它总是有0000。 因此,该错误在堆栈上留下了0000。 所以当函数返回时,它会跳转到地址0000,这正好是一个RET,它会popup堆栈中的下一个值(正确的返回地址),并跳转到该地址。 数据完美地掩盖了这个错误。
当然,在我的应用程序中,我在R1中有不同的价值,所以它只是坠毁….
这是在Linux上,但实际上可能发生在任何操作系统上。 现在大多数人可能都熟悉BSD套接字API。 我们高兴地使用它年复一年,它的工作。
我们正在开发一个大规模并行的应用程序,这个应用程序会打开许多套接字。 为了testing它的运行,我们有一个testing团队,可以打开数百个,有时甚至超过一千个连接进行数据传输。 随着最高的频道数量,我们的应用程序将开始显示奇怪的行为。 有时它只是坠毁。 另一次我们得到的错误根本不可能是真实的(例如accept()在随后的调用中返回相同的文件描述符,这当然会导致混乱。)
我们可以在日志文件中看到出了什么问题,但是很难查明。 Rational Purifytesting说没有错。 但有些事情是错的。 我们为此工作了好几天,越来越沮丧。 这是一个showblocker,因为已经谈判的testing会导致应用程序的破坏。
由于错误只发生在高负载的情况下,我再次检查我们所做的所有sockets。 我们从来没有在Purify上testing过高负载的情况,因为在这种内存密集的情况下这是不可行的。
最后(幸运的是)我记得大量的套接字可能是一个select()的问题,它等待套接字上的状态变化(可能会读取/可能写入/错误)。 果然,我们的应用程序开始大肆破坏到达带有描述符1025的套接字的时刻。问题是select()与位字段参数一起工作。 位字段由macrosFD_SET()和不检查其参数为有效性的朋友填充。
所以每次我们得到1024个以上的描述符(每个操作系统都有自己的限制,Linux的vanilla内核有1024个,实际值定义为FD_SETSIZE),FD_SETmacros会高兴地覆盖它的位域,并把垃圾写入内存中的下一个结构。
我用poll()取代了所有的select()调用,这是一个精心devise的神秘select()调用的替代scheme,高负载情况从来不是一个问题。 我们很幸运,因为所有的套接字处理都在一个框架类中,只需15分钟的工作就能解决问题。 如果select()调用已经遍布整个代码,那将会更糟糕。
得到教训:
-
即使一个API函数25岁,每个人都使用它,它可能会有你不知道的黑暗angular落
-
在APImacros中未经检查的内存写入是EVIL
-
像Purify这样的debugging工具不能适用于所有情况,特别是在使用大量内存的情况下
-
如有可能,始终为您的应用程序提供框架。 使用它不仅增加了可移植性,还有助于解决API错误
-
许多应用程序使用select()而不考虑套接字限制。 所以我敢肯定你可以通过使用许多套接字来在很多stream行的软件中引起错误。 值得庆幸的是,大多数应用程序将永远不会超过1024个套接字。
-
操作系统开发人员不希望有一个安全的API,而是把责任推给开发人员。 Linux select()手册页说
“如果描述符值小于零或者大于或等于FD_SETSIZE,这些macros的行为是不确定的,这通常至less等于系统支持的最大描述符数量。
这是误导。 Linux可以打开超过1024个套接字。 行为是绝对定义的:使用意外的值会破坏应用程序的运行。 开发人员只是简单地覆盖其他结构,而不是将macros复制到非法值。 FD_SET被实现为linux头文件中的内联程序集(!),并将评估为单个汇编程序写入指令。 没有丝毫的界限检查发生在任何地方。
为了testing自己的应用程序,可以通过在main()之后直接以编程方式打开FD_SETSIZE文件或套接字,然后运行应用程序来人为地增加描述符的数量。
Thorsten79
我的硬件问题是…
回到当天,我用了一台带有21英寸CRT显示器的DEC VaxStation,在新build的大楼里搬到了实验室,在房间的对angular安装了两个VaxStations,一旦开机,我的显示器就像迪斯科舞厅一样闪烁(是的,这是八十年代),但另一台显示器没有。
好的,交换显示器。 另一个监视器(现在连接到我的VaxStation)闪烁,我以前的监视器(移动过房间)没有。
我记得基于CRT的显示器对磁场是敏感的。 事实上,他们对60Hz交变磁场非常敏感。 我立即怀疑在我的工作区域发生了一个60Hz的交变磁场。
起初,我怀疑我的工作区域有东西。 不幸的是,即使所有其他设备被closures和拔掉,显示器仍然闪烁。 那时候,我开始怀疑build筑物里有什么东西。
为了testing这个理论,我们将VaxStation和它的85磅显示器转换成便携式系统。 我们把整个系统放在一个滚轮车上,并把它连接到一个100英尺的橙色结构延长线上。 计划是使用这种设备作为便携式场强计,以便find有问题的设备。
滚动监视器,使我们完全困惑。 监视器正好在房间的一半处闪烁,而不是另一边。 房间呈方形,对angular有门,显示器在连接门的对angular线的一侧闪烁,而不是在另一侧。 房间的四面都被走廊包围着。 我们把显示器推到走廊上,闪烁停了下来。 事实上,我们发现闪烁只发生在一个三angular形的一半房间里,而没有其他的地方。
经过一段时间的困惑之后,我记得房间里有一个双向天花板照明系统,每扇门都有灯光开关。 那一刻,我意识到什么是错的。
我把显示器移到了有问题的房间的一半,把天花板灯关掉。 闪烁停止。 当我打开灯,闪烁恢复。 打开或closures两个电灯的开关,在半个房间内打开或closures闪烁。
问题是由于有人在连接天花板灯时偷工减料造成的。 当在照明电路上连接一个双向开关时,在SPDT开关触点之间连接一对电线,在一个开关上连接一根公用电线,通过这些指示灯连接到另一个开关上的公用电线。
通常,这些电线捆绑在一起。 他们从一个开关箱中作为一个整体离开,跑到高架天花板上,然后到另一个箱子。 关键的想法是,所有的载stream导线都捆绑在一起。
当build筑物接线时,开关和灯之间的单线穿过天花板,但是在开关之间传播的电线穿过墙壁。
如果所有的导线彼此靠近并且相互平行,那么在一条导线中由电stream产生的磁场被在相邻的导线中由相等且相反的电stream产生的磁场抵消。 不幸的是,灯光实际连线的方式意味着房间的一半基本上在一个大型的单匝变压器初级内部。 当灯亮起时,电streamstream入一个环路,而差的监视器基本上坐在一个大的电磁铁内。
道德故事:交stream电源线中的热线和中性线是相邻的,这是有原因的。
现在,我所要做的就是向pipe理层解释为什么他们必须重新连接新build筑的一部分。
一个错误,你遇到一些代码,经过研究,你得出结论:“这是不可能有效的! 突然它停止工作,虽然它以前一直工作。
我在工作中帮助build立的产品之一是在客户站点上运行数月,收集并愉快地logging它收到的每个事件到SQL Server数据库。 它运行了大约6个月,收集了大约3500万条logging。
然后有一天,我们的客户问我们为什么数据库近两周没有更新。 经过进一步调查,我们发现执行插入操作的数据库连接无法从ODBC调用中返回。 幸运的是,录音的线程与其余的线程分开,除了录音线程之外的所有内容都可以在近两周内正常运行!
我们尝试了几个星期,以在任何其他机器上重现此问题。 我们永远不能重现这个问题。 不幸的是,我们的其他几个产品开始以大致相同的方式失败,其中没有一个的数据库线程与其他function分离,从而导致整个应用程序挂起,而这些应用程序每次都必须手动重新启动坠毁。
几个星期的调查变成几个月,我们仍然有相同的症状:在我们使用数据库的任何应用程序中完全的ODBC死锁。 在这个时候,我们的产品充斥着debugging信息和方法,以确定哪里出了问题,甚至到哪里,哪些产品会检测到死锁,收集信息,给我们发电子邮件的结果,然后重新启动自己。
有一天,在服务器上工作时,仍然收集应用程序崩溃时的debugging信息,试图找出发生了什么事情,服务器BSoD。 当服务器恢复在线状态时,我在WinDbg中打开了一个小型转储程序,找出这个有问题的驱动程序是什么。 我得到了文件名并将其追溯到实际的文件。 在检查文件中的版本信息后,我发现它是计算机上安装的McAfee防病毒套件的一部分。
我们禁用了反病毒软件,并没有遇到任何问题!
我只想指出一个可能发生在这个谷歌地区时间的相当常见和讨厌的错误:
代码粘贴和臭名昭着的减号
这是当你复制粘贴一些减号代码,而不是一个普通的ASCII字符hyphen-minus(' – ') 。
加号(U + 2212),连字号(U + 002D)
现在,尽pipe在某些编辑器(或者在DOS shell窗口)中,减号被假定为比连字符减号长,但是根据所使用的字符集,它实际上被呈现为常规的“ – ”连字符减号。
而且,你可以花数小时的时间来确定这个代码为什么不能编译,逐一删除每一行,直到find真正的原因!
可能不是那里最棘手的bug,但是令人沮丧;)
(谢谢ShreevatsaR在我原来的文章中发现了反转 – 请参阅评论)
首先是我们发布的产品出现了一个bug,但是当我试图debugging这个问题的时候,并没有发生。 我原以为这是一个“释放与debugging”的东西 – 但即使我编译的代码在释放模式,我不能重现这个问题。 我去看看是否有其他开发者可以重现这个问题。 不。 经过对程序输出的大量调查(产生一个混合的汇编代码/ C代码列表),并且通过发布的产品(yuck!)的汇编代码,我发现了这个违规行。 但是这条线对我来说看起来很好! 然后,我不得不查找汇编指令做了什么 – 并确信错误的汇编指令是在发布的可执行文件。 然后我检查了我的编译环境生成的可执行文件 – 它有正确的汇编指令。 事实certificate,生成机器不知何故已经损坏,并产生坏的汇编代码只有一个指令为这个应用程序。 其他一切(包括我们产品的以前版本)都会向其他开发人员机器生成相同的代码。 在向软件经理展示我的研究成果之后,我们很快重新构build了构build机器。
在一个networking应用程序的深处的某处是(简化)行:
if (socket = accept() == 0) return false; //code using the socket()
通话成功后发生了什么? socket
被设置为1. send()
在给定1时做什么? (如在:
send(socket, "mystring", 7);
它打印到stdout
…我发现4小时后,为什么我的所有printf()
取出后,我的应用程序打印到terminal窗口,而不是通过networking发送数据。
在80年代的数据通用小型机上使用FORTRAN时,我们有一个情况,编译器将一个常量1(一)视为0(零)。 这是因为一些旧的代码传递了一个常量值为1的函数,该函数声明variables为FORTRAN参数,这意味着它(应该是)不可变的。 由于代码中存在缺陷,我们对参数variables进行了赋值,编译器将其所使用的内存位置中的数据从1改为0。
许多不相关的函数后来我们有代码,它们与文字值1进行比较,testing会失败。 我记得在debugging器中盯着那个代码最长的时间。 我会打印出variables的值,它会是1,但是如果(foo .EQ。1)将会失败。 我花了很长时间才想让debugging器打印出它认为1的价值。 然后花了很多头发,通过代码追溯到常数1变成0的时候。
不是很强硬,但是当它被发现的时候我笑了很多。
当我为一家网上商店维护24/7订单处理系统时,一位顾客抱怨说他的订单被“截断”了。 他声称,尽pipe他所下的命令实际上包含了N个职位,但系统在没有任何警告的情况下接受了更less的职位。
在我们追踪系统中的订单stream程之后,揭示了以下事实。 有一个存储过程负责在数据库中存储订单项目。 它接受一个订单项列表作为string,其中(product-id, quantity, price)
三维编码列表是这样的:
“<12345,3,19.99> <56452,1,8.99> <26586,2,12.99>
现在,存储过程的作者太聪明了,不能诉诸于普通的parsing和循环。 所以他通过用"insert into ... values ("
和">"
with ");"
replace"<"
来直接将string转换成SQL多重插入语句");"
。 如果只是他没有将结果string存储在varchar(8000)variables中,哪一个都很好,很花哨!
发生了什么事是他的"insert ...; insert ...;"
被截断了第8000个字符,为了这个特定的顺序,剪切是“幸运的”,足以在insert
s之间发生,所以被截断的SQL在语法上仍然是正确的 。
后来我发现sp的作者是我的老板。
我在游戏机游戏中遇到了一个错误,只有在你打架之后才赢得一场长时间的boss战,然后在5点左右的时候才会出现这个错误。当它触发时,硬件100%被楔住,无法与外界通话在所有。
这是我遇到的最害羞的错误; 修改,自动化,检测或debuggingboss-battle会隐藏bug(当然,我必须做10-20次运行才能确定bug已经隐藏)。
最后,我通过反复阅读代码2-3天,发现问题(caching/ DMA /中断比赛的事情)。
当我认为C ++和数字手表相当整洁的时候,这又回来了…
我因能够解决难以解决的内存泄漏而获得了声誉。 另外一支队伍泄露了他们无法追查到的漏洞。 他们让我去调查。
在这种情况下,他们是COM对象。 在系统的核心是一个组件,给出了许多曲折的小COM对象,都看起来差不多。 每个分发给许多不同的客户端,每个客户端负责执行AddRef()
和Release()
相同的次数。
没有办法自动计算谁调用了每个AddRef
,是否有Release
d。
我花了几天的时间在debugging器上写下hex地址在小纸片上。 我的办公室被他们覆盖。 最后我find了罪魁祸首。 请求我帮助的团队非常感激。
第二天,我改用GC'd语言。*
(*不是真的,但会是故事的一个好结局。)
Sun Microsystems公司的Bryan Cantrill就他用一种他称之为dtrace的工具追踪到的一个bug进行了精彩的Google技术讲座。
技术讲座是有趣的,令人讨厌的,内容丰富,非常令人印象深刻( 长达 78分钟)。
我不会在这里提供任何破坏者,但是他在53:00左右开始揭露罪魁祸首。
在testing最近添加到交易应用程序中的一些新function时,我碰巧注意到显示某种交易types结果的代码将无法正常工作。 在看了源代码pipe理系统之后,很明显这个bug已经存在了至less一年了,我很惊讶没有一个交易者发现过它。
在困惑了一会儿并与同事核对之后,我修正了这个错误并继续testing了我的新function。 大约3分钟后,我的电话响了。 另一方面,一位愤怒的交易者抱怨说,他的某个交易没有正确显示。
经过进一步的调查,我意识到交易者已经受到了3分钟前我在代码中注意到的完全相同的错误。 这个bug一直存在一年左右,只是等着开发人员前来发现,这样才能真正实现。
这是一个被称为Schroedinbug的bugtypes的一个很好的例子。 当我们大多数人都听说过这些奇特的实体的时候,当你在野外遇到一个奇怪的实体时,这是一种令人毛骨悚然的感觉。
想到的两个最棘手的错误都是在同一types的软件中,只有一个是基于Web的版本,而一个是在Windows版本中。
该产品是平面图浏览器/编辑器。 基于Web的版本具有一个以SVGforms加载数据的Flash前端。 现在,这工作正常,只有有时浏览器会挂起。 只有在一些图纸上,并且只有当你在图纸上晃动鼠标一点。 我将问题缩小到一个绘图层,其中包含1.5 MB的SVG数据。 如果我只拿了一小段数据,任何一个小节都没有发生。 最终,我发现问题可能是文件中有几个不同的部分,这些部分组合在一起导致了错误。 果然,在随机删除图层的某些部分并testing了这个错误之后,我发现了这些不合理的绘图语句的组合。 我在SVG生成器中编写了一个解决方法,并且修正了这个bug,而没有改变一行actionscript。
在Delphi编写的窗口上的同一个产品中,我们遇到了一个类似的问题。 这里的产品需要autocad DXF文件,将它们导入到一个内部绘图格式,并在一个自定义绘图引擎中呈现它们。 这个导入例程不是特别有效(它使用了大量的子string复制),但它完成了工作。 只有在这种情况下,它不是。 一个5兆字节的文件通常在20秒内导入,但在一个文件上花了20分钟,因为内存占用空间膨胀到了十亿字节以上。 起初,这似乎是一个典型的内存泄漏,但内存泄漏工具报告它是干净的,手动代码检查也没有出现。 问题原来是Delphi 5的内存分配器中的一个错误。 在某些情况下,这个特定的文件正在重新创build,这将是容易严重的内存碎片。 系统会一直试图分配大的string,并且除了在最高分配的内存块之外,无法find它们。 集成一个新的内存分配库修复了这个bug,而不需要改变一行导入代码。
回想起来,最棘手的错误似乎是那些修复系统的不同部分,而不是发生问题的部分。
当客户的宠物兔子兔子通过以太网电缆中途啃食。 是。 这不怎么样。
在设备debugging器上有一个非常糟糕的平台有一个错误。 如果我们将printf添加到代码中,我们会在设备上发生崩溃 。 然后它会在不同于printf位置的地方崩溃。 如果我们移动printf, 崩溃就会移动或消失 。 事实上,如果我们通过对一些简单的语句进行重新sorting来改变这些代码,崩溃就会发生在一些与我们改变的代码无关的地方。
原来在我们平台的重定位器中存在一个错误。 重定位器不是零初始化ZI部分,而是使用重定位表初始化值。 所以任何时候二进制中的重定位表改变了,错误就会移动。 所以简单地添加一个printf就会改变重定位表。
当我在电脑商店工作的时候,这发生在我身上。
有一天,有一位顾客来到商店,告诉我们,他的全新电脑在晚上和夜间都能正常工作,但是在中午或者深夜,根本不工作。 麻烦的是鼠标指针在那个时候不移动。
我们做的第一件事是把鼠标换成新的,但麻烦不是固定的。 当然,两个鼠标都没有过错,
经过几次尝试,我们发现问题在于鼠标的特定品牌和型号。 客户工作站靠近一个很大的窗口,中午时分,鼠标直射阳光。 它的塑料非常薄,在这种情况下,它变得半透明,防止阳光直射光学机械轮:|
我的团队inheritance了一个基于CGI的multithreadingC ++ web应用程序。 主要的平台是Windows; 一个遥远的二级平台是带有Posix线程的Solaris。 由于某种原因,Solaris上的稳定性是一场灾难。 我们有不同的人看了这个问题一年多了,closures了(大部分是closures的),而我们的销售人员成功推出了Windows版本。
症状是可怜的稳定性:广泛的系统崩溃,小韵或原因。 该应用程序使用Corba和本土协议。 有一位开发人员竟然把整个Corba子系统当成绝望的手段去除了:没有运气。
最后,一位资深的,最初的开发者大声想着一个想法。 我们调查了一下,最终发现问题:在Solaris上,有一个编译时(或运行时?)参数来调整可执行文件的堆栈大小。 它被设置不正确:太小了。 所以,应用程序正在运行堆栈和打印完全红鲱鱼栈跟踪。
这是一个真正的噩梦。
得到教训:
- 头脑风暴,头脑风暴,头脑风暴
- 如果在一个不同的,被忽视的平台上发生了一些事情,这可能是环境平台的一个属性
- 注意离开团队的开发人员转移的问题。 如有可能,请以个人身份联系以前的人员以获取信息和背景。 乞求,恳求,达成协议。 必须不惜一切代价尽量减less经验的损失。
Adam Liss上面提到的关于我们所做的项目的信息让我想起了一个我不得不面对的有趣的错误。 事实上,这不是一个错误,但我们会在一分钟之内。
应用程序的执行摘要,如果你还没有看到亚当的消息:销售人员自动化软件…在笔记本电脑…他们拨了电话的一天结束…与母亲数据库同步。
一位用户抱怨说,每次他尝试拨入时,应用程序都会崩溃。 客户支持人员经历了所有通常的电话诊断技巧,而他们什么都没有发现。 所以,他们不得不放弃到最终:让用户将联邦快递的笔记本电脑送到我们的办公室。 (这是一个非常大的交易,因为每台笔记本电脑的本地数据库都是为用户定制的,所以一台新的笔记本电脑必须准备好,交给用户供他使用,而我们的原始工作,然后我们不得不退换让他最终在第一台笔记本电脑上同步数据)。
所以,当笔记本电脑到达的时候,我发现了这个问题。 现在,同步涉及将电话线连接到内置调制解调器,进入我们应用程序的“通信”页面,并从下拉列表中select一个电话号码(使用预选的最后一个号码)。 DDL中的数字是定制的一部分,基本上是办公室号码,以“+1”为前缀的办公室号码,以“9 ,,,”为前缀的办公室号码从酒店打电话等。
所以,我点击“COMM”图标,然后按回车。 它拨入,它连接到调制解调器 – 然后立即坠毁。 我累了几次。 100%的可重复性。
所以,在笔记本电脑和电话线之间挂上了一个数据范围,并查看了跨越线路的数据。 它看起来很奇怪…最奇怪的部分是我可以读它!
用户显然想用自己的笔记本电脑拨号进入本地的BBS系统,因此,更改应用程序的configuration使用BBS的电话号码,而不是公司的。 我们的应用程序期待我们的专有二进制协议 – 不长的ASCII文本stream。 缓冲区溢出了 – KaBoom!
事实上,在他改变电话号码之后立即开始拨号的问题,可能会给普通用户一个线索,认为这是问题的原因 ,但这个人从来没有提到过。
我固定了电话号码,然后把它寄回给支援小组,并附上一张便条,让他选出“本周的Bonehead用户”。 (*)
(*)OkOkOk …这个家伙的孩子真的发生了什么事,看到他父亲每天晚上拨打电话,都觉得这是你拨打BBS的方式,并在他独自在家的时候改变了电话号码与笔记本电脑。 当它坠毁的时候,他不想承认他碰了笔记本电脑,更不用说破了; 所以他就把它拿走了,没有告诉任何人。
那是在我的gradle论文。 我正在编写一个程序,用FORTRAN来模拟高强度激光对氦primefaces的影响。
一个testing运行如下所示:
- 使用程序1计算初始量子态,大约2小时。
- 对第一步的数据运行主要的模拟,最简单的情况下大概需要20到50个小时。
- 然后使用第三个程序分析输出,以获得有意义的值,如能量,tork,动量
这些应该总是不变的,但不是。 他们做了各种奇怪的事情。
经过两周的debugging后,我开始logging日志,并在模拟的每个步骤中logging每个variables,包括常量。
这样我发现我写了一个数组的末尾,这改变了一个常数 !
一位朋友说,他曾经以这样的错误改变了文字2。
我的第一个multithreading程序中出现死锁!
find它非常困难,因为它发生在线程池中。 有时池中的线程会死锁,但其他线程仍然可以工作。 由于池的大小比需要的大得多,所以花了一两个星期才注意到第一个症状:应用程序完全挂起。
我花了几个小时到几天的时间来debugging一些事情,最终只需要几个字符就可以修复。
一些例子:
-
ffmpeg有这样一个恶意的习惯,即当溪stream中的农作物价值实际上是完全有效的时候,会产生一个关于“brainfart cropping”的警告(指的是一个农作物的种植值大于等于16的情况)。 我通过添加三个字符来解决它:“h->”。
-
x264有一个错误,在极less数情况下(百万帧中有一个)有某些选项会产生完全错误颜色的随机块。 我通过在代码中的两个地方添加字母“O”来修复该错误。 原来,我在之前的提交中拼错了#define的名字。
我的第一个“真正的”工作是写一个客户 – 服务器销售自动化软件的公司。 我们的客户在他们的(15磅)笔记本电脑上运行客户端应用程序,并在一天结束时拨打我们的unix服务器与Mother数据库同步。 经过一连串的投诉后,我们发现一开始,在authentication过程中,一个天文数量的呼叫正在下降。
经过几个星期的debugging,我们发现,如果传入呼叫在进程ID包含一个偶数的服务器上由一个getty进程应答,并且紧接着是一个9,则authentication总是失败。事实certificate,这个authentication是一个自制的scheme,依赖于PID的8个字符的string表示; 一个错误造成了一个有问题的PID,使getty重新崩溃,重新生成一个新的PID。 第二个或第三个电话通常会find一个可接受的PID,而自动重拨使客户无需介入,所以直到电话账单到达月底才被认为是一个重大的问题。
“修正”(ahem)是将PID转换为一个以八进制表示其值而不是十进制的string,这使得不可能包含一个9并且不必处理潜在的问题。
基本上,任何涉及线程。
我曾经在一家公司担任过一个职位,在这个公司中,我有一个可疑的人,他是唯一一个能够轻松debugging线程来debugging讨厌问题的人。 惊恐的事件。 在允许编写线程代码之前,您必须获得某种authentication。
我听说高中时代有一个经典的错误, 如果你坐在前面的椅子上,你只能login的terminal。 (如果你站着,它会拒绝你的密码。)
它对于大多数人来说非常可靠地复制 你可以坐在椅子上,login,注销……但是如果你站起来,每次都会被拒绝。
最后竟然有一些混蛋交换了键盘上的几个相邻按键,E / R和C / V IIRC,当你坐下时,你触摸键入并进入,但是当你站立时,啄,所以你看着不合标签,失败了。
虽然我不记得一个具体的例子,但是最困难的是那些在系统运行了几个小时或者几天之后才会出现的错误,当它发生崩溃的时候,很less或者根本没有发现导致崩溃的原因。 是什么让他们变得特别糟糕呢,不pipe你认为自己的理由多么出色,并且采取了适当的修正措施,你还得等上几个小时或者几天才能对你有所信心已经真的钉上了它。
我们的networking接口,一个DMAfunction的ATM卡,偶尔会在接收到的数据包中传送损坏的数据。 当数据包进入线路时,AAL5 CRC已经检查出正确,但是DMAD到内存的数据不正确。 TCP校验和通常会被捕获,但是在ATM的繁忙时代,人们热衷于直接在AAL5上运行本地应用程序,完全无视TCP / IP。 我们最终注意到,这种腐败只发生在供应商工作站的某些型号上(谁将不知名),而不是其他人。
通过在驱动程序软件中计算CRC,我们能够以巨大的性能损失为代价来检测损坏的数据包。 在试图debugging的时候,我们注意到如果我们只是将数据包存储了一段时间后再回头看,那么数据损坏就会自然地愈合。 数据包内容会很好,如果驱动程序第二次计算CRC,它会检查出来。
我们发现运送CPU的数据caching中存在一个错误。 这个处理器中的高速caching与DMA不一致,要求软件在适当的时候明确地刷新它。 问题是,有时caching实际上并没有在被告知时刷新其内容。