调试九法

Debuging

调试在编程工作中很常见,几乎每天都会很它打交道。可以说对于编程人员来说是一件很困扰的事情,有时候查找一个 Bug 原因,需要花费大量的时间和精力。

本书一共总结了调试规则九则:

1.理解系统

2.制造失败

3.不要想,而要看

4.分而治之

5.一次只改一个地方

6.保持审计跟踪

7.检查插头

8.获得全新观点

9.如果你不修复 Bug,它将依然存在

单看这 9 个规则的标题,会有熟悉的感觉吧,会不会跟自己平时调试的时候,有一些方法差不多,觉得很简单。那回想一下,这些简单的规则,是否都能用对地方呢,是否在关键的时候,想的到呢?

其实在实际工作中,有时候越简单的规则往往越被人忽略。有些规则显而易见,会记得运用在特定的问题上,越往往不容易;有时候会被人忽视,有时候会走所谓的捷径,最终所花的时间越久。所以看的过程中,有时候会产生共鸣;有时候会暗自懊悔,自己为什么当时没有使用这种调试方法,所以要记住并运用好这些规则会很有帮助。

读这本书并写下读书笔记,最主要的是想提高自己的调试能力,提高工作效率;当然也能节省下来时间去做其他的事情。 书中每个规则里面都有详细细分了规则使用的不同情况,还有举例说明。我把自己在看书过程中的想法记录一下,加深下对于规则的理解,以便更好的运用到工作生活中去。

理解系统

项目已经到了中后期,基本每天会被分配到不同的 Bug,有时候会接到自己不熟悉的功能 Bug,接到这样的 Bug,首先要做的工作就是理解系统。

理解系统就是你必须掌握系统的工作原理以及它是如何设计的,在某些情况下,还要知道为什么这么设计,如果你没有理解系统的某个部分,那么通常就是出问题的地方。

『昨天我在查找一个 Bug 的原因:我查找到在跟 AI 一起比赛时,对方使用的 Buff 跟显示出来的一致;跟真实的玩家比赛时,对方使用的 Buff 跟显示出来的不一致。我咨询了主程 AI 跟真实玩家的数据上的区别:AI 的 Buff 使用是纯客户端处理,而真实的玩家需要发送给服务端,服务端会再进行计算,再分发给其他玩家的。我断定问题出在服务器那边。最后结果肯定不是服务端的问题,问题出在客户端读取服务端的一个类型 type 上,type 并不是 BuffType 而是需要做一个类型转换,之前直接当做 BuffType 接收的,所以会出现对方使用跟显示不一致的问题。』

上面的事例中我忽略了一个错误,就是没有理解系统,我没有去查看这个功能的数据接口相关的文档说明,导致了判断错误。

在开发的过程中,在接触到新的功能时、修改 Bug,容易犯经验主义错误,没有仔细的看相关文档,导致最后漏掉功能细节,或者修改 Bug 不彻底,甚至找不到出现 Bug 原因。

理解系统可以通过以下几方面来做:

1.阅读手册,手册里会告诉你正确的使用方法;

2.逐字逐句的阅读整个手册,解决问题的方法可能就隐藏在不起眼的角落;

3.知道什么是正常的,即必须掌握你所工作技术领悟内的基础知识,才能知道什么是正常的;

4.知道工作流程:当你尝试查找错误时,必须知道要查找的路线;

5.调试工具是用来观察系统的眼和耳,你必须选择正确的工具,正确地使用工具,并正确地解释得到的结果;你越是精通工具,就越容易查明系统中发生了什么事情;

6.了解你的工具:调试工具是用来观察系统的眼和耳,你必须选择正确的调试工具,正确的使用工具,并正确的解释得到的结果。还要必须了解工具的局限性,可以显示什么错误,不能显示什么错误。还必须了解开发工具,所使用的编程语言;

7.在项目中,对一个组件或者语法的运用有分歧,不要盲目的相信自己的记忆力,不要猜测,去查阅手册,要养成良好的查阅习惯。

制造失败

有时候理解了系统,还是锁定不了 Bug 的来源,这时候就需要执行下一个方案了。

1.制造失败:顾名思义就是把失败再重复一遍,或者几遍。

为什么要制作失败呢?

制造失败的原因有三个:可以观察他、可以专心的查找问题的原因、可以判断是否已修复问题。

2.要如何制造失败呢?

不同的情况采取的方式也不同。

最好的是在相同的环境下再操作一次。仔细观察做了什么,然后再做一遍,记录每一个操作步骤,然后按着步骤去做,看看是否能复现问题;如果复现的步骤简单,那就从头开始操作;如果复现的步骤复杂,且复现的频率不一定,这时候可以添加自动化测试。

3.不要尝试模拟失败

引发失败和模拟失败是不一样的。引发失败是在相同的办完操作环境下,试图重现失败的过程,即使做了一些改变,也是影响错误发生的频率,并没有影响错误发生的方式;而模拟错误是猜测错误发生的方式,并构建一个配置来模拟,这个有可能会发现新的问题。

4.间歇性 Bug

当故障只是偶尔发生时,用『制造失败』这种方法来调试就困难得多。很多棘手的问题都是间歇性的,这就是不能总是应用这条规则的原因—— 它很难应用。

那么如何处理间歇性 Bug 呢?

首先,把可能影响结果的因素都整理一下,然后查明它们;一旦查到了有哪些条件可能影响你的系统,必须大量尝试与这些条件相符的各种形式;初始化这些条件,并按照一种已知模式把这些条件作为你的问题软件的输入。

5.如果做了所有尝试之后问题仍然间接性发生

那么就要回归制造失败的最初的目的:一是观察错误,二是查找线索,三是确认是否已修复。

如何确认是否已修复呢?

更好的方法是找到一个总是与失败有关的事件序列。即使这个序列本身就是间接性的,但当它发生时,100% 会发生失败;然后当你确实已修复 Bug 时,就可以运行测试,直到这个序列出现如果没有发生失败,那么你确实已修复了 Bug。

6.那不可能发生

『那不可能发生』这句话之前我自己也跟其他同事说过,但结果往往是打脸的。既然同事测试出来了一些问题,事实是 Bug 已经出来了,我们只有接受这个数据并仔细分析具体情况,才会发现那个问题,然后尽快解决那个 Bug。

7.永远不要丢掉一个调试工具

有时候调试工具可以在其他的调试场合重复使用。当你设计它的时候,应该考虑到这一点,并且使它易于维护和升级,有时候也可以靠这个赚钱。

不要想,而要看

在没有事实作为参考以前妄下结论是个很大的错误,主观臆断的人总是为了套用理论而扭曲事实,而不会用理论来解释事实。

当要去解决一个 Bug 时,在前期亲眼看到 底层的失败是非常重要的。不要猜测失败是如何发生的,尝尝会修复一些不是 Bug 的问题;不但解决不了问题,还会浪费时间和金钱。

『不要想,而要看』中的『看』可以通过以下几方面来看

1.观察失败

2.查看细节

3.植入插装工具

4.添加外部插装工具

5.不要害怕深入研究

6.注意海森堡效应

7.猜测只是为了确定搜索的重点

分而治之

当你排除了所有的不可能,不管留下了什么,也不管看起来多么不可思议,那必定都是事实。

『分而治之』通俗的说,就是排除法,把最不可能的排除掉,剩下的就是有可能的;再在有可能的当中,排除掉最不可能的,剩下的一个,就是要找的 Bug 或者答案。

书中将『分而治之』分为 6 部分来介绍

1.缩小搜索范围

确定范围;确定问题在那一侧。

2.插入易于识别的模式

3.从有问题的支路开始查找问题

4.修复已知 Bug,Bug 互相保护,互相隐藏。因此一旦找到,立即修复它们。

5.首先消除噪声干扰

注意那些导致系统问题的干扰因素。对一些跟这个 Bug 无关的问题先记录下来,不要在这个 Bug 的过程中发现了另一个 Bug,又去解决另一个 Bug 去了。

一次只改一个地方

在尝试修复 Bug 的过程中,一次只改一个地方;要观察到具体发生了什么问题,再来尝试修复;修复的过程中,要隔离关键因素,一次只改一个测试;并与正常情况进行比较,如果所有出错的情况都有一些特征,而这些特征是正常情况下所没有的,那么就找到了问题所在;还要确定一下自从上一次正常工作以来你改变了什么地方,这有可能是问题的关键。

保持审计跟踪

保持审计跟踪,在检查某问题时,要记下来你所做的事,做事的顺序,以及发生的结果。每次都要完成这些记录。你是在检测测试步骤,就像检测软硬件一样。必须清楚没一个步骤和每步执行的结果,以此确定在调试时应重点关注哪一步。

我们在项目中的所使用的的 Log 日志,也属于审计跟踪。日志里面包括一些详细操作、操作的时间、相关数据。通过日志会发现这个问题的严重程度。对比不同的日志能发现 某些 症状与 其他症状 之间的一些『关联』,从而有可能发现出现 Bug 的原因。

写到这里想到了项目中用到了项目配置控制工具对于测试 Bug 也是很有帮助的。它可以告诉你那次修订引入了 Bug;能帮你『确定一下自从上一次正常工作以来你改变了什么地方』。

俗话说『好记性不如烂笔头』,在某些时候不要相信你的记忆,而要把它写下来。

检查插头

有时候遇到问题或者发现 Bug ,是一些简单的问题造成的。 一些显而易见的假设往往是错误的,但有时候是最容易修复的错误。

1.怀疑自己的假设 2.从头开始检查 3.对工具进行测试

获得全新观点

想要重新沥青一个案子的头绪,最好的方法就是把它将给别人听。

当自己身在一个 Bug 的迷雾中的时候,那个时候已经没有什么头绪了。这个时候需要跟其他人沟通下,可能会柳暗花明。

1.征求别人的意见,可能会帮助自己认识到先前没有注意到的事情。

2.获得专业知识,能够知道确定具体什么功能发生了错误。

3.听取别人的经验,别人可能比你经验多,也可能之前遇到过类似的问题。

4.帮助无处不在。

5.放下面子,Bug 发生了,以除掉 Bug 为自豪,而不要非得以自己除掉 Bug 才自豪。

6.报告症状,而不要讲你的理论,这样容易把别人拖进你的思维定式中。

7.你提出的问题不必十分肯定,自己有怀疑的事情可以提出来,有可能是问题的关键。

如果你不修复 Bug,它将依然存在

当危险已经离你很近时,拒绝承认它并不是勇敢的表现,而是愚蠢。

Bug 没有自己的思维,从来不会自动修复,虽然我们都希望看到 Bug 消失。如果自己修复了一个 Bug 后,要去立即检查问题是否已被修复,不要假设问题已被修复,而要测试它;检查确实是修复措施解决了问题,即取消这个修复,确定系统再次失败,然后再应用这个修复,再次验证问题已修复;如果这个 Bug 实在不好修复,也不要忽略它,要记录下来;要从根本上解决问题,而不是短暂的修复,那样过一段时间,还会 Bug 还会出现;要对过程进行修复,不要只是擦掉地上的油,而要纠正设计机器的方式。

笔记中大多数都有引用的书中的文字,是因为我感觉作者已经用了很准确是词句去描述,自己找不到更合适的词语去替代。

调试规则笔记写好了,接下来就是在项目中运用实际运用了,谨记关键的 9 条规则。