Udacity调试课笔记之简化失败
这一单元是本课的核心,讲解了一个调试技术,就是Zeller教授自己发明的Delta调试方法啦!Delta debugging。这个方法还是很容易理解的,过程清楚、明白,不难。具体实现则看个人的需要及编程水平了。
首先,为什么要有这么一单元?为什么要简化?简化的意思是什么?简化就意味着:
1、理解事物更容易
2、解释事物更容易
那根据什么原则来简化呢?这里就有个“相关”relevant的概念了。什么是“相关”的事物或条件?
好说,考虑佛家常说的因果,有因才有果,把 因 看作是果的“相关”条件的话,无因就无果——在这个相关的条件(集合)发生了改变时,结果就会不一样。所以,能够有差之毫厘,失之千里效果的东东,就是“相关”的条件了。
为什么要关注“相关”的条件?这很简单,就像福尔摩斯和柯南从错综复杂的案件中,找到真正的凶手一样,有很多人看似与案件有关,但永远不是真正的凶手。而我们要做的也是一样,抽丝剥茧,找到真正跟程序失败有关的输入。进而通过简化后的输入来找到程序中的缺陷。
所以,本单元的目标就是:
1、将测试案例简化,简化到剩下的每个字母都是失败的“相关”条件。
2、自动化完成步骤1
而要实现自动化需要两个条件:
1、策略——如何进行每一步的化简。
2、自动检测的机制。每一步化简后,确定是否通过测试。一般是直接使用测试框架,调用程序来处理化简的数据集合。
本单元介绍的策略就是Zeller教授的Delta调试法,应该还存在其他策略,大家积极发言啊。
课程中,他从《程序设计实践》一书的二分调试查找法开始,过渡到他的Delta调试法,我这里就直接列出最终Delta调试法的内容:
准备工作:输入数据集合X,初始化子集个数n = 2。
步骤:
1、将X分成n个子集(均不均分不用我说了吧),初始化n = 2,见准备工作。
2、自动检测各个子集,如果没有通过测试,则将这个子集作为新的X,递归处理,n=max(n-1, 2)。
3、全部通过测试,则提高颗粒度n=min(2*n, x的大小)。
再把本单元最后才介绍的内容提前在此说一下。Zeller教授一开始的代码中,是对HTML文件逐个字符地进行处理,但实际可以针对HTML的格式,将HTML代码分成各个标识符,这样就组成了一个标识符列表,HTML起作用的基本单元当然就是标识符啦。然后再使用delta调试法对这个列表进行处理,效率上会高很多。
也许有人会关心Delta调试法的效率如何——Delta调试会不会很慢?答案是,呃,从时间复杂度来看,不慢。因为最终简化得到的结果往往只占原集合的很小一部分,所以多数的阶段里,Delta调试法是以二分查找的方式在运作——也就是《程序设计实践》里的二分查找调试,只有极少数阶段会接近线性查找。所以,哪怕是1000行的html代码,每行20个字符,处理起来也会飞快(最起码应该比上一单元那模式库匹配要好一点吧),更不用说将结构因素考虑进去后,可以大幅度的减少数据的单位了。所以Delta调试法的时间复杂度大约是O(log n)。话又说回来了,如果他的检测机制过慢的话,那肯定是很慢了,比如每次都要重启下电脑花上两分钟的话,这就太慢了。
最终,使用delta调试法和检测机制的结合,将一个800行的html文件,化简成了8个字符的一个标识符,一个html标签,<select>。可以说是化简到了极致。
Delta调试法还可以用于简化fuzz inputs 和 更新(补丁),还有像sql 的where条件检查,处理方法与上相似,检测机制略有不同而已。
Fuzz测试就是生成随机的输入来测试一个程序和API,生成的随机输入就是fuzz inputs,用Delta调试法可以找出程序或API无法处理的输入是什么。
更新(补丁)则是找补丁中最小的、“相关”的补丁子集。
哲学层次部分笔记就先省了。最后补充两点:
1、Zeller教授指明了,之前找到的<select>标签是引发Mozilla崩溃的输入,但真正错误的当然不是这个标签,它的语法没有错误,就算错误的html也不应该引起浏览器的崩溃,所以实际的bug还是Mozilla浏览器中处理这个标签的代码。
2、可以使用Delta调试法的输入还是比较特殊的,比如像html,像sql 的where条件,输入的不同子集之间不存在影响或影响很小,而像算法处理的那些数据就不可能用Delta调试法来化简了。
调试课的字幕翻译完第3单元,第4单元在中秋节放假那几天因为没有网络,无聊地翻译了几个,现在已经不想再翻译了。累。