Automatic Software Repair: a Bibliography 自动软件修复概览(二)

原论文标题《Automatic Software Repair: a Bibliography》

原作者 Martin Monperrus,主页:https://www.monperrus.net/martin/

发表于 ACM Computing Surveys, 2017

原文链接:https://arxiv.org/pdf/1807.00515.pdf

此翻译已获得原作者授权

译者:ClarkC.

引用请注明出处

 

感谢原文作者Martin Monperrus教授提供翻译授权。鉴于原文较长,该翻译将分为四次上传。第一次为原文开头至第二节结尾,第二次为原文第三节,第三次为原文第四节,最后一次为原文第五、六、七节。原文中的参考文献可至原文中查看,原文链接附在开头与结尾。

 

3. 行为修复

行为修复是指在修复中更改程序的行为,即更改其代码。这种更改既可以在源代码上进行,也可以在二进制代码上进行(例如Java字节码或x86本机代码)。行为修复可以脱机或在程序运行时联机进行。

      脱机时,行为修复可能会发生在维护开发人员的开发环境(IDE)或持续集成服务器中。在线行为修复是指对已部署软件进行的修复。技术层面上,运行时的行为修复涉及一种动态软件更新(DSU),这本身就是一个研究主题。

     行为修复涉及修复操作(repair operator5),这是对程序代码的一种修改。 例如,增加前提条件,如下所示。

+ if (age >= 18)

        serve_adult_content ()

参考文献中定义了多种不同的修复操作,将在下面介绍并在表II中进行总结。有时,修复操作会涉及修复模板(repair template6)(以修复特定的错误类为目标的参数化的代码片段)。一组维修操作组成修复模型[114]。

例如,当使用规范3.1.1的测试套件时,行为修复程序的问题陈述即为:提供一个程序及其测试套件,测试套件中至少包含一个失败的测试用例,并创建一个能使整个测试套件通过的补丁。这种问题陈述可以称为基于测试套件的修复[124],Genprog对此进行了著名的研究,详见3.1.1节。

5或是“repair action”

6或是“repair strategy” 或“fix schema” [185]

 

3.1. 修复与预期结果

      如第2节所述,自动修复是针对预期结果的。 因此,本节将对参考文献中所提及的各种预期结果类型进行介绍。

 

       3.1.1 测试套件 测试套件是基于输入输出的规范。在现代面向对象的软件中,输入的复杂性与一组由大量方法调用构成的相互关联的对象相同,输出也可以是一系列以各种方式观察执行状态和行为的方法调用。在基于测试套件的修复中,失败测试用例充当bug检测预期结果,其余通过的测试用例充则充当回归预期结果。

       Genprog是弗吉尼亚大学开发的基于原型测试套件的开创性的修复系统[186,188,45]。Genprog使用三个由抽象语法树(AST)变体的修复操作:删除、增加AST节点以及替换现有节点。为了增加和替换,从代码库中的其他位置获取节点。从代码库中其他位置获取节点以实现增加和替换操作,称之为冗余假设[115,12]。Genprog能够处理现实世界中大规模的C代码。Genprog的最大规模的一次评估结果声称其可以修复105个错误中的55个[90]。但这些结果后来受到质疑,第5节中将对此进行讨论。Genprog原始团队[151,93]和其他实验室[137,139]还基于Genprog的思想发表了其他论文。现在,Genprog的核心思想已被广泛接受,但还需要改善其核心修复操作(例如[132])。

       远在Genprog出现之前的90年代中期,Stumptner和Wotawa [170] 用一种称为EXP的简单玩具语言(toy language)提出了自动修复的概念。其规范是一组测试用例(即测试套件)。据我所知,这是首次出现基于测试套件的修复。

       Arcuri [7,5,6]基于抽象语法树的修改定义了7个修复操作。例如,对于“促进突变”,节点被其子节点之一替换。运算符以随机方式堆叠。名为Jaff的原型实现,处理Java的一个子集,并使用玩具程序(toy program)进行评估。

       Debroy和Wong [33,130]提出使用突变测试文献中的标准突变来修复程序。因此,他们的修复模型包含:使用同一类中的另一个运算符替换算术、关系、逻辑、增/减或赋值运算符;if或while语句中的决策否定(decision negation)。通常,他们使用基于频谱的故障定位技术来定位故障语句。Nica等人[130]也使用突变进行修复。与Debroy和Wong相比,他们探索了所有突变。

       Kern and Esparza [78]的关键思想是生成一个根据突变操作整合所有可能突变的元程序。突变实际是由元变量执行和驱动的。修复是这些元变量的一组值。使用符号执行对元变量进行赋值。

       NGuyen等人[128]提出了一种称为Semfix的方法,用于基于符号执行和代码合成的修复。通过天使式调试(angelic debugging)[25]找到要修复的位置,然后使用输入-输出分量合成[68]来合成修复表达式。修复的位置是在赋值和布尔条件的右侧(RHS),综合表达式中混合了算术和一阶逻辑。Semfix存在一个可伸缩性的问题,其开发组提出了Angelix [116] 来克服这个问题。Angelix是一个类似于Semfix(译者注:原文为Sefix)的修复系统,其中的符号执行部分针对大型程序以及获得多个天使值(angelic value)进行了优化,这就是“天使之林”(angelic forest)。

       PAR系统[79]是一种自动修复Java代码bug的方法。PAR是基于修复模板的,其十个修复模板中的每一个都包含修复一种常见bug的通用方法。例如,对空指针的访问是一种常见的bug,对这种bug的常用修复方法是在不希望出错的访问之前添加空指针检查,这就是 “空指针检查器(Null Pointer Checker)” 模板。有些模板是通过变量进行参数化的,例如“空指针检查器”模板中就将变量名作为参数。以随机搜索的方式来应用和测试模板。

       Nopol [34]针对特定的错误类:条件错误。它通过修改现有的if条件或向代码中的任何语句或块添加前置条件(也称为保护)来修复程序。修改或插入条件是由使用基于输入输出的代码与SMT合成[68]和谓词切换[194]合成的。Nopol系统已扩展到可以修复无限循环类型的错误[87]。

       Tan和Roychoudhury提出了Relifix,一种专门用于修复回归bug[175] 的修复系统。其包括8个修复模板,其中一些是运算符转换,其他是参数化修复模板。Relifix的关键思想是模板应用程序由历史变更驱动,例如, “添加语句(add statement)” 模板仅添加与回归相关的之前的代码提交中涉及的语句。

       Mechtaev等人还实现了基于测试套件的修复[117],其最终目标是合成简单补丁。为了做到这一点,假设有一种非常特殊的程序:可以解释为跟踪公式的程序(与[58]中的布尔程序有关)。在这种假设下,他们可以将修复问题声明为最大满意度(MaxSAT)问题,其中最小的补丁就是刚好满足最大约束条件的补丁。

       SPR [109]定义了一组阶段性的修复操作,以便及早丢弃许多无法通过测试套件的候选项。这样就可以详尽地检索更小且价值更高的范围。

     CodePhage [156]的想法是将检查操作从­­­一个应用程序转移到另一个应用程序,以避免崩溃。该系统假定一个触发错误(error-triggering)的输入使一个应用程序崩溃,而另一个不会。其所考虑的错误(error)是越界访问,整数溢出和除数为0。缺失检查是根据输入字段上的符号表达式来推断的,并由回归测试套件进行验证。

       Ke和他的同事提出了SearchRepair [77],这是一个源于代码搜索的系统。SearchRepair首先将代码片段作为SMT约束进行索引,然后在修复时,通过将所需的输入输出对和单个约束问题中的片段相结合来检索片段。该系统是用学生在在线课程中编写的小型C程序进行评估的。

       Prophet [108]是一个使用过去的代码提交来驱动的修复系统。从版本控制系统中的过去的代码提交中,能获取到补丁的一组功能上的概率分布。使用这种概率分布既能加快修复速度,又可以增加找到正确补丁的可能性。使用Genprog基准测试中的69个实际缺陷进行了评估,结果找到了15个正确的修复。Le等人[94]也使用历史记录来选择最可能合适的补丁。与Prophet相反,他们是在Java程序上进行的实验。

 

       3.1.2 前置与后置条件 一些研究工作使用规则设计模式(design-by-contract [123])的经典前置和后置条件作为修复的预期结果。

       He和Gupta [61]使用前置和后置条件来计算“假定的程序状态”(根据后置条件)和“实际程序状态”(根据错误输入)。为了使假设的程序状态与实际程序状态相吻合,修复操作包括:更改赋值的LHS或RHS,或是通过简单修改(更改变量,更改关系运算符)来更改布尔条件。经典测试套件用于检测回归。

       AutoFix-E是Wei等人提出的方法[185,193],它依靠联系(前置条件,后置条件,不变式)为Eiffel程序生成修复补丁。AutoFix-E使用四个修复模板,其中包含要合成的代码段和空条件表达式。AutoFix-E的主要理念是代码片段和条件表达式都取自现有规则。

       Gopinath等人[57]使用以Alloy规范语言编写的前置和后置条件。该功能体也可以转换为Alloy公式。然后,Alloy的有限验证机制既可用于bug检测(类似于[65]),也可用于识别修复情况。修复操作会更改赋值的RHS并修改现有的if条件。

       Könighofer和Bloem [83]将断言(assertions)视为程序中可转换为SMT的规范。该方法是静态的,并且修复不会违反所考虑的输入域的断言。该方法是基于修复模板的,例如改变赋值的RHS或通过线性组合改变算术表达式。模板的空缺由SMT求解器(SMT solver)填充。

      

       3.1.3 抽象行为模型 抽象的行为模型,比如编码对象状态的状态机和相应的允许方法调用,可以用来驱动修复。

       2006年,在Genprog出现之前,Weimer [187]提出了第一个补丁生成技术。它要求输入安全策略(即typestate属性或API使用规则)和方法的控制流程图。整个方法是静态的:将bug检测为对安全性的静态违反,并且仅将通过安全性检测作为补丁的正确条件。有趣的是,其中没有提到“修复”这一术语,那时还没有这种叫法。

       Dallmeier等人[31]提出了一种修复Java程序的方法Pachika。Pachika的思想是,首先从执行中推断出对象使用模型,然后为失败的运行生成修复程序,以匹配推断出的预期正确行为。其评估包括修复ASPECTJ(75KLOC)的18个bug和RHINO(38KLOC)的8个bug。Pachika的两个修复操作是方法调用的添加和删除。Pachika与之前方法的主要区别在于行为模型是推断得到的,而不是给出的。

 

3.2. 静态分析

       静态分析工具输出错误(error)和警告(warning)。它们可以被自动修复。在这种情况下,正确性预期结果就是静态分析本身。

       Logozzo和Ball[105]在.Net代码的静态分析工具链之上提出了一种修复方法。对于一组静态识别的故障类(fault class)(例如,大小差一错误),他们提出了相应的修复操作。修复操作是针对每个故障类的,例如添加前置条件、更改数组分配的大小等。随后再次运行静态分析以验证修复的正确性。

       Logozzo和Martel[106]以整数算法(线性组合)的特定故障类为目标。算术溢出是静态检测到的,建议的解决方法是对算术运算进行重新排序。该修复程序可确保不再发生溢出。还有Cocker等人[27]也在算术溢出方面有研究。

       Gao等[49]提出了一种自动修复C程序内存泄漏的方法。该方法包括通过插入存储单元分配语句来静态检测和修复内存泄漏。该方法用14个程序进行了评估,其中考虑了242次内存分配。

       Gupta等人[59]设计了一种修复程序错误的端到端解决方案(译者注:原文说是修复编译器错误,不准确)。DeepFix的独创性在于其使用基于深度学习的语言模型来提出修复建议。该方法通过修复在线课程中的学生程序来评估。

       Muntean等人[126]静态检测缓冲区溢出,然后用变量参数化模板。模板中使用的正确变量是使用SMT找到的。

 

3.3. 崩溃输入(crashing inputs)

       行为修复可以作为对现场故障的响应(例如,崩溃异常或由缓冲区溢出引起的段错误)。一旦崩溃输入被确定且可能减至最少,就会进行修复过程。测试套件中的失败测试用例也可以看作是崩溃输入。但是,从基于预期结果修复的角度来看,崩溃输入和测试套件的主要区别如下:测试套件包含能够通过的测试用例(回归预期结果),而且失败的测试用例还包含对预期值的断言;而崩溃输入,顾名思义,只是指违反了“程序不应崩溃”的非功能约束。

       Gao等人[50]基于Stackoverflow修复崩溃异常。他们的系统名为QACrashFix,在Stackoverflow上挖掘成对的bug及其修复代码,以便提取编辑脚本。为了抑制崩溃异常,将依次尝试编辑脚本。Azim等人[10]检测Android智能手机应用程序中的现场故障(field failure)。其所考虑的故障(fault)是未处理的异常,修复操作包括使用二进制重写添加try / catch块。Clotho [39]是一个生成简单的catch块的系统,用于处理与Java中的字符串操作相关的某些运行时异常。catch块的内容基于静态和动态收集的约束。

       Sidoroglou和Keromytis [158]在产品运行时检测缓冲区溢出漏洞,然后使用ProPolice [44]获取漏洞源;最后,使用以转换语言TXL编写的代码转换规则来修改源代码。用手动提供的测试套件进行回归测试。

       Lin等人[100]试图为C代码中触发数组溢出的漏洞生成源代码补丁。其修复操作包括:通过在读取表达式中添加模型来修复越界读取错误;通过截断要写入的数据来修复越界写入错误(类似于对故障忽略计算(failure-oblivious computing))。

       Wang等人[182]的目标是自动修复整数溢出。其中包含三个修复操作:第一个是在溢出发生之前强制执行错误分支(error branch);第二个是在溢出发生之后强制执行错误分支;最后一个是停止(退出)程序。生成修复的条件是从动态符号执行中获得的路径条件。

 

3.4. 其他预期结果

      在自动修复设置中使用了其他特定的预期结果。

       关于修复并发bug有许多技术被提出。Jin等人[70]提出了AFix,其修复模型包括将指令放入关键区域。关于并发bug自动修复的工作已得到进一步扩展[103]。Lin等人[99]也通过将问题编码为可满足性问题来插入锁。在Dfixer [17]中,没有引入新的锁来修复并发bug,而是在一个线程中预先获取了现有的锁。最近,Liu等人[102]在一个名为HFix的工具中提出了另一个并发bug的修复操作:自动添加线程连接操作。

       Samimi等人[150]提出了一种在PHP中修复生成HTML标签的web应用程序的方法。其使用的预期结果是输出的HTML字符串是否格式错误,即不包含前后不一致的打开和关闭标签(例如“<a></i></a>”)。它们将修复编码为字符串上的约束问题。Wang等人[183]也在研究修复PHP代码输出的HTML代码,但使用运行时跟踪而不是约束求解。Medeiros等人[118]也研究修复web应用程序,但考虑了SQL注入,而且他们的修复操作由一个清理函数包装特定的调用组成。

       Liu等人[101]将手工填写的bug报告作为预期结果。其具有参数化的修复模板,并从bug报告中提取模板参数的实际值。例如,对于非空检查器(not-null checker)模板,它们从bug报告中提取要检查的变量的名称。

       Dennis等人[38]使用Isabel作为预期结果对ML程序使用基于证明的程序验证。当证明失败时,证明的反例将基于修复模板(将一个方法调用替换为另一个,添加一些代码)驱动修复方法。

       可以使用参考实现(reference implementation)作为修复规范。在这种情况下,参考实现既充当错误预期结果(当参考实现和错误程序的行为不对应时)又充当回归预期结果。在修复方面对此的探索还很少。Könighofer和Bloem [84]的方法使用基于SMT的模板。Singh等人[163]的方法在概念上相似,但实现方式不同,并且评估范围更大。参考实现和需要修复的程序都是用Python编写的。系统将它们转换到一个名为Sketch的编程环境,其负责探索候选修复的空间。评估是对上千在线课程中提交的错误程序进行的。Qlose[30]是一种基于Sketch的类似方法,其新颖之处在于它试图通过将行为变化的输入的数量减至最小来影响修复的语义。

       Jiang等人[69]提出使用变质关系(metamorphic relations)作为修复预期结果。他们根据学生程序制定的Introclass基准来评估他们的方法。由于实验对象的规模有限,因此尚未证明变质关系可以帮助修复大型的现实中的程序。Kneuss等人[80]使用一种符号测试来修复一种纯功能性的玩具语言。作为变质关系,符号测试可以生成新的测试数据。

 

3.5. 特定于域的修复

      自动修复的概念可以应用于许多计算制品。确实,有许多工作在特定于应用程序域的上下文中进行自动修复。

       Lazaar等人[89]修复了约束程序。使用特定于域的故障定位策略,修复包括删除或添加新约束。Gopinath等人[56]用一种称为Abap的面向数据的特定语言修复数据库选择语句。Kalyanpur等人[75]在OWL本体论的背景下提出了自动修复问题。Griesmayer等人[58]修复了一种称为布尔程序的特定类型的程序(仅包含布尔变量的程序),还在修复布尔程序方面做了进一步的工作[149]。Son等人[167]使用针对该领域的静态分析和转换,来修复Web应用程序中的访问控制策略。

       Nentwich等人[127] 对XML文档检测不一致性,并提出修复措施。他们的方法适用于具有明确的静态不一致规则的所有结构化文档。同样,Xiong等人[191]检测并修复了MOF和UML模型中的不一致;da Silva[162]使用Prolog提出了修复UML模型中不一致的修复计划;Xiong等人[192]专注于自动修复软件产品线中的配置错误。

       Tran等人[179]在强制源代码依赖项和指定可接受的依赖项的依赖项模型之间进行匹配的意义上进行修复。称之为“架构修复(architectural repair)”

       Daniel等人[32]的方法不是修复程序,而是修复在重构过程中损坏的测试用例。Memon[119]和Gao等人[51]修复GUI测试脚本。例如,这些方法更改用于驱动GUI操作的标识符。Leotta等人[95]在Selenium测试(针对具有HTML输出的Web应用程序的测试)的背景下进行修复测试。

 

3.6. 故障类与修复

      一些故障类已被充分理解,因此人们可以编写代码转换以一次修复特定故障类的所有实例。例如,可以将64位整数转换为无限精度的算术对象(例如Java中的BigInteger),以避免所有算术溢出。在相关的工作中,大多数故障类的修复转换是语义保持的,但不是必须。

       例如,关于语义修改转换的开创性工作是对故障的忽略计算(failure-oblivious computing)[143]。考虑读数组越界的错误,故障的忽略计算会转换代码,以返回第一个非空元素,或者该元素对分配的数组的长度取模。同样,Rinard等人[147]提出将越界写入存储在哈希表(hashtable)中,并且对之后越界索引的读取将返回先前存储在哈希表中的对象。这一系列的研究是基于可接受的结果比正确的结果更重要这一哲学理念,这就是所谓的“可接受性计算(acceptability-oriented computing)”[145]。

       Thomas和Williams [177]提出了一种自动将PHP代码转换为安全SQL语句的方法。转换会修改抽象语法树,以注入安全的“准备好的语句”。

       Google开发并使用了一个称为“易出错(error-prone)”的工具[2],可以自动修复类似Findbugs的错误[63]。Lawall等人[88]还定义了一种在Coccinelle工具中声明性地指定错误模式和相应补丁的方法。Kalval和Warburton[74]也提出了同样的想法,其修复策略是使用一种称为Trans的形式化转换语言编写的。

       Shaw等人[153]描述了两种解决C缓冲区溢出的转换:用可替代的安全库替换不安全的调用,以及用更安全的类型替换不安全的类型。他们表示转换可以扩展到大型程序,不会破坏现有测试,也不会减慢程序速度。Coker和Hafiz对另一种故障类(整数算术bug[27])采用了类似的方法。他们提出了三种针对整数的程序转换,并表明该方法可扩展至实际程序。

       Long等人[110]使用针对整数算术的静态分析来检测整数溢出。对于所有检测到的潜在溢出,系统会推断出一个只是简单地丢弃输入的过滤器。在此意义上,修复措施是拒绝输入,这也是一种在运行时实施的技术,将在4.5节中进行讨论。

       Cornu等人[28]针对Java中未处理的异常。他们分析测试套件的执行情况,以确定具有还原能力的良好捕获块。然后,它们将捕获的异常类型转换为更通用的异常类型(即超类异常(superclass exception)),以便捕获那些用其他方法无法捕获的异常。称作“捕获扩展(catch stretching)”的代码转换,是一种针对意外异常(unexpected exceptions)的主动修复。

(未完待续)

其余部分:(一):https://blog.csdn.net/ClarkCC/article/details/106029615

                  (三):https://blog.csdn.net/ClarkCC/article/details/106178275

                  (四):https://blog.csdn.net/ClarkCC/article/details/106178322

若发现翻译问题,可直接评论或与我联系:cheng.yifan@qq.com

原论文标题《Automatic Software Repair: a Bibliography》

原作者 Martin Monperrus,主页https://www.monperrus.net/martin/

发表于 ACM Computing Surveys, 2017

原文链接:https://arxiv.org/pdf/1807.00515.pdf

此翻译已获得原作者授权

译者ClarkC. 

引用请注明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值