最难调试修复的 bug 是怎样的？
手机版app好像没法设置引用，不过 whatever 这是Dave Baggett发表在Quora上一篇The hardest bug you've ever debugged，读起来让人十分惊叹。（如果是我的话，在代码中找不到可能就直接放弃了。。。） 回想起这个bug，仍然让我有些痛苦。作为一个程序员，在发现bug时，你学会了首先在自己代码中找问题，或许在测试一万次之后，你会把问题归咎于编译器。只有在这所有的都不起作用之后，你才会把问题归咎于硬件。 这是我遭遇一个硬件bug的故事。 抛开别的不说，我曾为《Crash Bandicoot》写存储卡（读写）代码。对于一个自大的游戏程序员，这就像是在公园里散步一样轻松愉快，我认为只要几天就写完了。我中止调试六个礼拜。在此期间我做一些其他的事情，但我一直回来处理这个bug——几天内每天几个小时。这个bug实在烦人。 这个bug的症状是，当你需要保存你的进度时，代码会访问存储卡，而大部分情况下没有什么问题…但是偶尔读写会超时…没有任何明显的原因。一个短小的写入经常毁掉存储卡。玩家要保存进度，我们不仅不保存，还擦除他们存储卡上的全部东西。天哪。 过了一段时间，我们在Sony的制作人Connie Booth慌了。我们显然不能带着这个bug发布游戏，而六个星期之后我对于问题出在哪一点线索都没有。通过Connie我们向其他 PS1 开发者求助：有没有人出现过像我们这样的情况？没有。绝对没有任何人在存储卡系统上出现任何问题。 在你绞尽脑汁之后，你能做的唯一一个调试方法就是分而治之：一点点去除程序中的代码，直到留下的代码很少但你仍然出问题。像木雕一样去除没有问题的代码，留下的就是你的bug所在。 在这样的背景下挑战在于，视频游戏是很难去除某一部分的。在你删除模拟重力或者显示字符的代码后，如何运行游戏？ 你必须做的是用一个假装做真正的事情，但实际上只是做很简单的不会出现bug事情的东西来替换掉整个模块。你必须写新的支撑代码来让这些玩意正常工作。这是一个缓慢而痛苦的过程。 长话短说：我做完了。我移除了大片大片的代码，相当多，只留下了初始化代码——就是准备游戏运行系统，初始化底层硬件等等。当然，我不能显示加载/保存菜单，因为我截除了所有的图像代码。但是我能够假装用户使用（不可见的）加载/保存屏幕并且请求保存，然后写入卡中。 我最终以一个带有这个bug的很少量的代码结束——但问题仍然随机出现！在大多数情况下没啥问题，但是偶尔会失效。基本上所有的Crash的实际代码都被移除了，但还是这样。这实在是莫名其妙：留下来的代码基本上都没做什么事。 在那时——估计是凌晨3点——一个想法蹦了出来。读写（I/O）涉及精确定时。无论是硬盘、存储卡、蓝牙发送器——随便啥——做读写的底层代码都是根据时钟来的。 时钟让不直接连接到CPU的硬件设备和cpu运行的代码同步。时钟决定了波特率——数据从一头传到另一头的速率。如果计时有什么问题，硬件或者软件或者两者都会乱七八糟的。这真的，真的很糟糕，并且通常导致数据损坏。 如果我们的初始化代码以某种方式弄乱了计时会怎么样？我又看了一遍测试程序中和计时有关的代码，并注意到我们将PS1上的可编程计时器设置到了1kHz（1000跳每秒）。这是比较快了，当PS1启动的时候，默认状态大概是100Hz。因此，大多数游戏将他们的计时器设置为100Hz。 这个游戏的带头（和除我外的唯一）开发者Andy，将计时器设置为1kHz，使得Crash的动作计算更加准确。Andy喜欢矫枉过正，如果我们要模拟重力，我们应该尽可能的提高精度！ 然而如果提高计时器频率莫名其妙的干扰了整个程序的计时，故而将这个计时器设置到存储卡的波特率上会怎样呢？ 我将计时器代码注释掉。然后我就无法复原这个bug了。但是这并不表示bug被修复了，这个问题是随机发生的。万一我只是运气好呢？ 几天过去了，我还是在玩我的测试程序。Bug没有再出现。我回到全部的Crash代码中，修改了加载/保存代码，在访问存储卡之前将可编程计时器重置为默认设置（100Hz），之后设置回1kHz。从此之后没有发现问题再次出现。 但是…为什么？ 我重新回到测试程序上，试着检测当计时器设置为1kHz时出现的那些错误的模式。终于，我注意到这些错误出现在使用PS1手柄的人身上。因为我自己很少这样做，所以我没有注意到（为啥我要在测试加载/保存代码的时候用手柄）。但是有一天我们的美工等我去完成测试（我确定那时候我在爆粗口），而他紧张的摆弄着手柄。卡损坏了。“等下，怎么回事？喂，再来一次！” 一旦我发现了这两件事是联系着的，就很容易重现bug：开始写入存储卡，动一下手柄，存储卡损坏。在我看来完全是硬件bug。 我去找Connie告诉他我的发现。她转述给设计过PS1的硬件工程师。她被告知：“不可能，这不可能是硬件问题。”我跟她说问一下我能不能直接和他说。 那个工程师给我打电话了，他用着他的烂英语，我用着我更烂的日语，我们争论一会。我最后说：“我给你一个30行的测试程序，让你在动手柄的时候能够出现这问题。”他答应了。他向我保证，这是浪费时间，而他正在一个新项目上很忙，但因为我们是Sony很重要的开发者，他会试的。 第二天晚上（我们在洛杉矶，而他在东京，所以对于我来说是晚上而他是到了第二天），他给我打电话，不好意思的向我道歉。这是个硬件问题。 我还是没有完全搞清楚问题到底在哪，但是我的印象中，从Sony总部的反馈听到的是，如果将可编程计时器设置到足够高的时钟频率，会影响到主板上时钟晶振附近的一些东西。这些东西之一就是存储卡的波特率控制器，同时也设置手柄的波特率。我不是搞硬件的，所以对于细节我相当模糊。 但是主旨是主板上两个独立部分的串扰，以及手柄接口和存储卡接口数据发送的结合在1kHz的时钟频率下会导致丢位，从而数据丢失，以致卡损坏。 这是我全部编程生涯中，唯一一次因为量子力学debug的问题。 Retrieve from http://www.quora.com/Programming-Interviews/Whats-the-hardest-bug-youve-debugged Baggett, D.