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

原论文: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教授提供翻译授权。鉴于原文很长,该翻译将分为六次上传。这是最后一次,包含原文第7节和之后的内容。原文中的参考文献可至附在开头与结尾的原文链接查看。


7. 修复推荐

修复推荐程序是一种不会尝试产生修复的技术,而只是提出一些可能在软件上进行操作以修复故障的更改。在某些情况下,建议的更改可能会完整描述所需的修复,在其他情况下,开发人员可能需要付出一些努力才能生成最终的修复。尽管这些技术不会产生实际的修复,也就是说,它们不会产生一个应该是正确的软件的新版本,但是在最好的情况下,它们的输出可以很快成为一个修复。

到目前为止,已经有了一些修复推荐技术。 在此,我们讨论根据故障模型分类的这些技术。 通用技术旨在对一系列程序都有效,而不是将其范围限于特定类别的故障。 其他技术可以解决:使程序容易受到攻击的安全漏洞;可能由于滥用数据结构和其他类型的数据而导致的数据类型故障; 可能导致死锁和其他并发问题的并发故障;可能会导致某些功能的执行时间无法接受的性能故障。

表4总结了所讨论的技术( “技术”列),每种技术处理的故障类别( “故障模型”列)以及描述该技术的章节(“章节”列)。

表4:修复推荐技术

 

7.1 通用技术

BugFix[39]可以分析特定程序语句中的调试信息,并向开发人员报告可能修复错误代码的操作列表。该技术利用基于频谱的故障定位来识别可疑语句,并使用静态和动态度量来对必须修复的问题与调试规则数据库中的条目进行分类和关联。最后将从数据库中提取的修复建议按优先级排序的列表报告给开发人员。

MintHint[141]还生成了一个可用于修复故障的操作的排序列表。MintHint使用基于频谱的故障定位来识别可能有故障的语句。每个错误语句都被一个符号状态转换器(即仅在程序状态上定义的一种抽象语句)替换,使其能够让程序通过所有可用的测试用例。然后,MintHint探索一个可以对错误语句执行的可能更改的空间,以获得由状态转换器定义的相同行为。最终将可能的解决方案根据在修复的语句中出现的可能性进行排序,返回给开发人员。

如果与开发环境适当集成,自动程序修复技术也可以作为修复推荐程序使用。Pei等人[142]已经研究过这种可能性,他们将AutoFix-E集成到EiffelStudio开发环境中,自动查找故障并推荐源代码级的修复。

Logozzo等人[143]提出了一种技术,该技术为富含规则(前置条件、后置条件和不变量)的程序在设计时推荐代码修复。修复建议的生成是由用于检查规则的验证技术触发的,在本例中是cccheck[144]。当断言不成立时,该技术利用基于向后分析或数据类型分析的一系列分析来产生可能的修复。

QACrashFix[55]通过从用户在Q&A页面发布的代码片段中提取可能的修复,使导致程序崩溃的错误的修复过程自动化。QACrashFix首先分析应用程序的崩溃报告,构建一个提交给搜索引擎的查询,以检索可能与故障相关的一组Q&A页面。然后利用检索到的页面中存在的代码片段来标识应在代码中实现的一组更改,以修复错误。此分析由在检索到的页面中搜索的自然语言关键字指导,例如“代替”或“将x更改为y”。可能修复程序的更改被编码在AST级别的编辑脚本中,这些脚本可以转换程序的源代码。最后,通过定位与检索到的Q&A页面中的错误代码片段匹配的代码中可能出现的错误区域,并运行AST编辑脚本来应用修复。注意,转换后的代码不是自动验证的,而是简单地由开发人员进行手动检查。

 

7.2 安全故障

BovInspector [145]使用静态分析和符号执行来分析缓冲区溢出威胁,并通过应用以下三种更改策略来提出解决方案:添加边界检查,用对更安全的API函数的调用替换API调用以及修改缓冲区实例化。 应用于每个API调用的特定更改基于一组模式。

Abadi等人[146]提出了一种建议修复Java中输入净化例程的技术。该技术使用商业工具来找出与输入值相关的漏洞,并建议对转义不安全的特殊字符进行修复,或者在SQL查询的情况下,将不安全的Statement类型替换为安全的PreparedStatement类型。分析时可以考虑不能清除的受信任值的存在。

CDRep [147]针对Android应用程序中的密码误用。 在初始阶段,它使用CRYPTOLINT [148]检测反编译Java代码中的误用情况。 然后,其建议使用一组预定义的转换规则进行修复,这些规则可以通过消除检测到的滥用来修改程序。 CDRep支持七种常见的密码误用。

 

7.3 数据类型误用

Coker等人[149]提出了一种技术,可以推荐针对C程序中Integer类型滥用的修复程序。 该技术提供了三种可以从Eclipse IDE执行的程序转换:添加显式整数强制转换操作,用对检测溢出和下溢的安全函数的调用替换算术运算符,以及更改整数变量的类型。

Malik等人[150]建议利用由数据结构修复工具[151]生成的报告来推荐修复程序。他们所提出的技术将Java方法,操作数据结构时该方法必须保留的结构不变性以及违反这些不变性的输入作为输入,并提出了应避免违反不变式的修正建议。 首先确定正确的数据结构而不是损坏的数据结构,然后确定产生这种数据结构应该执行的操作,从而确定修复程序。最后根据该技术支持的一组预定义案例,向用户推荐修复程序。

 

7.4 并发故障

ConcBugAssist [152]是一种诊断并建议并发故障的修复的技术。其应用有界模型检查来计算程序输入,并识别使程序失败的错误线程调度(例如,违反断言)。然后使用MAX-SAT解算器计算导致并发问题的最小线程交错集。接下来使用阻塞子句重复模型检查,以从分析中排除已找到的线程交错。重复此过程,到没有新的失败执行出现为止。这时,利用已生成的阻塞子句,可以将修复问题实例化为二进制覆盖问题,并就执行顺序关系向用户报告可能的解决方案。

 

7.5 性能故障

Selakovic等人[153]提出了一种为JavaScript性能故障推荐修复的技术。该技术基于在程序AST级别定义的23个重复修复模式。一旦应用了一个模式,就将执行并比较程序的原始版本和更改后的版本。如果修改后的程序的性能在统计意义上显著提升,则将其转换推荐给开发人员。

CARAMEL [56]是一种可以建议针对特定系列的性能故障的技术:那些在循环条件变为真之后依然浪费时间在循环内执行计算的应用程序。这些故障通常由开发人员引入break语句来修复。

CARAMEL通过首先确定循环内的语句来解决这些问题,在某些情况下,这些语句在循环外毫无作用或用处不大(即,它们影响在以后的计算中不使用的变量)。然后,它检查是否可以同时跳过所有被标识的语句,如果可以,则将与各个语句相关的条件的结合用作中断循环的条件。最后,当满足这样的条件时,CARAMEL检查循环是否已经终止。如果不是这种情况,则CARAMEL会向用户报告性能故障,并报告循环中包含的保护性中断条件形式的潜在修复。

 

 

8. 经验证据

软件修复技术已经在许多不同的背景下进行了评估,包括提出新方法的论文和独立的经验评估。我们分析了这些论文中的发现,并重新构建了迄今为止收集到的关键经验证据。这些证据对于了解该领域的研究现状、计划进一步的研究以及确定未来的研究方向具有重要意义。

我们首先讨论基准测试和应用程序,然后讨论结果,区分使用生成和验证方法以及语义驱动方法获得的结果。

 

8.1 工具,基准和实验主题

目前为止,本文考察了许多自动程序修复技术。为了促进技术的复现和不同技术之间的比较,工具的可用性以及基准的存在和共享主题应用程序至关重要。我们通过检查在提供自动程序修复技术的论文中是否报告了公共可访问工具来调查工具的可用性。我们还检查了介绍新技术的论文作者的网站,并在Google中搜索了Web输入工具的名称。使用此过程,我们已经能够找到针对本文所调查技术的46%的工具。我们在附录A中报告了这些工具的名称,其网站以及它们适用的语言。尽管可以使用几种工具,但根据我们的调查,只有不到一半的技术在Web上提供了可用的工具。在实践中,这可能会使结果的再现和技术之间的比较变得非常困难。

根据经验研究,已使用多种应用程序和缺陷来评估自动程序修复技术。 在大多数情况下,评估都涉及实际系统中的真实故障[154],[155],尽管有时也可以利用人为预置的缺陷[156]。 当出于评估目的需要测试套件时,通常使用由软件开发人员编写的测试套件[154]和使用测试工具自动生成的测试套件[24],[93],[138]。 该领域中最早的论文被认为是较小的程序,并为评估目的手动定义了测试套件[35],[64],[97]。

研究集中于单元测试用例的使用,有时系统测试用例用于命令行应用程序,而很少考虑配备GUI测试用例和验收测试用例的交互式应用程序。 PHPRepair [138]和R2Fix [120]是两个例外,因为它们分别使用GUI测试用例和类似于从用户提供的故障报告派生的接受测试用例的测试用例。

就大小而言,自动程序修复技术已经成功应用于具有成千上万行代码的程序,并且在少数情况下可扩展至具有数百万行代码的大型程序[154]。 这是该方法可伸缩性的一个很好的初步证据,尽管使用自动程序修复技术进行试验的大型应用程序的范围仍然非常有限,无法得出关于其可伸缩性的任何结论。

在分析中,我们确实注意到,用于评估的案例范围受到了公开的基准的强烈影响。事实上,最初用于评估GenProg[20]的一组应用程序,以及许多bugs和IntroClass基准测试[154]在我们分析的论文中被用作实验对象。如果我们还包括SIR基准[156],这个百分比将增加到45%。如果我们排除了仅使用示例作为验证形式的论文,以及解决并发和安全性错误的论文,这两个百分比将分别增加到44%和62%。我们可以得出这样的结论:尽管有基准是很重要的,因为它有助于技术之间的比较,但存在针对可获得基准产生过拟合的风险。因此,重要的是要更好地利用目前可用的一组基准(例如,CoREBench[155]、Defect4J[157]和IBugs[158]基准分别只在3%、2%和2%的论文中使用),并努力设计和发布有助于实现一般结果的附加基准。

 

8.2 生成和验证方法的证据

我们将从基于生成和验证的软件修复论文中提取的主要证据分为三组:关于测试套件影响的经验证据,关于搜索空间影响的经验证据,以及其他关于生成和验证方法有效性的经验证据。

 

8.2.1 测试套件的影响

通常,生成和验证技术会运行测试套件,以检查候选解决方案的质量并确定是否可以向用户报告可行的修复程序。在这一节中,我们列举了有关测试套件如何影响修复过程的主要发现。

测试套件的大小可能会严重影响生成的修复的质量 :生成和验证技术的行为在很大程度上取决于可用的测试用例。一个仅仅部分地覆盖了被修复程序行为的小测试套件,可能会导致生成和验证技术通过删除测试未检查但干扰修复问题的函数的方法来修复故障[110],[159]。在[160]中也有一种趋势,即通过删除语句来生成通过所有测试用例的程序,在这种情况下,将更改操作的选择偏向于删除语句操作,提高了成功率并缩短了GenProg的修复时间。另一方面,拥有大范围(且不必要)覆盖要修复程序的执行空间的大型测试套件可能会减慢修复过程的速度[29],[41],[161]。因此,要成功地修复程序,重要的是要拥有能够有效采样执行空间的测试套件,以覆盖可能没有冗余的大多数行为[20],[22],[27],[29]。

测试套件的性质可能会影响生成的修复 :一些研究报告说,使用覆盖率高的测试套件可能有利于生成和验证技术[162]。这可能会让人们相信,向测试套件中添加许多积极的测试用例是构建有效指导修复过程的测试套件的好策略。这似乎不一定是真的。事实上,经验结果表明,具有许多阳性测试用例的测试套件使修复过程更加困难[110],[161],只是部分地用更好的故障定位质量来补偿这一趋势[161]。相反,很少有证据表明大量失败的测试用例有时可能是有益的[161]。

初步研究调查了测试套件实现的覆盖类型的影响以及修复过程的有效性报告,与满足语句覆盖的随机测试套件相比,满足分支覆盖的测试套件可以更有效地防止生成错误修复[36]。

运行测试用例是修复过程的主要成本 :分析较大的程序时,演化一组候选解决方案技术的主要计算成本在于计算每个解决方案的质量,这意味着对所有的候选解决方案运行所有可用的测试用例[29]。例如,有报告显示其时间耗费可能占GenProg总修复时间的64%[41]。

该经验证据解释了为什么不发展候选解决方案的技术(例如,RSRepair和AE),尽管无法通过应用多个更改操作生成修复,但仍可能成功,有时比演进候选解的算法(例如GenProg)更有效。实际上,这些技术只需要运行测试用例来检查候选解决方案是否令人满意,并且当第一个测试用例失败时,测试执行可能会中断,因此与总是执行整个测试套件的技术相比,测试执行成本显著降低。测试用例的有效使用会使得更多的程序修复被生成和检查。

Fast等人[163]研究了如何通过减少为评估候选解决方案而必须执行的测试用例的数量来提高修复过程的效率。特别是,他们研究了修复过程的有效性,该过程使用测试用例的随机子集来评估每个候选解决方案,而不是使用整个测试套件。仅当候选解决方案通过所选子集中的所有测试时,才进行全面评估。有报告显示这种优化使GenProg的运行时间减少了81%。尽管此方法在适应度函数(fitness function)的评估中引入了噪声,但结果表明这在实践中可能不是问题。

测试如何覆盖缺陷比缺陷的复杂性更为重要 :有报告显示,生成和验证技术的成功与否很大程度上取决于测试用例对缺陷行为的采样情况,而不是缺陷本身的复杂性[20]、[22]、[27]、[29]。当然,只有在修复技术的搜索空间中存在修复,才能修复缺陷,这意味着可以通过应用该技术所使用的一个或多个更改操作的组合来修复缺陷。但是,对缺陷进行良好采样是成功修复的关键因素。

 

8.2.2 搜索空间

修复技术探索出的搜索空间的形状对其成功至关重要。合理和正确解决方案的密度等方面可能是影响修复过程有效性的决定性因素。

搜索空间的大小和形状主要取决于用于生成候选解决方案的策略(例如,基于搜索vs蛮力)和更改运算符(例如,原子vs模板)。在这一部分中,我们报告了关于搜索空间与生成和验证技术之间关系的主要发现。

策略的影响不明确 :该策略的影响,无论是基于搜索的还是基于蛮力的,以及策略的单个变体的影响,例如使用遗传编程或随机搜索,都不清楚。有对比的证据,有时表明一方对另一方有利,反之亦然。例如,据报道,随机比基因编程效果更好、高效地生成合理的修复方法[29],[164],反之亦然[161]。

这部分矛盾的证据可能是由于难以控制的因素造成的。例如,策略的有效性可能取决于可用的测试用例、缺陷和正在修复的程序的类型。此外,技术通常不仅在搜索策略上有所不同,还实现了其他可能影响观察结果的启发式和优化。因此,当报告不同的结果时,总是很难将搜索策略的效果从其他混杂因素中分离出来。

总的来说,关于搜索策略的可靠证据仍然很少,这种策略应该优先用于自动软件修复。

蛮力技术可能更适合修复小程序 :Kong等人[161]报告了初步证据,表明蛮力技术可能受益于小程序通常具有短的执行时间,因为它们可以有效地探索搜索空间的很大一部分,与进化候选解决方案的技术相比,这些技术仍然需要在每次迭代中多次执行整个测试套件,并且常常无法以更高的效率补偿额外的成本。虽然在较大的程序中,使用暴力技术进行的探索是有限的,但是暴力与其他方法之间的区别并不显著。

重复运行测试用例的代价至少可以通过适应度抽样技术得到部分补偿,这种技术只需要执行测试集的随机子集,如[87],[163]中所述。

这些证据可能是进一步调查的一个有趣的起点,而这些调查对于确认这一初步结果是必要的。

搜索空间中很少有正确的修复程序 :尽管据报告生成和验证技术能够生成合理的修复程序,即能够通过所有可用测试用例的修复程序,但对这些修复程序的仔细分析表明,大多数修复程序很难被开发者接受[110],[159]。 得到这些似乎合理但不可接受的修复程序,时因为可获得的测试用例的弱点,这一弱点使得一些不会被开发人员接受的程序修改发生(例如,删除功能)。

对由生成和验证技术产生的搜索空间的仔细分析显示,一些合理的修复方案(plausible fixes)可能会在获得正确的修补程序之前错误地返还给开发人员[159],从而阻碍正确修复方案的生成。 该证据提出了有关应用于指导生成和验证方法的测试套件应满足的属性的问题(第8.2.1节也有说明)。

当修复属于该技术范围时,使用狭窄变更模型的技术可能更可取 :在同一应用程序中重用语句的生成并验证技术[20],[29],[111]可能很难区分合理的和正确的修正,而具有更狭窄的变更模型的技术[27],[36],[45],[50],[110],尽管仅限于设计该技术时可以预见的修改,但它们报告生成正确修复的能力良好。在尝试使用通用变更模型的技术之前,开发人员应该首先体验这些技术。

更大的搜索空间不一定对应于更有效的修复过程 :原则上增加搜索空间会增加可以使用生成和验证技术修复的故障范围,但在实践中可能有害。事实上,增加搜索空间实际上可能会降低正确修复的密度,从而降低整个过程的效率,如[159]所述。

Martinez等人[165],[166]研究了有效导航大型修复空间的问题。他们提取了在几个软件版本中为修复软件而执行的修复操作,并定义了一个概率分布,以确定哪些修复操作在修复缺陷方面更为成功。在他们的研究中,他们观察到可能的正确修复集中在搜索空间的特定区域,利用概率选择修复操作可能有助于将搜索导向这些区域。

更改通常可以由程序中已经存在的代码组合而成 :对几个大型Java项目中的变更的分析从经验上验证了整形手术假设,即应用程序中新代码的内容大部分可以从已经存在的代码中派生出来。这个假设对于一般的基于搜索的技术特别重要,这些技术使用更改操作,从正在修复的程序的不同部分借用代码。结果表明,43%的应用程序新代码(包括修复程序)可以从正在更改的版本中的现有代码重建。此外,他们发现新提交的代码中30%可以在同一个文件中找到。

[167]中给出了类似的分析,其中临时冗余假设得到了验证。临时冗余提交由以前提交中可以找到的代码元素组成。52%的提交结果显示是临时冗余的。

总的来说,这些结果支持这样一种观点,即通用的程序修复解决方案可以通过重用同一程序中已经存在的代码来定义。

从其他程序中获取语句可能有助于解决更多错误 :修改程序源代码(例如GenProg)时在同一应用程序中重用语句的生成和验证技术受以下事实的限制:如果应用程序需要的代码行在当前的源代码中不存在,那么该技术将无法找到正确的修复方案。 Sumi等人[168]研究了从其他程序获取的语句中产生修复的实际可能性,方法是抽象借用语句的结构,并使用出现在缺陷位置附近的变量名,而不是最初在该语句中找到的变量名。在他们的实验中,他们观察到使用此方法生成的合理修复程序数量增加了20%。

为了有效地选择可用于修复缺陷的语句,Yokoyama等人[169]研究了使用存在于与必须修复的区域相似的代码区域中的语句的想法。对多个错误报告的分析表明,使用应用程序中已经存在的与缺陷区域相似的代码区域中的代码,可以修复超过75%的错误,这表明这种启发式方法可能对程序修复过程很有用。

反模式可以通过删除可能产生错误修复的区域来减少搜索空间 :Tan等人[170]确定了几个反模式,这些反模式定义了搜索空间的区域,这些区域经常导致开发人员丢弃修复,并使用这些反模式来裁剪搜索空间。用SPR和GenProg进行的实验表明,反模式对于生成有意义的修复非常有用,这些修复不太可能删除功能,并且更容易手动检查。

 

8.2.3 有效性

实证结果表明,除了测试集和搜索空间之外,还有其他因素影响生成和验证技术的有效性。本节将讨论这些因素。

使用从人工编写的修复中提取的模板可以提高可接受性 :模板通常是由开发人员实施的修复中定义的。由于这些修补程序很可能类似于开发人员更改其代码的方式,因此从这些修补程序获得的模板更有可能产生可能被开发人员立即接受的更改。在[22]中,研究了计算机科学专业的学生和软件开发人员的选择。结果表明,与使用原子更改操作的技术相比,模板可以产生更容易被接受的修复。

基于频谱的故障定位有效地使修复过程产生了偏差 :大多数生成和验证技术都利用基于频谱的故障定位来确定每次迭代时应在何处更改程序(请参阅第5.1节)。与基于程序的要更改的点的无偏选择相比,使用基于频谱的故障定位对要修改的语句进行偏向的选择一直被认为是有用的[20],[22],[27],[ 29]。

Qi等人[171],[172]根据GenProg在生成有效修复之前生成的无效候选解决方案的数量,对14种基于频谱的故障定位技术的有效性进行了经验比较。在这个实验中,与其他定位技术相比,Jaccard系数[90]总是产生更高或同等的效果。

自动修复bug成本很低 :自动软件修复不是免费的,因为修复需要大量的资源来生成。两项研究量化分析了基于AE和GenProg搜索技术的成本[87],[111]。他们在亚马逊的云计算基础设施上进行了实验,通过将租用基础设施的成本除以已修复的缺陷数量,计算出每个缺陷的成本。早期结果表明,使用AE和GenProg修复故障的成本分别为4.40美元和14.78美元。

虽然自动修复缺陷似乎非常便宜,但是这个评估并没有考虑开发人员在检查软件修复技术产生的输出以确认修复情况时所付出的努力。请注意,开发人员可能会放弃一些修复[110],因此执行此检查不是一项简单的活动。研究自动修复过程的总成本仍然是一个开放的问题。

问题的表示形式()对修复过程的有效性有很大影响 :生成和验证技术在个体表示形式,搜索空间的定义以及突变和交叉操作的设计上有所不同。 Le Goues等人[160]经验性地研究了各个方面对修复过程的影响。报告的结果之一表明,在成功率方面,将个体表示为对原始程序的修改列表比AST表示更有效。

平滑适应度函数的梯度可能会改善自动修复过程 :Fast等[163]研究了使用Daikon [16]学习表征程序成功和失败行为的程序不变式的想法。与正在修复的程序相关联的程序不变量与候选解决方案相关联的不变量之间的差异可用于定义适应度函数,从而平滑其梯度:特定的候选解决方案可能仍然无法通过测试用例,但它可以将不变量变为真。通过这种方式,修复过程可以更好地捕获对修复进行识别的过程。在他们的实验中,Fast等人观察到,平滑的适应度函数可以更好地指导搜索过程,但是计算此特定函数的成本超过了整个搜索过程的收益,导致的平均额外开销为3%。

 

8.3 语义驱动方法的证据

与生成和验证技术相比,语义驱动技术的研究较少,影响其有效性的许多因素仍然未知。然而,迄今为止收集到的经验结果足以报告一些有趣的观察结果。

测试套件的大小可能会极大地影响修复程序的生成 :对于生成和验证方法,较大的测试套件会产生更多的数据,这使得修复程序的生成更加困难,因为必须满足许多约束条件才能匹配所收集的数据。例如,如果新子句必须满足通过执行一个大型测试套件收集的数千个约束,而不是十几个约束,那么为if条件合成一个新子句的问题就更难了。直觉上,要生成通过许多测试的修复是比较困难的。但是,需要注意的是,从小测试套件生成的修复可能不准确,并且在彻底验证时产生错误的行为。因此,语义驱动技术使用测试套件也很重要,这些测试套件广泛覆盖了必须修复的语句的程序行为,而不是像SemFix早期所经历的那样过大或冗余。

程序合成技术比表达式枚举技术快 :通过显式地枚举所有可能的表达式(这些表达式可以从一组程序变量和运算符开始)来识别修复程序比使用程序合成技术更容易,但速度也明显较慢。例如,SemFix估计通过枚举生成约束可能比使用程序合成技术慢四倍[21]。

SMT求解是修复过程中的主要成本因素 :测试用例执行是生成和验证技术的主要成本因素,而SMT求解是语义驱动技术的主要成本因素。有报告显示,SMT求解最多可占总修复时间的99%[26]。因此,设计语义驱动的技术来更好地利用解算器是未来的一个重要发展方向。

并发缺陷的修复可能会严重影响正在修复的程序的性能 :并发缺陷检测技术可能不准确,例如,将较大的代码区域标识为错误区域(例如,可能触发原子性违规bug的区域)。因此,修复技术可能会生成由锁定和解锁操作保护的大型临界区域,从而在修复的程序中产生大量开销[82]。这是一个针对缺陷(例如并发缺陷)的修复可能会引入另一个缺陷(例如性能故障)的示例。

 

9. 尚待解决的挑战

自动程序修复技术已经在许多具有一定复杂度的环境中证明了它们的有效性,但是它们的普遍实用性以及在工业环境中利用这一技术的可能性仍然需要证明。在我们的分析中,确定了一些重要的挑战以及研究趋势,表明了该领域未来的着力点可能在哪里。在本节中,我们将这些挑战分为三个小节:修复的正确性挑战、过程挑战和技术挑战。

 

9.1 修复的正确性挑战

合理与正确的解决方案 :大多数技术都可以生成合理的修复,例如通过可用测试套件中所有测试用例的修复。相反,开发人员需要正确的修复,也就是说,能够满足正在修复的程序的所有需求的修复。尽管测试套件的使用无疑是验证自动生成的修复的主要方法,但其他方法也经过了经验性验证,比如使用规范[42]和代码契约[94]。然而,在这些情况下,生成的修复也只是合理的,也就是说,根据已使用的特定验证标准(例如,规范或规则)是正确的,但对开发人员来说不一定是正确的。

为了减轻这个问题,一些方法调查了从开发人员编写的实际修复程序得到启发而来的对修复操作的定义[22],[119],[120],其他方法则认为手动检查修复程序是最有用的验证方法[55]。但是,根据开发人员的判断来确定产生正确且可理解的修复程序的方法仍然是一个普遍的挑战[51]。 尽管生成“保证”满足开发人员期望的修复程序可能是不可行的,但寻找生成并评估开发人员可能接受的修复程序的方法是一个重要的研究方向。

修复非功能性质量问题 :合理的解决方案和正确的解决方案之间的差距由于许多非功能性的特性而加剧,这些质量问题是软件开发人员完全可以接受的。例如,开发人员可能需要既安全又高效的修复,这些方面常常被可用的测试套件不充分覆盖甚至忽略。如何满足非功能性的特性,特别是那些难以用测试套件进行测试的特性,是一个开放性的研究课题。

解决方案的整洁性也很重要。可接受的程序变更通常必须满足在最佳编码实践和命名约定方面定义的结构和语法要求。这些实践可能因组织而异,自动修复技术必须满足具有一定复杂度的定制和复杂程度,以便在生成修复程序时有效地考虑所有这些方面。

对自动生成的修复程序进行交叉验证 :尽管开发人员无需进行任何修改即可直接接受由自动程序修复技术产生的修复程序,但实际上开发人员需要先检查其正确性,然后才能将其集成到程序中。 虽然某些修复程序可能很容易检查,但是许多修复程序可能不是很明显,或者需要一定程度的适应才能最终确定。 交叉检查和了解修复程序所需的工作可能会很耗时,这有可能降低自动技术的好处。

但是,自动程序修复技术除了生成修补程序外,还可能能够生成说明修补程序原理的信息,并在验证修补程序期间为开发人员提供便利和帮助。 生成这些额外的知识是一个开放的挑战。

 

9.2 过程挑战

为正确的情况选择正确的技术 :如何在给定的情况下选择合适的程序修复解决方案是一个重要的开放性问题。我们可以在多个层面做出选择。首先,开发人员可能更倾向于使用全自动修复技术,而不是修复推荐技术,反之亦然。这种选择可能取决于开发人员的偏好,但也取决于修复任务的性质。例如,有些任务可能很难完全自动化,在这些情况下,修复推荐程序可能是一个有效的选择。不幸的是,对于一类方法应该优先于另一类方法,人们仍然不太了解。

另一个变量是选择缺陷特定技术还是常规技术。一个好的策略可能是在适用时倾向于特定于缺陷的技术,而在其他情况下则使用常规技术。不幸的是,可能很难先验地推测出要修复的缺陷是什么,因此,选择正确的特定于缺陷的技术可能非常困难,除非故障已经对缺陷的性质进行了解释,例如无限循环故障。

决策的另一个层次是更改模型的类型和修复缺陷应采用的算法策略的选择。做出这些选择的能力可能会大大促进修复技术的选择。但是,由于在一个缺陷被修复之前它是未知的,因此在实践中做出这些决定非常困难。

Le等人所做的工作是确定应用于修复缺陷的方法的初步结果,他确定了一种方法,该方法决定应该使用标准技术还是使用自动程序修复技术来解决缺陷报告[173]。 。

定义方法和调整软件过程 :定义合适的方法和软件过程以将软件修复技术有效地集成到工业过程中,仍然是一个完全开放的研究领域。需要更多关注的方面是理解如何设计能够有效地自动修复的程序,定义何时应该执行自动程序修复例程,以及它们的输出应该如何集成到组织过程中,质量保证过程的修复责任和角色。

全自动修复过程的挑战 :尽管程序修复技术有时可以自动生成合理的修复程序,但是修复程序的可接受性仍然取决于开发人员的判断。 如果修复过程可以完全自动化(至少在某些情况下),则使用这些技术的好处将显而易见。 一些方法研究了在现场[101],[174],[175]自动生成和部署“快速且脏的”的修补程序的可能性,但是自动生成的修复程序和开发人员编写的质量相当仍然是一项长期挑战。

程序可维护性 :尽快开发人员手工维护程序的方法已是众所周知,但是开发人员和程序自动修复技术共同维护软件的方法仍然不清楚。原则上,内在质量较低的自动修复可能会妨碍代码的可维护性,其积累可能会导致软件可演化性方面的重大问题。在不影响软件项目的可维护性的情况下,设计修复程序的技术很大程度上仍然是一个尚未被探索的领域。

 

9.3 技术挑战

分散的发现集 :有许多软件修复技术和许多可用的研究,但是这些研究的成果仍然是分散的,很难合成一个清晰的画面。这通常是由于该领域的每项工作引入了创新(例如,提出了一种合成程序修复的新策略)和优化(例如,对测试用例进行优先级排序以进行验证)的混合。由于这些因素的重叠效应,有时很难确定哪一个因素对结果的影响更大。是策略吗?是一个特定的启发吗?还是实现的质量?[176]也讨论了在比较和研究同一领域的解决方案时,所有这些因素可能影响结果的方式。

提高该领域的成熟度,并更好地理解重要和有用的策略和启发式方法是很重要的,这些策略和启发法应该出现在每种自动程序修复技术中,例如增加对等价性检查影响的独立研究的数量[111],候选解决方案的优化表示[27]、[102]和测试优先级方案[29]、[106]。

用于程序修复的测试套件 :有明确的证据表明,测试套件可能对生成并验证技术(见第8.2.1节)和语义驱动技术(见第8.3节)的有效性产生巨大影响。 但是,只有初步证据表明一个好的程序修复测试套件所具有的特征,例如预期结果[110]的强度及其完整性[36],[159]。 这导致缺乏有关测试套件构建的指导原则,这些指导原则可以最佳地支持自动修复过程。 定义创建这些测试套件的正确方法可能会对程序修复解决方案的有效性产生重大影响。

程序修复的设计 :有证据表明程序的复杂性和结构(例如,可能增加通用程序修复技术[100],[167]的有效性的语句的存在)以及注释的存在( 例如,函数前置条件和后置条件[92]的存在对成功和可应用的程序修复技术的类型有影响。 这就提出了一个更普遍的问题,即我们是否可以构建便于程序修复的软件,例如使用特定的编码样式,或者避免/优先选择某些编程结构,或者使用特定的库。 由于我们不能期望程序修复技术能够解决所有可能的情况,因此我们应该考虑构建方便于自动修复的软件。

替代验证机制 :几乎所有的生成和验证技术都将测试用作验证机制。但是,测试用例在用于验证候选解决方案时表现出一些局限性。特别是,功能弱的测试套件可能会倾向于删除一些功能的修复,而功能强大的测试套件可能会使生成正确的修复程序变得异常困难[110]。虽然了解如何为程序修复设计好的测试套件绝对是一个选择,但测试套件并不是验证的唯一选择。例如,FixMeUp[42]使用访问控制规则作为验证机制。更一般地说,自动程序修复应该研究替代的验证机制,例如,基于定义需求或软件设计的相同工件的重用,这些机制可以是基于测试套件验证技术的补充,从而获得更好的修复。

生成更好的搜索空间 :迄今为止定义的搜索空间已被证明没有足够的正确程序来设计高效和通用的程序修复解决方案[159]。因此,有必要产生更好的搜索空间和更高密度的正确修复,以被程序修复技术有效覆盖。为此,重要的是尽可能多地利用信息源,包括来自其他应用程序和系统的修复、用户提示、规范和注释。到目前为止,只有现有的修复被基于实例的技术所利用(见第6.1.3节)。

基准 :为促进这一领域,重要的是要有研究人员可以用来评估和比较技术的大量基准。自动程序修复技术已经广泛地利用了一些可用的基准,例如在ManyBugs基准[154]和SIR存储库[156]中定义的应用程序和缺陷。虽然这些基准肯定有用,但重要的是更好地利用现有资源,并确定额外的基准,丰富作为实验对象的一组缺陷和案例,以防止收集因使用有限的基准应用程序和缺陷而产生有偏差的经验证据的风险,并提高提供超出分析的特定案例的结论的能力。

更好地使用测试套件和解算器 :有报告显示,在生成和验证技术[41]中运行测试套件和在语义驱动技术中运行解算器[26]是主要的成本因素。由于设计更有效的解决方案可以对搜索空间进行更彻底的探索和更有效的合成,因此降低这些成本非常重要。一些技术已经部分地解决了这个问题,例如,对测试用例进行优先级排序以降低测试执行的成本[29],[106]。但是,有必要设计一种修复技术,可以更智能,更好地利用这些高成本的技术,以将程序修复解决方案提高几个数量级。

 

10. 总结

自动程序修复技术的目的是为了解决自动修复有缺陷的软件的艰巨挑战。在过去十年中,程序修复技术产生了相关且有影响力的结果,证明了从长远来看其可能对测试、验证和调试实践产生重大影响。

本文整理了该领域的知识,调查了现有工作,并讨论了主要成果和挑战。特别地,本文指出程序修复方法是根据两种主要策略来解决软件修复问题的。生成和验证方法会生成一个包含可能修复的搜索空间,然后在该搜索空间中查找正确的修复。语义驱动的方法产生修复程序问题的表示,并求解该问题以获得实际的修复。

我们将从多个不同研究中收集到的经验证据整合并总结成一组关键事实,这些事实显示了影响这些方法有效性的因素和它们之间的权衡。最后,讨论了我们所认为的最重要的挑战,这些挑战可能会影响到该领域未来的研究。

 

(完)

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

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

译者:ClarkC.

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

猜你喜欢

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