Automatic Software Repair: A Survey 自动软件修复:综述 (3)

原论文:Automatic Software Repair: A Survey 

原作者:Luca Gazzola, IEEE会员 Daniela Micucci, IEEE高级会员 Leonardo Mariani

2017年10月发表在IEEE Transactions on Software Engineering

原文链接:https://ieeexplore.ieee.org/document/8089448

译者:ClarkC.

此翻译已经原作者授权,翻译内容的准确性与原作者无关,引用请注明出处

感谢原文作者Leonardo Mariani教授提供翻译授权。鉴于原文很长,该翻译将分为六次上传。本次包含原文第6.1.1节的内容。原文中的参考文献可至附在开头与结尾的原文链接查看。

6.1.1 原子更改操作

原子更改操作在其抽象语法树(Abstract Syntax Tree,AST)的单个位置修改程序,例如插入、删除或修改语句,甚至修改表达式的单个运算符。这是最简单的更改操作类,因为它不需要对程序进行任何分析,而只需要分析需要更改的程序位置即可实现。

有几种技术使用这些简单的更改操作,因为它们可以被有效地应用并组合在一起,以获得正在修复的程序的许多变体,从而可能找到一个正确的修复。相反,需要对正在修复的程序进行更详尽分析的更改操作可能会减慢搜索过程,尽管它们在某些情况下可能更有效。下面,我们将讨论使用原子更改操作的生成并验证技术,并以类似的方式对这些技术进行分组。

6.1.1.1 通用的基于搜索技术

到目前为止设计的基于搜索的程序修复技术(参见表1中“基于搜索“行)实现了一种随机搜索过程,该过程旨在潜在地应对代码中的任何类型的缺陷。这类技术[20],[27],[29],[36],[50],[96],[98],[99]使用稍有不同的算法和不同的启发式方法,以最大限度地提高在程序AST级别定义的一组原子更改操作的效率,即每个操作改变AST的单个元素,通常是删除、修改和在AST中插入节点,同时修改语句中使用的单个运算符和变量。

特别地,GenProg、Marriagent、RSRepair和SCRepair使用相同的三个原子更改操作:删除AST的一个语句、插入从AST的随机位置复制的语句、使用从AST的随机位置复制的元素修改AST的元素。这组操作构成了一个相当通用的更改模型,只要修改后的程序包含足够多的可以从一个位置复制到另一个位置的语句,就可以任意修改程序。JAFF通过可以操纵AST子树的操作和可以在程序中随机生成新语句的操作扩展了这组原子更改,从而进一步扩展了可以在过程中每个步骤产生的更改集。

请注意,这些更改操作是通用的,并且可能有助于修复程序中的任何缺陷,只要程序中的其他地方仍包含必须用于生成修复的语句(此假设称为整形外科假设[100])。例如,可以通过这些技术来修复错误的算法4,只需将程序的第9行语句exit(0);复制到第2行。当然,如果程序中没有包含修复所需的语句,这些技术就无法修复故障,例如算法2要更改的if条件没有在程序中其他地方出现。JAFF是此规则下的一个例外,因为它具有随机生成新语句的能力,这使得它在处理大量缺陷时十分有用,尽管随机生成修复所需语句的可能性很小。

MUT-APR,pyEDB和CASC还将潜在地修复任何类型的缺陷作为目标,但是他们选择采用一种完全不同的变更模型。他们没有使用可以产生大量不同更改的更改操作,而是使用了一小组集中的更改操作。特别地,这些技术集中于使用可以修改目标程序中使用的运算符和变量的更改操作。

下面,我们将详细讨论各个技术。

GenProg [20],[64],[65],[87],[88],[101]是一种使用遗传编程来指导生成和验证活动的修复技术。在每次迭代中,根据与基于频谱的故障定位算法计算的语句的可疑程度相匹配的概率分布,来随机确定要应用原子更改的位置,如第5.1节中所述。可以看出,如果目标语句与故障充分相关,则更改更有可能影响故障。

当一组非空的候选解可用时,生成活动除了使用原子更改操作外,还使用单点交叉。单点交叉随机选择两个候选解,随机选择两个解中的一个点,通过将第一个候选解的初始部分与第二个候选解的最终部分并置产生两个新的候选解,反之亦然。

每个候选解决方案均已通过可用的测试套件进行了验证。GenProg定义了一个适应度函数,该函数根据通过和失败的测试用例的数量来衡量每个程序变体的质量。具体而言,程序的适应度定义为通过和失败的测试用例数量的加权和。通过的测试用例与正权重相关联,而失败的测试用例与负权重相关联。适应度较高的程序变体得到保留,而适应度低(小于或等于0)的程序则被丢弃。

整个过程与基于突变操作(即原子更改操作)、选择(即丢弃许多测试失败的候选解的能力)和交叉(即混合筛选时剩余的合格解决方案的能力)的经典遗传编程一致。GenProg的搜索空间由所有可以通过应用任意数量的原子更改和交叉得到的程序组成。

文中还对GenProg的有效性进行了优化研究。例如,当需要很长时间重新编译和重新安装程序时,可以利用弱重新编译技术在增量生成的候选解决方案上运行测试用例,而不必重新编译整个程序[102],[103]。类似地,当GenProg应用于x86汇编代码和Java字节码时,可以以共享库的形式重新编译更改后的函数,然后调用这些库而不是程序中函数的原始版本,从而节省了重新编译和重新安装整个程序的时间[104][105]。

Marriagent [96]是一种类似于GenProg的技术,但使用了不同的交叉算法。由于随机或基于适应度选择个体进行交叉可能会导致相似个体的选择,这可能会产生只略微提高候选解的种群质量的相似后代,所以Marriagent选择个体进行交叉有利于多样性。在实践中,它考虑对每个程序相对于原始程序的共同和总体更改集,来衡量一对程序之间的差异。选择交叉对的概率取决于交叉对中程序的多样性。

RSRepair [29](以前称为TrpAutoRepair [106])是使用随机搜索来指导修复生成过程的一种修复技术。每次迭代中,它确定必须应用于要随机修复的程序的更改操作,而应用更改的位置则根据依据语句可疑性的概率分布来确定。

与GenProg相比,RSRepair不使用进化算法,因此它不会对候选解决方案使用原子更改,也不会执行交叉操作。假设在自动程序修复领域,随机搜索算法可能表现得很好,甚至比进化算法更好,因为平滑的渐进进化可能很难定义。在每次迭代中,RSRepair只生成一个候选解决方案(图5中的k=1),随后立即被验证,如果其没有通过所有的测试用例就被丢弃。因此,任何候选解总是包含与要修复程序P有关的单个更改。这将导致搜索空间,其中包含通过修改要修复程序的单个语句可以获得的所有程序。因此,使用此算法无法生成需要对多个位置进行多次更改的修复。

为了加快候选解决方案的验证速度,RSRepair对可用测试用例进行优先级排序,以提高测试套件中较早执行的一个测试用例能够检测到不正确候选解决方案的可能性[107]。优先级策略根据能够排除的候选解决方案的数量为每个测试用例分配优先级。已经有效地排除了无法修复程序的变更的测试用例应比其他测试用例更早执行[106]。

SCRepair [98]扩展了RSRepair,引入了一种度量标准,该度量标准计算两个AST的代码片段的相似度。此度量用于指导在转化过程(mutation process)中选择可以替换现有代码的新代码。特别是,SCRepair查找与必须替换的代码不完全相同的的代码,但必须足够相似,这样它们才能与更改位置周围的代码很好地集成在一起。如果程序的多个位置都有满足此标准的代码,则开发人员可以定义更可能修复程序的更改类型,因此在选择时作为首选(例如,引入If条件的更改)。

JAFF [63],[99]是一种利用一组在单个节点级别和要修复的程序的AST子树级别都能工作的更改操作的技术。更改的位置依然基于语句的可疑性来确定,但是JAFF增加更改位置选择的随机性的方法是,首先随机选择n条语句,然后在所选语句中选择可疑度最高的语句。这些更改操作可用于根据三种可能的搜索算法生成候选解:随机搜索,爬山(hill climbing)和遗传编程。

图6:算法3中while循环的ATS

pyEDB [27]使用遗传编程来生成和演化候选解决方案。与GenProg相比,pyEDB引入了一种新颖的方法来表示必须转化的候选解决方案。GenProg使用候选解决方案中代码的完整表示形式,但pyEDB通过考虑相对于必须修复的程序的增量来表示候选解决方案。特别是,候选解决方案通过已应用于正在修复的程序的一组更改来辨识。这种新的表示形式特别方便,因为它结构紧凑且处理效率高。实际上,与整个程序的大小相比,获得候选解所需的一组更改总是很小。

为了获得这种表示,pyEDB利用了一个小型且集中的变更操作集。当需要修复一个程序P时,pyEDB首先生成两个修改表:第一个表包括对P中的关系运算符的所有可能的更改,第二个表包括对P中变量名的所有可能的更改。更改必须始终使程序有效,因此变量名只能替换为修改指令作用域内的其他变量名。pyEDB用32位字符串表示每个更改,其中前4位标识修改表,接下来的20位标识必须修改的AST节点(即标识表中的行),后8位标识特定的更改(即标识所选AST节点的修改表列出的更改中的特定更改)。

例如算法3中的while循环,其AST如图6所示,则相应的修改表如下:

算术运算符替换的修改表

变量替换的修改表

搜索空间完全由程序定义,这些程序可以通过应用修改表中报告的任何更改组合来获得。因此,任何候选解决方案都可以表示为一组32位字符串的集合。

在[102]中也探讨了关注增量的想法,其使用了同样的思想,即通过部分重新编译程序来优化编译时间,从而大大降低了编译成本。

pyEDB根据基于频谱的缺陷定位,使用交叉和偏倚选择修正集(即候选解)来进化修正集。

MUT-APR [36]是一种与GenProg非常相似的修复技术:它使用基于频谱的缺陷定位来识别潜在的故障语句,并使用遗传编程来探索可能的程序变体的搜索空间。两者的主要区别在于用于生成搜索空间的原子更改操作的性质。GenProg是一种通用技术,而MUT-APR专门解决可以通过修改程序中使用的运算符来修复的故障,因此,更改操作仅限于替换关系、算术、按位和移位运算符。

CASC [50],[97],[108]使用遗传编程和主要影响编程运算符和变量名的原子更改操作。CASC阐述了Arcuri等人[35],[40]先前提出的协同进化的概念,即CASC不仅进化候选解,而且进化测试用例。显然,奖励通过最多测试用例的候选解决方案和排除最多候选解决方案的测试用例,可以得到更好的程序变体和更好的测试套件。该解决方案具有挑战性的方面是在不产生错误测试用例的情况下改进测试套件,不正确的测试用例可能会将程序变体的生成引向有缺陷的程序。

 

示例

对于第4节中介绍的样本缺陷集,GenProg、Marriagent、RSRepair和SCRepair只能修复算法4中报告的故障。实际上,修复包括在第1行if条件的then分支中插入exit(0); 语句,可以通过原子更改操作获得,该操作将程序中已经存在的语句插入到程序的另一个位置。在算法4的情况下,第9行出现了exit(0); 语句。

第4节中的其他缺陷都不能用这四种技术中的任何一种来修复,因为程序中没有修复要用到语句。当然,如果这个算法是更大程序的一部分,并且修复所需的语句出现在代码库的其他地方,GenProg,Marriagent,RSRepair和SCRepair也可能会修复这些错误。实际上,GenProg,Marriagent,RSRepair和SCRepair能够起作用在很大程度上取决于以下假设:修复程序所需的语句可以在要修复的程序中的某个位置找到。

对于JAFF而言,由于它能够随机生成新语句,因此它可能能够修复第4节中报告的任何错误。 但是,尚不清楚该功能在实际应用中效果如何。

pyEDB、MUT-APR和CASC处理第4节所述样本缺陷的能力取决于缺陷性质与每种技术所使用的更改模型之间的匹配。这三者可以处理能通过更改运算符和变量名来修复的缺陷。在示例中,它们都可以通过将 < 替换为 == 来修复算法5中的故障。由于CASC和pyEDB也可以更改变量名,所以它们都能修复算法3和6中的缺陷。虽然这三种技术都不能解决算法2和算法4中的问题,因为添加新语句和修改复杂的if条件不在其更改模型的范围内,也就是说,修复后的程序不在这些技术的搜索空间内。

 

6.1.1.2 通用的蛮力技术

蛮力技术通过系统地探索搜索空间来搜索针对要修复的程序的修复。通常,不同的技术可能使用完全不同的策略和不同的原子更改操作集来解决不同类型的缺陷。此类别中的程序修复技术使用四种类型的更改操作(请参阅表1中的“蛮力”行):运算符替换和条件否定,方法调用的插入或删除,功能删除和AST操作。

Debroy和Wong的技术[45]研究了利用变异测试中使用的相同操作进行自动程序修复的思想。因此,该技术所考虑的原子更改操作是将算术、关系、逻辑、增量/减量或赋值运算符替换为其他同类运算符,以及if/while条件的取反。

它的工作方式是系统地更改要修复的程序中的语句,从最可疑的语句开始,到最不可疑的语句为止,或者到分配给修复过程的时间用完为止。该技术的搜索空间仅包括可以通过对正在修复的程序进行单一更改来修复的程序。

PACHIKA [25]是一种利用规范挖掘技术[109]来修复面向对象的软件的程序修复技术。PACHIKA中的思想包括观察和比较在通过和失败测试用例执行期间的软件行为,以便为每种调用的方法推断成功执行该软件所必须满足的前提条件。失败的测试用例必须违反了某些先决条件,否则该技术无法修复缺陷。

其修复策略包括通过添加或删除可能影响违反前提条件的方法调用,在基于模型的修复处定位(请参阅第5.2.1节)所标识的位置修改程序。被移除的方法不会从程序中物理删除,而是包装在if块中,在前置条件不成立时会跳过它们的执行。

Kali [110]是一种试图通过删除可能不必要但有害的功能来修复程序的技术。Kali执行此策略的方法是:使用基于频谱的缺陷定位来识别程序中500个最可疑的语句,并使用一组专门设计用于删除功能的原子更改操作来系统地修改这些语句(例如,将if条件设置为true或false,添加返回语句或删除语句)。KALI所考虑的搜索空间包括:从正在修复的程序中,将KALI的一个原子更改操作应用到500个最可疑的语句中的一个,可以获得的所有程序。

尽管在某些情况下KALI可能会成功修复缺陷,但该修复解决方案的主要目的是通过实验研究得出合理但不正确的修复问题。实际上,能让测试用例通过的程序通常可以通过简单地删除功能来获得,但是开发人员很少会把这种合理的解决方案当作正确的修复,因为他们在修复程序的时候并不希望损失功能。在第9.1节中将进一步详细说明生成不仅合理而且正确的修复程序所面临的挑战。

AE [111] 所考虑的搜索空间与RSRepair相同,即所有程序变体的空间,可以通过将单个GenProg的单个更改操作应用于程序的单个语句来获得所有程序变体的空间。其挑战在于该搜索空间通常很大且难以导航。 AE旨在通过利用语义等效检查来减小搜索空间的大小。

语义等效检查包括检测语义上相同但在语法上不同的候选解决方案集的功能。此功能允许在不运行测试用例的情况下排除多个候选解决方案,这通常是昂贵的操作。如果两个候选解决方案的任何如下元素不同,则AE可以检测到它们在语义上是等效的:

•语法等价:仅因存在重复的变量名或重复的语句而有所不同的程序;

•死代码:仅因存在某些死代码而有所不同的程序;

•指令调度:程序在某些相邻指令的顺序上有所不同,而不引用公共资源,可以在不影响程序语义的情况下对其重新排序。

只要被修复的程序是确定性的,AE就是确定性的,因为它利用SBFL产生的排名来系统地更改程序中的语句,从最可疑到最不可疑。

 

示例

PACHIKA和KALI对于第4节中介绍的缺陷样本集无效。PACHIKA无效是因为该程序不是面向对象的程序,因此,所提出的缺陷均不在本技术范围内。KALI无效是因为这些故障都无法仅通过删除功能就修复。Debroy和Wong的技术可能能够修复这个有缺陷的程序,这取决于有缺陷的程序是否使用了不正确的条件和算术运算符,如算法3和5。由于AE与基于RSRepair搜索的技术具有相同的搜索空间,所以AE也可以解决相同的缺陷,因此可以修复算法4中报告的缺陷。

 

(未完待续)

若发现翻译问题,可直接评论或与我联系:[email protected]

原文链接:https://ieeexplore.ieee.org/document/8089448

译者:ClarkC.

此翻译已经原作者授权,翻译内容的准确性与原作者无关,引用请注明出处

猜你喜欢

转载自blog.csdn.net/ClarkCC/article/details/107269810