您必须跟踪和修复哪些最恶劣,最困难的错误以及为什么?
在我们发言的过程中,我既是真正的好奇又是膝盖深处.正如他们所说 - 苦难喜欢公司.
Heisenbugs:
heisenbug(以海森堡不确定性原则命名)是一种计算机错误,当尝试研究它时,它会消失或改变其特征.
比赛条件和僵局.我做了很多多线程进程,这是最难处理的事情.
在发布模式下编译但不在调试模式下发生的错误.
任何基于时间条件的错误.这些通常在使用线程间通信,外部系统,从网络读取,从文件读取或与任何外部服务器或设备通信时出现.
代码本身不在您的代码中的错误,而是在您依赖的供应商模块中.特别是当供应商没有反应时,你被迫破解解决方案.很沮丧!
我们正在开发一个数据库,用另一种语言保存单词和定义.事实证明,这种语言最近才被添加到Unicode标准中,并没有进入SQL Server 2005(尽管它是在2005年左右添加的).在整理时,这会产生非常令人沮丧的效果.
单词和定义很好,我可以在Management Studio中看到所有内容.但每当我们试图找到给定单词的定义时,我们的查询都没有返回任何内容.经过8个小时的稳定调试后,我才开始认为我已经失去了编写简单SELECT查询的能力.
也就是说,直到我注意到英文字母与其他英文字母匹配,其中包含任意数量的外国字母.例如,EnglishWord将匹配E!n @ gl ## $ ish $&Word.(用!@#$%^&*代表外国字母).
当排序规则不知道某个字符时,它无法对它们进行排序.如果它无法对它们进行排序,则无法判断两个字符串是否匹配(对我来说是一个惊喜).如此令人沮丧,整整一天都在为一个愚蠢的整理设置而流失.
线程错误,特别是竞争条件.当你无法阻止系统时(因为bug消失了),事情很快变得艰难.
我经常遇到的最困难的是那些没有出现在任何日志跟踪中的.你永远不应该默默地吃一个例外!问题在于,吃异常通常会使代码进入无效状态,而后者在另一个线程中以完全不相关的方式失败.
也就是说,我遇到的最困难的一个是函数调用中的C程序,其中调用签名与被调用签名不完全匹配(一个是long,另一个是int).在编译时或链接时没有错误,并且大多数测试都通过了,但是堆栈由sizeof(int)关闭,因此堆栈上的变量会随机出现错误的值,但大多数时候它会正常工作(该错误参数后面的值通常作为零传入.
这是一个跟踪的BITCH.
由于硬件坏导致负载下的内存损坏.
在一台服务器而不是另一台服务器上发生的错误,您无法访问违规服务器进行调试.
与线程有关的错误.
对我来说最令人沮丧的是编译器错误,代码是正确的,但是我遇到了一个无证的角落案例或编译器错误的东西.我开始假设我犯了一个错误,然后花了好几天试图找到它.
编辑:另一个最令人沮丧的是我把测试用例设置稍微错误的时间,所以我的代码是正确的,但测试不是.花了好几天才发现.
总的来说,我猜我遇到的最糟糕的错误就是不是我的错.
追踪和修复最难的错误是那些结合所有困难案例的错误:
由第三方报告,但您不能在自己的测试条件下复制它;
bug很少发生且不可预测(例如因为它是由竞争条件引起的);
bug是在嵌入式系统上,你不能附加调试器;
当你试图获取日志信息时,bug就会消失;
bug在第三方代码中,例如库...
...您没有源代码,因此您只能使用反汇编;
并且该错误位于多个硬件系统之间的接口处(例如,网络协议错误或总线争用错误).
本周我正在处理所有这些功能的错误.有必要对图书馆进行逆向工程以找出它的内容; 然后产生关于哪两个设备正在比赛的假设; 然后制作旨在激发假设竞争条件的程序的特别装备版本; 然后,一旦确认了其中一个假设,就有可能同步事件的时间,以便图书馆100%的时间赢得比赛.
有一个项目使用beowulf集群构建化学工程模拟器.碰巧网卡不会传输一个特定的字节序列.如果数据包包含该字符串,则数据包将丢失.他们通过更换硬件解决了这个问题 - 首先发现硬件要困难得多.
我必须找到的最难的错误之一是内存损坏错误,只有在程序运行数小时后才会发生.由于破坏数据所花费的时间很长,我们假设硬件并首先尝试了两三台其他计算机.
这个bug需要几个小时才会出现,当它出现的时候通常只会在程序搞砸了之后才会注意到很长一段时间它开始行为不端.在代码库中缩小到发生错误的位置是非常困难的,因为内存损坏导致的崩溃从未发生在损坏内存的函数中,并且错误显示了很长时间.
该错误在一个很少被调用的代码片段中被证明是一个一个错误的错误,用于处理一个有错误的数据行(来自内存的无效字符编码).
最后证明调试器无用,因为在违规函数的调用树中从未发生崩溃.fprintf(stderr,...)的良好排序流调用代码并将输出转储到文件中,这最终使我们能够确定问题所在.
并发错误很难跟踪,因为当你还不知道错误是什么时,重现它们会非常困难.这就是为什么每次在日志中看到无法解释的堆栈跟踪时,都应该搜索该异常的原因,直到找到它为止.即使它只发生在一百万次中,也不会使它变得不重要.
由于您不能依赖测试来重现错误,因此必须使用演绎推理来找出错误.这反过来需要深入了解系统的工作原理(例如Java的内存模型如何工作以及可能的并发错误来源).
这是我几天前找到的Guice 1.0中的并发错误的一个例子.您可以通过尝试找出导致该异常的错误来测试您的错误发现技能.这个错误并不难发现 - 我在15-30分钟内找到了它的原因(答案在这里).
java.lang.NullPointerException at com.google.inject.InjectorImpl.injectMembers(InjectorImpl.java:673) at com.google.inject.InjectorImpl$8.call(InjectorImpl.java:682) at com.google.inject.InjectorImpl$8.call(InjectorImpl.java:681) at com.google.inject.InjectorImpl.callInContext(InjectorImpl.java:747) at com.google.inject.InjectorImpl.injectMembers(InjectorImpl.java:680) at ...
PS有故障的硬件可能会导致比并发更多的错误,因为可能需要很长时间才能自信地断定代码中没有错误.幸运的是,硬件错误比软件错误更少见.