调试九法读书笔记(一)

前言

最近开始尝试新的技术领域。来到陌生的领域,虽然都是技术,但是感觉好像一下到了荒原。以前开发和debug的经验突然不适用了,顿时慌了手脚。不过,总算跌跌撞撞把程序写出来了,过程就不提了,中间都是辛酸泪。通过这次发现自己在舒适区待了太久,让自己都忘记了一些基本的编程和解决问题的能力。重新开始学习,强化内功。这本书是很经典的讲解遇到bug时候如何处理的书籍。自己一个人写程序有点闭塞,所以学习别人的思路开阔视野。书籍已经看了一半,收获挺多,把书籍的重要内容记录下来方便以后查阅。

第一章 简介

第一章作者把书籍的内容概括性的讲解了,提出了几个原则,给我很大的启发。

1.1 学会调试的基本原则

  1. 如果查找一个bug花费了大量时间,那么原因可能是忽略了某个基本的、重要的规则,一旦应用了那条规则,很快就会找到问题。
  2. 善于快速调试的人已经深刻理解并应用了这些规则,而那些很难理解或使用这些规则的人很难找到bug ps:所以作者后面就是在讲解这些规则了

1.2 本书的目的

目的只有一个就是查找bug并修复,而不是预防

1.3 本书适用于各种调试问题

我觉的不仅仅是适用于软件,硬件或者其他技术问题。生活中出现问题了,我们也可以使用这些规则。这些规则是一种思想,适用于各种场合。

第二章 全部规则

  1. 理解系统
  2. 制造失败
  3. 不要想,而要看
  4. 分而治之
  5. 一次只修改一个地方
  6. 保持审计跟踪
  7. 检查插头
  8. 获得全新观点
  9. 如果你不修复bug,它将依然存在

第三章 理解系统

在理解系统之前,我们是无法对问题进行调试的。一旦了解它,问题也就显而易见了。
如果你没有理解系统中的某个部分,那么这通常就是出问题的地方。(这不仅仅是墨菲定律的问题,如果你不能理解你所设计的系统,你的工作可能会一团糟)
ps:通常来说对系统越理解,那么它的异常情况,你就可以很快就定位到。理解系统的含义我觉得应该包含两个部分,一部分是业务内容,一部分是技术内容。业务内容就是为了实现业务而开发出来的代码,对业务内容的理解深刻,当出现业务异常和面对非技术人员的疑问时候,我们可以快速定位和解答,这样才能做好我们的本职工作。另一部分就是我们使用的技术,就是我们的技术工具和技术栈。就像是工匠的锤子和斧子一样,当他们出了问题,不好使了。我们就做不好“活”了,所以对这部分理解也是至关重要的。对自己使用的技术越熟悉,实现业务功能的时候就越迅速。

3.1 阅读手册

理解系统的基本方法就是阅读手册。
ps:阅读书籍,阅读手册,阅读源码这都是我们了解系统的手段。一般来说源码是根本,手册是权威,书籍方便理解。如果是新技术一般从书籍开始,然后是手册,最后是源码。对大神来说,可以直接上源码。我现在一般时间允许是从手册开始。

3.2 知道什么是正常的

只有知道什么是正常的,出现问题才能发现。
ps:当出现自己当前阶段无法解决的问题的时候,把一切都恢复到起点,然后逐步添加自己的改动,不失为一种解决问题的“笨”方法。

3.3 知道工作流程

知道系统的工作流程。当我们查找bug的时候,就知道查找的路线。这样我们可以采用二分法,查看中间过程中是否出现问题,不断二分最后找到问题所在。

3.4 了解你的工具

了解工具的使用是我们能做好工作的基础。如果我们想获取系统内存,那么我就要使用top,或者vmstat或者free。这时候使用其他命令肯定得不到我们想要的结果。同时不同的工具所输出的信息和使用方法也完全不同,所以了解自己使用的工具。

3.5 总结

理解系统
这是第一条规则,所以也是最重要的

  1. 阅读手册。它会告诉你使用除草机的时候要在除草头上涂润滑油,这样除草绳才不会烧化。除非我们是系统的创建者,否则最了解系统的应该是写手册的人。
  2. 仔细阅读各个章节,可能一些细节(“坑”)就在这些章节中
  3. 掌握基础知识。要有常识
  4. 了解工作流程
  5. 了解工具。搞清楚温度计哪一段才是用来测量体温的,程序只有在调试模式下,断点才会起作用。
  6. 查阅细节。连爱因斯坦都会去查阅细节,不要盲目相信我们的记忆力。

第四章 制造失败

如果我们能让bug复现,那么总能找到问题所在。

4.1 如何制造失败

仔细观察做了什么,然后再做一次,并且记录下做的每一个步骤。然后再按照记录下的步骤去做,确定是这样做导致了错误。

4.2 从头开始

制造失败要从一个正确的状态开始。不要从一个错误的状态开始,那样会导致更大的错误。

4.3 引发失败

当我们能确认失败的步骤后,让这个过程自动化会很有帮助。

4.4 如何处理间歇性bug

很多bug是间歇性的,关键问题在于我们并没有弄清楚失败是如何产生的。你知道自己做了什么,但并不知道完整的,准确的条件。还有其他你没注意到的或无法控制的因素,例如初始条件、输入数据,时序,外部过程,温度,噪声等等。
总之要想清楚哪些条件可以影响到我们的系统,必需大量尝试与这些条件相符的各种形式。初始化这些条件,并且按照一定的规则作为输入,直到问题出现。

4.5 为什么制造失败

  1. 观察错误
  2. 查钊线索
  3. 确认是否修复 这个过程就像侦探破案一样,真相只有一个,失败肯定有原因,你一定能找到它。它只是巧妙地隐藏在你尚未发现的大量随机因素背后。

4.6 是已经修复bug,还是仅仅是运气好,它不再发生

如果失败是随机发生的,那么想要证明bug是否修复就会变得困难许多。如果采用统计测试的方法,随着运行样本越多,结果就越准确。但是更好的办法是找到一个总是失败的时间序列,即使这个序列    本身是间歇性的,但是它发生的时候,100%会发生。然后确认修复后,我们可以运行这个序列,如果没有失败发生,那么可以认为修复了bug。

4.7 永远不要丢掉调试工具

调试工具应该本身包含在项目中,尤其是那种可以重复使用帮助我们更好的了解系统的。最好把他们工程化,并实现文档。另外日志是非常重要的了解系统的工具。在服务器上长时间运行的系统是一个黑盒,我们只能通过侧面的信息去了解系统内部的状态。日志需要将必要的信息打印出来,出现问题的时候才能方便追查,但是也不能打印过多,因为会影响系统的性能。

4.8 总结

制造失败,虽然看起来很简单,但是如果不制造失败的话,调试会变得很难

  1. 制造失败。目的是为了观察它,找到原因,并检查是否已经修复。
  2. 从头开始。修车工需要知道汽车车窗在被冻住前,你洗过车。
  3. 引发失败。用喷水的管子向漏雨的屋子喷水
  4. 但不要模拟失败。用喷水管向漏雨的那扇窗子喷水,而不要向另一扇不同的、“类似的”窗子喷水。
  5. 查找不受你控制的条件(正是它导致了间歇性失败)。改变能够改变的每件事情,振动、摇晃、扭曲,直到再现失败。
  6. 记录每件事情,并找到间歇性bug的特征。我们的绑定系统总是只在呼叫顺序错乱时才会失败。
  7. 要认识到“那”是可能会发生的。甚至冰淇淋的口味也会影响汽车的发动。
  8. 永远不要丢掉一个调试工具。自动击球板可能在某一天就会派上用场。