关注 | 填洞规则是,有类型则只找这个,没有类型就随机生成;比基于SAT的方法效果好 |
评分/5 | ⭐⭐⭐⭐ |
总结 | 对于sketch上的hole,会生成一个list,然后补全所有的hole之后,利用单元测试的方法来验证是否满足意图。如果不满足,回溯到上一个hole然后访问list的其他代码片段。然后还有一些基于java语法的剪枝策略。 |
亮点 | hole包括了if和while的condition, 表达式语句,赋值语句。语句生成根据返回类型从list中选择,如果不是赋值,就随机选;支持java的高级语法,一些reflection, 本地调用等 |
缺憾 | sketch的hole需要由用户指定一些详细信息才行;sketch也是用户提供的,不是自动得到的 |
Hua, Jinru; Khurshid, Sarfraz (2017): EdSketch: execution-driven sketching for Java. In Hakan Erdogmus (Ed.): Proceedings of the 24th ACM SIGSOFT International SPIN Symposium on Model Checking of Software. ISSTA '17: International Symposium on Software Testing and Analysis. Santa Barbara CA USA, 13 07 2017 14 07 2017. New York, NY: ACM, pp. 162–171.
下面是论文内容节选。
程序草图[26]是一种程序综合的方法[1, 7, 19, 29],允许用户编写有 "漏洞 "的部分程序,并让综合工具填补这些漏洞,使完成的程序满足给定的测试线束或参考实现。现有的草图方法[15, 26]将部分程序翻译成命题可满足性公式,并利用反例引导的归纳综合(CEGIS),在现成的求解器基础上生成具有期望功能的程序。 虽然这些基于翻译的方法在一系列的程序上显示了它们的有效性,但它们有一个关键的限制:当应用于具有复杂库的实际应用时,这些 基于翻译的方法需要翻译所有直接或间接被给定草图调用的库,或者创建这些库的模型,这可能导致不实际的SAT问题。
本文介绍了EdSketch,这是一种新颖的方法,使用软件模型检查器中常用的反追踪深度优先搜索,执行执行驱动的草图来合成Java程序。我们工作的关键创新之处在于引入了有效的剪枝策略,以减少可能解决方案的搜索空间,并有效地探索存在库的实际程序行为。EdSketch提供了一个实用的解决方案,用于勾画现实世界中具有复杂结构(如反射或本地调用)的小部分应用程序。
作为输入,EdSketch需要一个带有使用Java语法编写的漏洞的草图(部分程序),以及一个描述正确性规范的测试套件。EdSketch基本上支持三种类型的漏洞:布尔条件(例如,对于一个while循环),表达式(例如,字段解读),以及赋值语句块。 EdSketch利用给定的草图在程序中引入非决定性,这使得回溯搜索可以探索候选程序的空间。EdSketch针对插桩的程序执行测试套件,并在遇到失败(运行时失败或测试断言失败)时进行回溯搜索。 当候选程序的空间被耗尽或找到满足所有测试的完整程序时,EdSketch就会终止。
为了初始化搜索,EdSketch根据用户给出的目标类型为表达孔生成候选表达式。候选表达式是根据用户提供的可见变量或变量来计算的。例如,使用最多两个字段的转引,从代表单链表中一个条目的变量e派生出的Entry类型的表达式应该是{e, e.next, e.next.next},其中字段next代表链表的下一个条目。也就是所谓的根据表达式返回类型生成表达式!
EdSketch引入了两个关键的修剪策略来优化草图的完成。 这些策略对赋值语句块和条件表达式的冗余候选者进行修剪。
对
本文有以下贡献:
用户可以指定在while块中勾画的语句数量;默认情况下,EdSketch每次递增一个赋值,直到找到第一个解决方案或达到预设的赋值上限。EdSketch先生成代码,然后如果不通过,就不断回溯搜。
我们将在本节中描述执行驱动的草图。如图2所示,EdSketch首先基于程序草图P和字段解引用b的默认边界(章节3.2)在表达式级别构造所有候选对象。然后EdSketch对程序进行检测,使其能够动态地为“孔”选择候选者(章节3.3)。第3.4节描述了我们在通过测试执行(T)评估候选对象时应用于草图分配和条件的修剪策略。第3.5节讨论了我们用于有状态原型(EdSketch-JPF)和无状态原型(EdSketch-JVM)的回溯引擎。按照hole来生成其中的每个表达式
为了合成不确定空洞,EdSketch需要为表达式空洞指定表达式类型,或者为条件语句块和赋值语句块指定左右两边表达式类型。
用户需要提供的:
例子:
Entry.hole_type(?不确定可能是那类型, index, variable_name, variable_reference)
EdSketch.COND(Entry.class, 0, new String[]{ “head", “ln1", “ln2", “ln3"}), new Object[]{head, ln1, ln2, ln3});
EdSketch. BLOCK(Entry.class, 0, new String[]{“head", “ln1", “ln2", “ln3"}), new Object[]{head, ln1, ln2, ln3}).
Entry ln2 = (Entry) EdSketch.EXP(Entry.class, 0, new String[]{“head", “ln1", “ln2", “ln3"}), new Object[]{head, ln1, ln2, ln3}).
所以他是先随机生成,然后按照表达式的类型,来筛选那些满足类型的生成片段。
每个洞都会有个list,如果没有通过测试用例,在回溯时会从这里list里选择list的其他元素。
为了在程序中引入不确定性,并允许回溯搜索以探索候选程序的空间,我们基于候选表达式对程序进行插装。接下来,我们将描述如何对用户给出的原始草图进行插装,并将其转换为可执行程序,以通过测试套件进行验证。图3展示了激励示例的插装程序,并使用“+”信号突出显示了新插装的代码。
==循环会限制最长迭代次数,这个参数用户可以自行配置。对于接下来的赋值表达式生成,先用for循环遍历list中的表达式作为等式的右侧,==然后根据“hole”的候选标识符选择左边的表达式,左右联合写等号就构成了赋值表达式。 只会为可以赋值的变量和字段访问生成case语句,但不会为这个对象生成case语句,如array等不可修改的字段。长度,以及默认候选值,如null、−1或0。
基于Java语法和程序同构分析,定义了4条剪枝规则。
源表达式 | 剪枝后 |
---|---|
e1 = e1 | 省略 |
v1 = v2;v2 = v1 | v1=v2 |
v1 = v2;v1 = v3 | v1 = v3 |
v3 = v1;v2 = v1, while id(v3) > id(v2). | " v2 = v1;v3 = v1 “和” v3 = v1;v2 = v1 "视为同构解 |
Condition Candidate Generation.。在第一次访问条件草图时,EdSketch通过将表达式候选项与条件操作符相结合来生成所有条件候选项。我们定义了两个条件运算符{==,!=}用于非基本类型,6个用于基本类型的条件运算符{==, ! =, >, <, >=,<=}.。基本类型操作符也适用于相应的包装类,如Integer。
我们只需要考虑组合e1 op e2其中id(e1)
Condition Value Grouping.根据求值结果,生成的候选条件被进一步划分为两个集合,如算法2中的第9 ~ 13行所示。如果它不是第一次访问,EdSketch将重新评估集合中的每个候选条件,并根据新执行中每个条件候选条件的评估值划分候选集。如果EdSketch在上一次执行中选择了布尔值true,那么trueSet会在这次执行中被分割。例如,激励例子中的条件候选ln1 != null在一次迭代中可能为true,而在下一次迭代中它的值可能变为false。因此,条件候选ln1 != null在第一次迭代时被放入trueSet,在下一次迭代时被移动到faleset。
EdSketch在每次调用结束时根据两个候选集的大小选择一个布尔值,如算法2中的第26 ~ 31行所示。如果没有被评估为true的候选项,EdSketch将在算法2的第27行选择false,表示为1。如果被评估为false的候选集为空,EdSketch将选择true表示为0。getCondition方法返回选中的布尔值以及两个候选集。如果选择的值不满足测试断言,EdSketch将回溯到之前的选择,并选择一个不同的(不确定的)值。
如算法3所示,EdSketch执行所有测试用例,每当遇到运行时异常或测试失败时,它都会回溯并获取下一个选择,直到它已经探索了整个搜索空间或找到一个满足正确性标准的解决方案。
我们基于两个不同的回溯引擎构建了两个原型:一个基于Java PathFinder[31]的有状态原型和一个基于重新执行的无状态原型。
Java PathFinder(JPF)[31]是一个成熟的模型检查器,实现了一个定制的JVM。**JPF是一个通用的有状态模型检查器,它提供了现代软件模型检查器所支持的所有常见操作,包括对多线程程序的检查。它提供了一个默认的深度优先搜索,可以有效地存储和恢复程序状态。使用JPF作为后端来实现回溯搜索是很直接的。虽然JPF可以处理Java字节码,但它只能处理有限的本地调用,**这就限制了它在现实世界应用中的合成适用性。
我们的第二个原型是基于一个专门的无状态搜索[10],使用重新执行[5]。由于这个原型在标准的JVM上执行,它**允许在开源项目的背景下进行合成,具有高级功能,如反射、I/O和本地调用。**然而,实现一个用于回溯的重新执行引擎需要精心设计,**以撤销某些状态的更新,例如对静态变量的更新,或者恢复控制搜索和剪枝的变量的值。**我们目前的实现使用启发式方法来提高效率,可能会跳过一些正确的候选者,但只输出通过所有测试的解决方案。注意,我们的有状态原型不需要对回溯进行任何特殊处理,因为JPF已经支持它了。
我们在一个小型但复杂的数据结构基准上对EdSketch进行了评估,并说明了EdSketch绘制具有高级功能的真实世界Java代码的能力。
我们在评估中解决了以下研究问题。
与基于SAT的合成器相比,EdSketch在勾画小而复杂的主题方面的效果如何?
修剪策略是如何影响草图的搜索空间的?
EdSketch能否勾勒出具有高级语言特征的真实世界的Java程序?
为了研究EdSketch在勾画小而复杂的数据结构方面的功效,我们从java.util的源代码和算法书中选择了10个主题[4]。如表1所示,这些题目是:。二进制搜索树插入(BSTAS和BSTCD)、寻找中值(MEDAS和MEDCD)、红黑树插入(RBTAS和RBTCD)、单链表反转(LLREV)、双链表先加(DL-LAF)、双链表后加(DLLAL)和红黑树移除(RBTRM)。
图4表示EdSketch-JVM、EdSketch-JPF和Sketch synthesizer绘制不同数量任务的绘制性能时间。这是每个任务都单独画了一张图。
图5展示了三种工具在勾画条件方面的表现,包括if条件和while条件。
Angelic Programming.。与我们的方法类似,天使编程[2]利用了不确定性回溯算法[8]。Barman等人[2]将angelic choice构造嵌入到Scala编程语言中,并构建了一个并行回溯求解器来探索他们的回溯求解器的可扩展性。由于没有任何剪枝策略,该方法比Sketch合成器的扩展速度快得多。实验表明,所提出的剪枝策略是有效的,EdSketch在该数据集上的性能与基于sat的合成器相当,甚至更快。
本文提出了一种新的执行驱动的草图方法,它使用回溯搜索来合成Java程序。我们的关键见解是引入有效的剪枝策略,以减少解决方案的搜索空间,并通过执行给定的测试套件探索实际的程序行为。EdSketch可以合成现实世界中的Java代码,这些代码可能使用命令式语言的复杂结构,如反射和本地调用。我们的实验表明,在我们的数据集上,我们的方法与基于SAT的合成器相当,甚至更快,并且可以应用于具有高级语言特征的开源项目。我们相信,我们的执行驱动方法是解决各类合成问题的实用和可扩展的关键,包括那些在程序修复中出现的与测试案例有关的实词缺陷。