《调试九法》是一本关于调试技术的指南,作者通过九种不同的方法来解决各种调试问题。文章深入浅出地介绍了每种方法的原理和步骤,并强调了调试过程中的重要性和技巧。读后让人受益匪浅,对提高调试技术有很大帮助。
调试九法读后感(一)
书里面讲的案例都很有共鸣,其实调试方法软硬件是通用的. 就拿我一直在用的windows 10 insider Preview 来说吧. 我明知道这是一个埋伏着各种bug的系统, 但仍然是用的乐此不疲. windows 新系统现每个月至少更新一次, 频繁的时候三五天就更新一版(MIUI开发版也差不多, 正常情况下, 工作日周一到周四一周更新四次), 每次都要下载一个2.6G左右的大包, 光下载就得半小时一小时的. 在早期的升级包下载时, 总是很慢, 甚至有好多次无法下载成100%就断了, 这时候, 就出现了错误提示, 但提供的错误码在KB上找到的信息都没用, 只能自己摸索, 基本上就是网络链接错误, 我记得去年有一个版本, 我连续更新了十几次都下载不了完整的更新包, 最后我是挂着VPN下载完成的. 终于 Windows 10出了正式版之后, 这个问题得到了改善, 但最近最近半年来改进的有些矫枉过正了, 每次我发现打开网页非常卡, 甚至打开视频网站都播放不畅的时候, 我就预感到 windows insider新版在更新了, 去Update一查, 果然, 系统正在一条道跑到黑的全速下载, 根本不管你正常上网的死活, 而且还不能暂停, 有时候我实在有事, 要联网, 又不想重启, 在设置里面把 fast 改成 slow , 禁用了windows update 服务,才得以喘息. 还有一个版本,每次更新都是到76%就停住了, 而且还没有任何提示,我睡一觉等着它以为 windows 自己能扛过去,但终究是迈不过这道坎, 试了好几次, 根据我自己的猜测, 每次都修改一处, 可能是杀毒软件, 或者我个人文件太多了, 导致处理缓慢, 最后还是没猜到, 最后忍无可忍, 重新安装了一遍, 后来在论坛里面发现, 是因为 non-unicode 设置的问题, 如果改回en-us就能顺利升级了, 而这个bug, 在微软官方的已知bug列表里面是没有的, 折磨了我三天. 当然, 这个bug在后来的版本里面修复了. 上几个版本还出现了不兼容腾讯软件的问题, 游戏全面不能玩了, 甚至QQ都用不了了, 看起来像是腾讯公司写了一堆病毒, 在新版windows里面失效了. 而且巧的是, 迅雷也用不了, 会在马上下载到100%之前准时崩溃, 所有任务都失败, 这些软件公司都干了些什么啊. 说到游戏, 有个版本对 Xbox 手柄的支持出了问题, 摇杆一摇就崩溃, 方向键就没问题, 后来那一个多星期我是用方向键跑完了好几把赛车. 每次发现bug, 自己能找到bug触发的准确条件, 像是发现了宝一样, 兴奋至极, 当然, 如果我自己能把这些 bug 都修复了, 那就更完美了. 在论坛里面大家有时候都不是在问, 新的测试版有什么新特性, 而是再问, 新版有什么新 Bug 吗? 为什么成天抱着找 Bug 的想法在用软件. 也许是强迫症, 也可能是因为真的很闲吧.
调试九法读后感(二)
一、调试九大规则
1. 理解系统
2. 制造失败
3. 不要想,而要看
4. 分而治之
5. 一次只改一个地方
6. 保持审计跟踪
7. 检查插头
8. 获得全新观点
9. 如果你不修复 bug ,它将依然存在
二、九大规则的具体解释
1. 理解系统
简单来说,就是要熟悉业务。这条规则最重要。
更具体点,你必须知道系统的工作原理以及它是如何设计的,某些情况下,还要知道为什么这样设计。
你需要做到以下:
- 阅读手册。阅读相关文档
- 仔细阅读每个细节
- 掌握基础知识
- 了解工作流程。知道所有的模块和接口都是做什么
- 了解工具。花时间学习与工具的一切,同时要了解工具的局限性
- 查阅细节。不应盲目相信自己的记忆,养成良好的查阅习惯
2. 制造失败
简单来说,就是复现 bug 。
试着让 bug 重现有一下3个原因:
- 可以观察它
- 可以专心查找原因。准确地知道在什么条件发生,有助于集中精力查找原因
- 可以验证是否已修复问题
怎么制造失败(重现):
- 从头开始。试着从一个已知的状态出发
- 引发失败。尽量自动化
- 但不要模拟失败。要模拟导致失败的条件,而不是模拟识别机制本身
- 处理间歇性失败(偶现 bug )。改变能控制的条件,直到失败发生
- 记录每件事。让系统尽可能多的输出信息,并记录到日志
- 如果得到足够多的信息,需要确定哪些与 bug 有关
- 用好调试工具
3. 不要想,而要看
凭空想象,问题可能有几千条原因,而实际的原因只有去看了才能发现。
- 观察失败。仅靠猜测某个地方出了问题就修复它,不仅没修复问题还可能把真正问题隐藏起来,还误以为修复了问题
- 查看细节。一直观察,直到把问题原因锁定在几种可能之内
- 植入插装工具。其实就是用工具调试,并记录日志
- 不要害怕深入研究
- 注意海森堡效应。测试工具可能影响被测系统
- 猜测只是为了确定搜索的重点。但不要过分依赖猜测以免被引入歧途
4. 分而治之
其实就是排除法,逐步逼近,这是调试的核心
- 确定问题范围
- 逐步逼近缩小搜索范围
- 从有问题的一边开始搜索。不要把精力花费在没问题的地方
- 修复探索过程中的已知bug
- 消除干扰因素
5. 一次只改一个地方
不要乱改,为验证问题,一次只改一个地方
- 隔离关键因素,别改出了其它问题
- 一次只改一个测试
- 与正常情况进行比较。这样才能发现问题所在
- 确定自从上一次正常工作以来你改变了什么
6. 保持审计跟踪
- 记下你所做的事、做事的顺序,以及发生的结果,写下来
- 任何细节都可能是重要的
- 把时间关联到一起
- 检查代码关联工具看是否是那一次修改引入了 bug
- 把 bug 修复过程记录下来,形成自己的 bug 修复集
7. 检查插头
一些显而易见的假设往往是错误的。
- 质疑你的假设
- 从问题开始观察
- 对工具进行测试,工具本身也可能有 bug
8. 获得全新观点
要想重新理清一个案子的头绪,最好的方法就是把它讲给别人听。 - 福尔摩斯
如果问题没有头绪,不妨休息下,听听别人的看法
- 寻求帮助。人们通常很愿意帮忙,因为这给了他们一个证明自己很聪明的机会
- 向别人解释问题会让你对问题有全新的认识。小黄鸭调试法
- 咨询专家,获取专业知识
- 借鉴别人的经验。如果有人出现过类似问题,不妨找他们了解下
- 放下面子。bug 发生了,以除掉 bug 为自豪,而不要非得已自己除掉 bug 才为自豪
- 寻求帮助时,报告问题发生现象,而不是自己的猜测,以免误导他人
9。 如果你不修复 bug,它将依然存在
- 验证问题确实已被修复。不要假设它已被修复,要测试它
- 验证确实是你的修复措施解决了问题。而不是自己在修复过程中误操作
- bug 从来不会自动消失。也许当时复现不了,当它终会出现
- 从根本上解决问题
- 对过程进行修复
三、当你的用户发现了 bug
实际上很多时候 bug 是通过用户发现的,他不一定了解这些专业知识
- 不能全信用户的观点
- 多依赖日志
- 要让发生问题的用户告诉你他们做了什么
- 不要假设用户如何使用你的产品。对所有的事情都要进行确认
- 如果问题再次复现了,让用户联系你
调试九法读后感(三)
掌握基础知识:必须掌握系统的工作原理以及它是如何设计的。在某些情况下,还要知道为什么这样设计。如果你没有理解系统中的某个部分,那么这通常就是出问题的地方。 阅读手册:我们应该首先阅读手册,而不是等到所有办法都不管用之后才去读它。而且要逐字阅读整个手册。应用书名通常不仅描述了系统是如何工作的,而且专门给出先前已发生过的问题。常见错误的警告具有难以置信的价值。 了解工作流程:当你尝试寻找bug时,必须知道要查找的路线。开始时,需要猜测在哪里把系统分隔开,以便隔离问题。 了解工具:通常,查明系统行为的关键,是你的调试器设置得怎样,或者是否正确触发了分析器。越是精通工具,越容易查明系统中发生了什么事情。同时必须了解工具的局限性。走查源码可以显示逻辑错误,单无法显示时序和多线程问题。
重现问题的好处:我们可以观察它;可以专心查找原因(准确知道问题在什么条件下回发生,有助于集中精力查找原因);可以判断是否已修复问题。 调试故障的时候,如果需要手工执行很多步骤,那么是这个过程自动化会很有帮助。 引发失败和模拟失败两者之间存在非常大的差别。模拟那些导致失败发生的条件而不是模拟失败机理本身。 间歇性失败的关键问题在于,并没有完全弄清楚失败是如何发生的。你知道做了什么,单并不知道完整的、准确的条件。还要其他没注意到或无法控制的因素,例如初始条件、输入数据、时序、外部过程、电子噪声、温度、震动、网络流量等。如果你能控制所有这些条件,那么久可以一直使错误发生。一旦想到了有哪些条件可能影响你的系统,必须大量尝试与这些条件相符的各种形式。记录没见事情,并找到间歇性bug的特征。是已修复bug,还是仅仅由于运气好,它不再发生了,这样需要运行足够多的测试样本,来做统计。
亲眼看到底层失败是非常重要的。如果猜测失败是如何发生的,那常常会修复一些根本不是bug的问题。 观察就算不是永远也是常常比你想象的要复杂得多。 在停下来思考问题之前,对细节的观察应该到什么程度才合适呢?简单的答案是:一直观察,直到把问题的原因锁定在几种可能性之内。
通过反复地把问题分成好的一半和坏的一半,来缩小搜索范围,然后进一步研究有问题的那一半。
隔离关键因素:如果你在检查日照时间的影响,就不要改变灌溉方案。 用双手抓住黄铜杆:如果你在不知道具体发生了什么问题的情况下就试图去修理核潜艇,肯恩购汇引发一次水下的切尔诺贝利爆炸。 确定自从上一次正常工作以来你改变了什么地方。
有事看起来最不起眼的事情却是导致发生bug的关键,在测试人员看来不重要的细节可能对于bug修复人员很重要。因此,你必须记录下每一件事情。 要记下你的每部操作、顺序和结果。而且要知道,任何细节都可能是重要的。并且要把事件关联到一起。
质疑你的假设。是不是燃气用完了?插头是否已插好?对工具进行测试:燃料油表是否被黏住了?量表是不是没电了?
寻求帮助:获得全新观点(有事想别人解释问题,也会是你有全新的认识;其他人会从一个无偏见的角度看问题);询问专家:咨询专家来了解需要快速掌握哪些知识,专家知道查找问题的大致路线图;借鉴别人的经验:周围的人以前见过类似的情况,有些系统给提供了故障维修指南。 到哪里寻求帮助:同时;wiki;供应商; 报告症状,而不是结论:在寻求帮助时,描述观察到的一切,以及条件,告诉什么是间歇发生,什么事情不是。但不要告诉他认为问题的原因是什么。及时有些不是十分肯定的事,也可以提出来。
当你认为已经修复了一个设计问题时,取消这个修复,确定系统再次失败。然后再应用这个修复,再次验证问题已修复。 如果一个硬件设备失败了,不要以为它是无缘无故坏掉了。如果在某种条件下有零件会损坏,那么更换这个零件也只能是为你换来很短的时间,然后新的零件也会损坏。你必须找到真正的失败之处。 对过程进行修复:不要只是擦掉地上的油,而要纠正设计机器的方式。
调试九法读后感(四)
一般当工程师把一个东西称为艺术甚至玄学的时候,说明这个东西难度很大,没有太多规律可以遵循,调试就属于此列。几乎每个程序员都有被bug搞到死去活来的经验,有时候颠来倒去,问题似乎解决了,但也不知道为什么,就把它当作不可解释的玄学现象,最烦人的一种bug是偶尔出现难以复现的,学名海森堡bug。
不过毕竟软件还是属于科学技术的范畴,调试也应当是门技术活。《调试九法》是一本少见的讲调试技术的书,九个普遍性的原则不仅适用于软硬件开发维护,甚至还能运用到日常生活,为了找到这本书我也是费了点神,幸好图灵社区还有正版的电子书卖。
说实话,作者举的很多例子偏硬件,所以理解起来有点隔膜,但是九个原则确实很实用,在我以前的开发调试经历中可能也不自觉的总结过一些,但是看了本书,还是有拿到武功心法的感觉。看过之后,下面再把九个原则遍历一下,加深理解。
1 理解系统
这是最重要的一条原则,要分析bug,自然要理解系统是如何运作的,这就需要学习掌握一些基本原理,对具体的类库、工具、技术都需要认真的去读相关文档。我最近几年才养成了认真读官方文档的习惯,搜索引擎的发达,使得我们养成了面向google编程的习惯,面对问题,总是想一蹴而就的解决,结果往往是走了很多弯路,因为舍不得花时间看路标。本书还特别强调“逐字逐句”读手册,简直是对浮躁的人当头棒喝。不理解系统就开始调试和不理解原理就写代码是一脉相承的,俗称“面向巧合编程”。
2 制造失败
这条原则讲的是复现bug的重要性与方法,一个bug,肯定是在某个特定的条件下发生的,抽丝剥茧找到这个特定的条件,就成功了一半。以我的硬件维护经验来看,发现bug的人如果能详细的记录整个过程是非常有帮助的。有很多bug出现的条件比较苛刻,所以程序员的口头禅之一就是“在我这里没问题啊”。针对这类bug,首先最好能找到模拟方法,比如加快软件运行交互的速度,进行压力测试等,其次就是在软件内部能有详细合理的bug记录机制,便于从内部找到复现的条件。
3 不要想,而要看
这一条强调的是观测的重要性,面对现实比胡思乱想重要。语言影响思考,所以遇到bug的口头禅应该是“我看看“而不是”我猜可能是因为“。我们应该想尽办法去看清楚出bug的细节,所以成熟的程序中都应该有用于调试的基础设施,个人经验至少日志是必不可少的,初级程序员习惯于完全靠打断点调试,问题是断点本身会改变程序执行的流程(尤其是多线程的情况下)。当然,猜测依然是有用的,可以帮我们缩小观察的范围,或者至少拟定一个观察的优先级,这样经验就能发挥作用,但是不管怎样,经验不能替代观测。
4 分而治之
二分查找法可以把查找的时间复杂度从线性变成对数,不仅是程序中的查找算法,也是调试时的方法论,一个系统有了这个意识后,关键是如何划分系统,又回到了原则1。至少在打断点调试时,要找到出bug的位置,也是可以用二分法的,或者插入日志记录时也可以应用这个原则。另外,书中还提到了bug间相互影响的现象,我觉得甚至又bug跷跷板的现象,解决之道是一个都不要放过。调试bug时发现代码质量实在太差,重构一下有时候也是有必要的,否则浮沙之上筑不了高台啊,甚至经过有效的重构,bug很自然的就发现并消除了。
5 一次只改一个地方
这条原则类似实验科学中的对照原则,一次只考察一个变量,比对正常情况和异常情况,一定不能忽略任何一个测试条件的差异。上一条说bug一个都不放过,但也得一条条过,重构的时候也要注意不要对不懂的代码乱改一气,很多代码的危险性在于牵一发而动全身,我觉得这也是修复bug时的最大风险,往往是消除一个bug同时增加几个bug,还是回到原则1,要理解系统。不过有时候,理解前人的代码谈何容易,这也启示我们写代码时要多积德,不要以为代码就是给编译器看的,而应该是给以后维护我们代码的人看的。
6 保持审计跟踪
这条原则讲的是记录的重要性与方法,我想这就像医生问诊一样,需要问合适的问题,给出具体的有效答案。我的个人经验是一旦开始调试bug,可能整个人就彻底陷进去出不来,通过记录的方式可以把自己从思维的泥潭中抽身出来,不断修正调试bug的计划与方法。对一些专用系统软件,需要培训软件的使用者如何用有效的语言来记录出现bug的情况,如果没有记录,那么调试的人就需要用适当的问题帮助使用者唤醒记忆。此外,我觉得半夜调不出的bug需要睡一觉,让发散思维自动起作用,一般早上就解决了。
7 检查插头
这条原则大概说的是所谓”低级失误“了,类似我妈跟我说电脑音箱不响了,我首先得问电源开了没这种。又比如把main函数写成mian函数,有一次我打断点调试就是不进断点,后来发现是因为有两段代码比较类似,我断点打错地方了。根据场景和经验,问问自己是否犯了该场景下常见的低级失误,往往针对能立竿见影的解决很多问题,就像很多电器设备说明书的故障FAQ中,也会强调检查插头。可以说,低级失误低级的是难度,而不是频度。
8 获得全新观点
这条原则说的是求助和交流,这里面很重要的是通过他人的观点来破除自己的思维定势,所以最好是只给别人详细描述现象,不要说自己的猜测,以免污染别人的判断。对程序员而言,这时候面向google编程的力量是惊人的,可能九成的问题都不需要你真正的再去提问,而是找到合适的搜索词去搜索即可,对中国程序员而言,用英文搜索是必须的技能,当然搜到东西后真正理解也是非常重要的,不能见到药就吃。如果遇到的问题google真的搜不出来,证明你层次稍微高了点,这时候可以上stackoverflow之类的社区提问。
9 如果你不修复bug,它将依然存在
这条原则说的是不要心存侥幸,不要因为bug只是闪现了一下就采取鸵鸟策略,该来的总会来,不是不报,时候未到。当然实际情况可能很复杂,软件工程本来就是时间、成本和质量的妥协体,但即使放过某些bug,也要分析出这样可能造成的后果,建立充分的防护机制,因此在很多软件认证体系中,都会有软件安全等级标准。这里还涉及bug复现和举一反三的问题,需要确认真正找到了bug的原因提供了有效的修复手段,并排查所有类似的问题。比如修复了一个内存泄露问题,就要修复所有导致这类泄露的代码。
附记:如何写出适合调试的代码
从调试的原则反观,对软件构建本身也很有帮助,毕竟调试只是查漏补缺的,最好还是写出来的代码少一些bug,为了调试的时候方便多预留一些手段。这里也简要总结一下个人经验,可能需要不断完善。
1.程序中应该提供日志机制,在程序的debug版便于输出调试信息,即使在release版,也要记录软件发生问题(如C#中抛出异常)时的详细情况;
2.要写出适合人类阅读的代码,否则后续维护的人看都看不懂,要如何调试?怎么写《代码大全》中说得最详细了,我觉得除了各种习惯写法之外将心比心也是很重要的;
3.使用各种类库或工具时,尽量弄懂原理,仔细的阅读官方文档,比如有些类库会详细说明如何防止内存泄露,如何应对多线程情况,看清楚再用,就会少种下祸根,至少不用等到调试的时候再无头苍蝇一样猜测原因;
4.写便于测试的代码,这样在调试时,容易剥离问题,或者使用二分法,这方面TDD编程实践讲得比较充分,我个人也需要加强。
调试九法读后感(五)
《调试九法》副标题是<软硬件调试之道>,列举了9条调试规则:
看完之后,发现在工作中经常违犯其中一到两条规则。比如总是没有事实依据地猜测问题发生的原因,问题出现的时候只是草草地记录,导致后面忘记诱发错误的条件等。
可能你是程序员,那么本书对你有直接的作用。但是从某种角度来说,凡是遇到问题的人,都可以从这本书介绍的思维中借鉴解决问题的方法。
比如遇到问题,首先要理解出现问题的整件事情前因后果,它不出现问题是怎样的。不靠猜想问题的诱因而要去观察它们,把这件事情分为好的部分和坏的部分,我们目的是解决问题,所以只关注坏的部分。通过不断二分,就能锁定问题环节。这其实是解决问题的一种套路思维,掌握它,我们可以有效地解决大部分问题。
将调试分为三个阶段总结一下,调试前去理解和审计,调试中要观察验证,调试后反思总结。
1.调试前
通过阅读手册等手段,了解系统的工作原理和工作流程,还要懂得调试工具的能耐。至少在撸起袖子动手前,要知道正常长什么样子。
更向前一步,编码的时候,预测容易出问题的地方,打下不同级别的日志,给BUG留下一条轨迹。
是否进行了审计跟踪,最好有自动编译脚本,一键构建任意版本及其运行环境,不必为了重现劳心费力。有了审计跟踪,至少可以回到上一次系统能成功运行的好时光。
审计跟踪的详细程度、严重程度要记录,每步操作、顺序和结果,症状关联到时间和人员,注明相关事件和影响,甚至你的推理,修复的时候记录下操作。
既然BUG已经出现,在解决它之前,就要让它在受控情况下复现。复现BUG,目的有三——观察错误,查找线索,确认修复。
从头开始一系列时序步骤,模拟那些导致失败发生的条件,而且要记录下来便于分析,解决BUG后故意地制造条件来验证BUG被修复了。
2.调试中
调试过程中,不要想当然,要亲眼看到底层的失败,看到故障发生的情况。不要想,而要看。经验是使你尽可能少地按错误的猜测行动,而没有观察基础的猜测会让你误入歧途。
观察细节,把问题的原因锁定在几种可能之内。二分法是一种高效率的问题定位方法,因为我们目的在于修复问题,去验证正确的事情是无益的,要从有问题的一侧开始搜索。如果猜测不对,就要回到猜测之前。
我们之所以这样猜想,是因为这个猜想的情况更容易出现,或者是因为这个猜想更容易验证,凭空猜想是浪费时间。同样要质疑假设,你对调试工具所做的假设也可能是错误的。
植入插装工具(调试日志)、添加外部插装工具(GDB)、隔离变量、消除干扰的因素、对比正常和有问题的记录、回到上一次正常工作再逐步加入更改、寻求帮助、通过解释给别人听来理清头绪都是问题解决的方法。要注意海森堡效应,不要让插装工具影响了系统。
寻求帮助的时候,客观地描述,不要把别人带进你的理论。修复已知的BUG,不要让它们相互庇护。一次只改一个地方,如果修改没有解决问题,应该立即把它改回来。
3.调试后
确定问题是修复措施解决的,取消这个措施将导致系统再次失败。有时候不要过于相信统计数据,谨防BUG不再发生是没有找到特定的时序去引发它。
进一步追踪导致问题的基本条件,要从根本上解决问题。至少在关键的地方添加注释和日志,保留调试工具,下一次在周围出现新问题,就不至于从头再来DEBUG。
阅读成长
延伸阅读:
调试九法读后感(六)
调试九法:软硬件错误的排查之道-“你知道,现阶段我非常忙,但我打算在晚年倾力写一本书,把所有侦探艺术都集中写在这本书里。”-福尔摩斯《格兰其庄园》
调试规则-“我在这里要讲的理论(可能你认为它们非常荒谬),实际上都是非常实用的,我就是靠着它们挣得我这份面包和奶酪的。”-福尔摩斯《血字的研究》
调试规则:
1理解系统-当所有方法都不管用时读读指令,编程指南和API,自己写怀疑可能有bug的注释会使修改bug时更省力,硬件软件处理方法不同,图表不能被忽略因为数据表可能有模糊数据,应用程序可能有特定需求,养成良好的查阅习惯。
2制造失败-有规律地自动化能让错误更快发生,目的有三:观察错误查找线索和确认修复与否,注意测试不正确的序列,内存缓冲器可能装满没清空造成新连接出现异常,杂乱的数据有可能与电话公司的信号流量、附近小镇年轻人打电话的习惯及时间段有关。
3不要想,而要看-调试器的设置和分析器的正确触发, 在思考之前先观察,软件中观察意味着设置断点添加调试语句监视程序值及检查内存,树形地获得错误细节,获取详细信息观察变量、指针、缓冲层次、内存分配、事件时序关系、信号标记和错误标记查看函数调用和退出以及它们的参数和返回值查看命令、数据、窗口消息和网络数据包,直到错误范围足够小时再行动(修复),调试间歇性bug时观察底层的失败细节,在运行时开启状态消息输出调试窗口即在设计时考虑接入插入工具调试监视器和分析过滤器,把错误代码注释掉或加上“#ifdef”标记,无法使用调试器查看内部代码时可以接入调试总线的分析器对机器执行的指令进行反汇编留意十六进制数的加减进位,最易解决的问题先查看修复。
4分而治之-上下游,加入容易识别的溶剂很容易就观察到河流是怎么被污染的,直到查到污染与否的分界处了再查看分支,发现个问题马上解决因为解决了以后才会发现其他问题或者是同一个问题,有些问题(差的线程同步、意外重入例程及未初始化的变量)容易导致其他问题但不要过于极端要综合分析修复问题的难度和是否真的会引起问题。
5一次只改一个地方-调试时只改变一个变量或参数,发现自己的改变没改善问题的时候要记得改回去,查清楚问题所在再动手解决,对比正确和错误的调试日志,当新设计与原系统不兼容或新的条件使原有漏洞暴露时源设计跟踪系统(源代码控制系统SVN根据版本跟踪程序代码)(配置控制系统注意构建程序的工具)可以帮助你查看两个版本之间的所有区别。
6保持审计跟踪-不起眼的事情可能很重要因此必须记录下所有事情、做事的顺序及发生的结果(包括发生问题的准确时间、关键点和没在日志中出现的所有条件和症状),为读者提供足够多的信息让他们能够准确地理解你的体会,怀疑并注意所有细节关联起来(很多bug都是关联症状和时间表后发现的),保存调试日志和跟踪日志并注明相关的事件和影响以及你的推理和修复操作等等。
7检查插头-明确基础问题即怀疑自己的假设,当你看到另外一个世界的问题时应该停下来看看自己是不是还在地球上,一切都要从头检查特别是是否重载了新代码和初始化内存,对工具(默认设置和应用程序的环境)进行测试即上层没问题就要往下层找,工具本身可能也有问题所以要先验证工具没工作在有问题的东西上时也没问题。
8获得全新观点-向有专业知识或经验的人请教,碰到问题时先对自己讲一遍,知识管理系统可以从文档和电子邮件收集信息供查询谁掌握这些知识,问供应商甚至可以给一个修复或临时解决方案,当所有其他方法都失败时再次阅读手册和查询网上资源,不合理的地方也要提出但不要说你理论上认为是什么问题污染别人的思想。
9如果你不修复bug,它将依旧存在-不要假设问题已修复而要测试它,代码修复确认成功起作用要取消修复看问题是否再次出现。
详细小结:
1理解系统-“人们要想掌握本书中所有有用知识也并非完全不可能,事实上我就是尽全力这样做的。”-福尔摩斯《杀人的五个橘核》
阅读手册。它会告诉你在使用除草机时,要在除草头上涂润滑油,这样除草绳就不会被烧化。
仔细阅读每个细节。有关微处理器如何处理中断的详细信息就隐藏在数据手册的第37页。
掌握基础知识。电锯本来就会发出很大的噪声。
了解工作流程。引擎的转速可能和轮胎的转速不同,这是由传送轴造成的。
了解工具。弄清楚体温计的哪一端才是用来测量体温的,弄清楚Glitch-O-Matic逻辑分析器的强大功能是如何使用的。
查阅细节。连爱因斯坦都会去查阅细节,而Kneejerk却盲目相信自己的记忆力。
2制造失败-“什么也比不上直接取得的证据来得重要。”-福尔摩斯《血字的研究》
制造失败。目的是为了观察它,找到原因,并检查是否修复。
从头开始。修车工需要知道汽车车窗在被冻结之前你洗过车。
引发失败。用喷水管向漏雨的那扇窗子喷水。
但不要模拟失败。用喷水管向漏雨的那扇窗子喷水,而不要向另一扇不同的、“类似的”窗子喷水。
查找不受你控制的条件(正是它导致了间歇性失败)。改变能够改变的每件事情,振动、摇晃、扭曲,直到再现失败。
记录每件事情,并找到间歇性bug的特征。我们的绑定系统总是只在呼叫顺序错乱时才会失败。
不要过于相信统计数据。绑定问题看起来与时间段有关,但实际上真正的原因是当地的年轻人占用了电话线路。
要认识到“那”是可能会发生的。甚至冰淇淋的口味也会影响汽车的发动。
永远不要丢掉一个调试工具。自动击球版可能在某一天就会派上用场。
3不要想,而要看-“在没有事实作为参考以前妄下结论是个很大的错误。主观臆断的人总是为了套用理论而扭曲事实,而不是用理论来解释事实。”-福尔摩斯《波希米亚丑闻》
观察失败。高级工程师看到了真是的问题,并且能够找到原因。而初级工程师们认为他们知道错误发生在哪里,结果他们修复的地方根本没有出错。
查看细节。听到水泵似乎发出声音时不要停下来。到地下室查明是哪个水泵。
植入插装工具。使用源代码调试器、调试日志、状态消息、信号灯和臭鸡蛋的气味。
添加外部插装工具。使用分析器、示波器、量表、金属检测仪、心电图仪和肥皂泡。
不要害怕深入研究。虽然它是软件成品,但它出问题了,你必须打开并修复它。
注意海森堡效应。不要让仪器影响了系统。
猜测只是为了确定搜索的重点。大胆地猜测内存时序发生了错误,但在修复之前应该先查看它。
4分而治之-“当你排除了所有的不可能,不管留下了什么,也不管看起来多么不可思议,那必定都是事实。”-福尔摩斯《四签名》
通过逐次逼近缩小搜索范围。猜测1~100内的一个数字,只需7次。
确定范围。如果数字是135而你却认为它在1~100内,那么你必须扩大范围。
确定你位于bug的哪一侧。如果你所在的位置有排放物,则排放管就在上游。如果没有排放物,则排放管就在下游。
使用易于查看的测试模式。从干净、清澈的水开始,以便当排放物进入河流中时很容易看到它。
从有问题的一端开始搜索。如果你验证的是正确的部分,那么需要验证的地方太多了。应该从有问题的地方开始,然后向后追查原因。
修复已知bug,bug互相保护,互相隐藏。因此一旦找到,立即修复它们。
首先消除噪声干扰。注意那些导致系统问题的干扰因素。但对一些无足轻重的问题不要过于极端,也不要为了追求完美而去修改所有地方。
5一次只改一个地方-“有人说天才就是无止境地吃苦耐劳的本领。这个定义下得很不恰当,但是在侦探工作上倒还适用。”-福尔摩斯《血字的研究》
隔离关键因素。如果你在检查日照时间的影响,就不要改变灌溉方案。
用双手抓住黄铜杆。如果你在不知道具体发生了什么问题的情况下就试图去修理核潜艇,可能会引发一次水下的切尔诺贝利爆炸。
一次只改一个测试。我之所以知道我的VGA采集相位被破坏了,就是因为其他东西都没有发生改变。
与正常情况进行比较。如果所有出错的情况都有一些特征,而这些特征是正常情况所没有的,那么你就找到了问题所在。
确定自从上一次正常工作以来你改变了什么地方。我的工友改变了唱机转盘上的唱头,因此这是一个很好的调试起点。
6保持审计跟踪-“在侦探学的所有分支中,没有比足迹学这门艺术更重要而又最易被人忽视的了。”-福尔摩斯《血字的研究》国王说:“那个恐怖的时刻,我永远,永远也不会忘记。”“你会的,”王后回答说,“如果你不记一个备忘录的话。”-刘易斯••卡洛尔《镜中世界》
把你的操作、操作的顺序和结果全部记录下来。你上一次喝咖啡是什么时候?你的头痛是什么时候开始的?
要知道,任何细节都可能是重要的。视频压缩芯片的崩溃是由于格子衬衫造成的。
把事件关联到一起。“它发出噪声,从21:04:53开始,持续4秒”比仅仅说“它发出噪声”要好得多。
用于设计的审计跟踪在测试中也非常有用。软件配置控制工具可以告诉你那次修订引入了bug。
把事情记录下来!无论那个时刻多么恐怖,都要把它记到备忘录中,这样你才不会忘记。
7检查插头-“没有什么比一个显而易见的事实更能迷惑人了。”-福尔摩斯《博斯科姆比溪谷秘案》“深信不疑是真理的可怕敌人,甚至比谎言更为可怕。”-弗里德里希•尼采
质疑你的假设。是否运行了正确的代码?是不是燃气用完了?插头是否已插好?
从头开始。是否正确地对内存进行了初始化?是否按了除草机上的“primer bulb”按钮?开关是否已打开?
对工具进行测试。是否运行了正确的编译器?燃料油表是否被粘住了?量表是不是没电了?
8获得全新观点-“要想重新理清一个案子的头绪,最好的方法就是把它讲给别人听。”-福尔摩斯《银色马》
征求别人的意见。甚至一个不说话的人体模特也能帮助你认识到你先前没有注意到的事情。
获取专业知识。只有VGA视频采集卡的产商才能够肯定相位功能发生了错误。
听取别人的经验。别人会告诉你车内顶灯的线被挤压出来了。
帮助无处不在。同事、供应商、网络,还有书店,都在等待着为你提供帮助。
放下面子。Bug发生了。以除掉bug而自豪,而不要非得以自己除掉bug才为自豪。
报告症状,而不要讲你的理论。不要把别人拖进你的思维定式中。
你提出的问题不必十分肯定。甚至连“穿了格子衬衫”这样的事情也可以提出来。
9如果你不修复bug,它将依旧存在-“当危险已经离你很近时,拒绝承认它并不是勇敢的表现,而是愚蠢。”-福尔摩斯《最后一案》
查证问题确实已被修复。不要假设是电路问题,而仍然让汽车带着脏的滤油嘴上路。
查证确实是你的修复措施解决了问题。口中大喊“Wubaa!”并不是使计算机打开的窍门。
要知道,bug从来不会自己消失。使用最初导致它失败的方法再次制造失败。如果必须交付产品,那么就在产品中设计一个用于捕捉bug的“陷阱”,以便产品在客户现场发生失败时,把它捉住。
从根本上解决问题。在烧坏另一台变压器之前,先把无用的8音轨磁带卡座扔掉。
对过程进行修复。不要只是擦掉地上的油,而要纠正设计机器的方式。
所有规则总结:
1通过一个案例讲述所有规则-“你了解我的方法。它建立在对琐事的细微观察之上。”-福尔摩斯《博斯科姆比溪谷秘案》:读取脉冲的丢失并不是噪音造成的额外丢失而是电路板改动使芯片错误判断屏蔽读取脉冲。
2“有人发明,就有人能看懂。”-福尔摩斯《跳舞的人》:
2.1老房子的插座接入吸尘器使线路短路反而打开了电灯。
2.2线路过长使内存控制线引脚发出噪声改变了读取的数据。
2.3接口错误设置位数导致帧指示位丢失而呼叫失败,另外一个却错误使用了全部8位而没有发现bug。
2.4编译器把数组放在偶数的地址边界上使内存中两个数组之间留下了一个空位,而算法没有算到这个空位使Y值开始于X值总数之后,造成所有Y值所在位置都比它们原来的靠后一位,读取时编译器读的就是后一个Y值了,造成右边界使用下一行的值结果差很多最后那个甚至是随机值,使触摸屏靠近右侧的准确度很差右下角更差。
3从帮助台得到观点-“总是与匿名者打交道,是一件很难缠的事情。”-福尔摩斯《蓝宝石案》:两边的情况根本不一样,比如电缆中的电路板和CD-ROM被当成杯架。
3.1理解系统。不仅是产品本身的一切内容以及推荐的或可能的配置,而且还有它的帮助台历史——以前报告过的问题和应急之策,另外还有一些与你产品相关的其他内容——连接到本产品的其他产品、运行在本产品中或基于本产品运行的部件、占用本产品所有内存的部件或者其他不利于本产品使用的方面。配置报告工具、第三方工具性能监视工具等能告诉你更多,而电缆接错问题非常多!
3.2制造失败。规律地发现错误消息,从头开始仔细识别正在被操作的是哪些系统、窗口、按钮和字段,并确保你确切地知道故障的具体内容和发生位置。
3.3不要想,而要看。耐心,远程控制程序或web会议服务和日志文件,逻辑分析器非常好用。
3.4分而治之。配置上的bug可以做一个带插装工具的特殊版本发给客户。
3.5一次只改一个地方。一定要保存好原始环境,阻止用户胡乱更换零部件,更改文件、软件模块或硬件组件,然后再观察变化。
3.6保持审计跟踪。指导用户完成或撤销一些操作时让他们完成时告诉你做了什么再进行下一步。日志或系统生成的其他审计跟踪比用户更可靠,要一直发掘和客户现场环境有关信息,比如把软盘用磁铁吸住和通过打字机。
3.7检查插头。不要假设用户如何使用的产品,他们会不知道没电会使文本丢失、Mac计算机上装windows、把文件举到计算机屏幕前想把它传送出去。
3.8获得全新观点。故障检修指南(公司产品的bug历史数据库)和周围同事都可以帮助你刷新自己对问题的理解、知道些没有归档却有用的信息及有建设性的应急办法。
3.9如果你不修复bug,它讲依然存在。为排故障检修数据库提供数据记录,要明确描述症状和解决问题的具体方法。应急措施修复后要尽力使后续产品避免相关问题。关注问题的残留影响或修复的副作用可以赶在发生太多随机变化之前处理问题。
3.10详细小结:
遵循规则。无论用户多么糊涂,都必须找到应用规则的途径。
对行动和结果加以确认。用户会误解你的意思,同时会犯错误。通过确认他们所说和所做的一切可以及早发现这些问题。
使用自动工具。不要让用户参与系统生成的日志和远程监控与控制工具。
即使是最简单的假设也需要确认。是的,有些人就是不知道有电才能使用字处理器。
使用可用的故障检修指南。要处理的很可能就是已知的、好的设计。不要忽略历史。
帮助完善故障检修指南。如果找到了某个已知系统的一个新问题,将解决问题的所有内容进行归档可以帮助下一个支持人员。
4结束语-“我漏掉了什么事情吗?我相信我没有忽略任何因果关系。”-华生医生《巴什克维尔的猎犬》:调试规则网站:http://www.debug-gingrules.com/。在处理完每个调试事件后要进行总结:你的做法是否有效?使用或不使用规则是否影响你的效率,下次你会采取哪些不同的做法?哪个规则应该重点实践?