CN115098355A - 基于历史数据驱动的jvm测试程序生成方法 - Google Patents

基于历史数据驱动的jvm测试程序生成方法 Download PDF

Info

Publication number
CN115098355A
CN115098355A CN202210520621.7A CN202210520621A CN115098355A CN 115098355 A CN115098355 A CN 115098355A CN 202210520621 A CN202210520621 A CN 202210520621A CN 115098355 A CN115098355 A CN 115098355A
Authority
CN
China
Prior art keywords
program
code
seed
jvm
test program
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Granted
Application number
CN202210520621.7A
Other languages
English (en)
Other versions
CN115098355B (zh
Inventor
陈俊洁
赵英全
王赞
刘梦迪
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
Tianjin University
Original Assignee
Tianjin University
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Tianjin University filed Critical Tianjin University
Priority to CN202210520621.7A priority Critical patent/CN115098355B/zh
Publication of CN115098355A publication Critical patent/CN115098355A/zh
Application granted granted Critical
Publication of CN115098355B publication Critical patent/CN115098355B/zh
Active legal-status Critical Current
Anticipated expiration legal-status Critical

Links

Images

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F11/00Error detection; Error correction; Monitoring
    • G06F11/36Preventing errors by testing or debugging software
    • G06F11/3668Software testing
    • G06F11/3672Test management
    • G06F11/3684Test management for test design, e.g. generating new test cases
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F11/00Error detection; Error correction; Monitoring
    • G06F11/36Preventing errors by testing or debugging software
    • G06F11/3668Software testing
    • G06F11/3672Test management
    • G06F11/3688Test management for test execution, e.g. scheduling of test suites
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F9/00Arrangements for program control, e.g. control units
    • G06F9/06Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
    • G06F9/30Arrangements for executing machine instructions, e.g. instruction decode
    • G06F9/30003Arrangements for executing specific machine instructions
    • G06F9/3005Arrangements for executing specific machine instructions to perform operations for flow control
    • G06F9/30065Loop control instructions; iterative instructions, e.g. LOOP, REPEAT
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F9/00Arrangements for program control, e.g. control units
    • G06F9/06Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
    • G06F9/30Arrangements for executing machine instructions, e.g. instruction decode
    • G06F9/30003Arrangements for executing specific machine instructions
    • G06F9/30072Arrangements for executing specific machine instructions to perform conditional operations, e.g. using predicates or guards
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F9/00Arrangements for program control, e.g. control units
    • G06F9/06Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
    • G06F9/44Arrangements for executing specific programs
    • G06F9/455Emulation; Interpretation; Software simulation, e.g. virtualisation or emulation of application or operating system execution engines
    • G06F9/45504Abstract machines for programme code execution, e.g. Java virtual machine [JVM], interpreters, emulators

Landscapes

  • Engineering & Computer Science (AREA)
  • Theoretical Computer Science (AREA)
  • Software Systems (AREA)
  • Physics & Mathematics (AREA)
  • General Engineering & Computer Science (AREA)
  • General Physics & Mathematics (AREA)
  • Computer Hardware Design (AREA)
  • Quality & Reliability (AREA)
  • Debugging And Monitoring (AREA)
  • Test And Diagnosis Of Digital Computers (AREA)

Abstract

本发明公开了一种基于历史数据驱动的JVM测试程序生成方法,整体运行过程依序包括程序综合阶段以及执行阶段,首先进入所述程序综合阶段,包括5个步骤分别为从历史测试程序中提取代码片段、对种子程序进行初始化、选择代码片段、选择种子程序以及最终将代码片段插入至种子程序中;接着,进入所述执行阶段,执行合成的测试程序并检查其是否能触发JVM错误。与现有技术相比,本发明使用历史测试程序学习到历史上JVM错误的特征,从而指导后续的测试程序生成;很好地平衡了提取效率与代码片段有效性之间的关系;基于设计的损坏约束修复策略,一方面增强了代码片段与种子程序间的交互,另一方面提升了合成程序的有效性。

Description

基于历史数据驱动的JVM测试程序生成方法
技术领域
本发明涉及软件工程和软件测试领域,特别是涉及一种JVM测试程序的生成方法。
背景技术
Java虚拟机(Java Virtual Machine,简称JVM)为Java程序提供运行环境。近年来,已有很多公司或组织开发了不同的JVM实现,例如Oracle开发的Hotspot、IBM开发的OpenJ9以及阿里巴巴开发的龙井虚拟机(DragonWell)等。然而,同其他软件系统一样,JVM在开发与维护的过程中同样会引入缺陷。作为支撑Java语言庞大生态系统正常运行的重要基础设施,JVM的正确性对在其上面运行的所有Java程序的正确性起着决定性的作用。因此,确保JVM的正确性和健壮性对于Java程序至关重要。
JVM在开发与维护的过程中产生了大量的测试程序,这些测试程序由测试人员设计并实现对JVM的不同组件进行针对性的测试,且在JVM的历史版本中触发了不同的JVM缺陷。因此,历史测试程序中包含了大量与JVM缺陷相关的代码片段,而这些代码片段中则包含了JVM缺陷的不同特征,例如可能导致JVM缺陷的控制流、易错的API调用等。JavaTailor通过从JVM历史测试程序中提取类似的代码片段,并将其综合至新的种子程序中,在种子程序中为历史代码片段提供新的上下文,从而生成更多多样且容易致错的测试程序。此外,为了解决现有方法生成的测试程序有效性低下的问题,JavaTailor在程序综合过程中自动补全代码片段中缺失的变量或函数依赖,以此来保证合成程序的有效性。随后基于差异测试的思想对JVM进行测试,最终实现一套完整的JVM测试程序生成及测试执行的新方法。
另外,JVM其输入并非Java源程序,而是经过前端编译器(javac)对Java源程序编译之后生成的扩展名为.class的字节码文件,随后再由不同平台JVM中的后端编译器将字节码进行编译为相应平台的计算机指令。字节码作为源码与机器码的中间表达方式,屏蔽了不同硬件之间的差异,完全由JVM负责字节码到机器码的转换。JVM对字节码解析的正确与否决定了程序的正确性,尽管Java虚拟机规范中定义了详尽的字节码解析执行规范,但仍存在JVM中规范实现不完备、甚至JVM规范中忽略的问题,从而导致JVM中存在缺陷。同时,为了优化字节码程序的性能,JVM内部引入了大量的优化方式,例如,JIT编译器。在程序运行时,解释器首先对字节码进行解释执行。随着时间的推移,JIT编译器通过识别出频繁被执行的代码片段,即热点代码,随后将其优化编译成本地代码,从而获取更高的执行效率。除此之外,不同的优化方法还包括方法内联、常量传播以及窥孔优化等等。开发人员通过设定不同的优化方法组合从而使Java程序在JVM上获得更高的执行效率。如此庞大的优化空间同样可能会引入大量的优化缺陷。因此,为了对JVM进行测试,需要生成大量且多样的测试输入,即字节码文件。
常用的构造字节码文件的方式主要分为两种:一种是人工构造,即测试工程师针对特定的组件设计对应的测试用例及其想用的测试语言。然而人工构造的方式通常需要花费大量的精力且设计的测试用例并不足以测试JVM的所有特性,成本较高;另一种方法则是通过模糊测试的方法随机的对已有的种子程序进行变异,从而生成新的测试程序。然而,由于字节码文件中包含着大量复杂的语法和语义约束,同时,不同的程序之间还存在复杂的依赖关系。因此,此类方法生成的测试程序中存在大量冗余无效的测试程序。目前,已有研究工作基于此类方法生成新的测试程序监测JVM错误,例如classfuzz以及classming。其中classfuzz通过设定大量的变异规则(语法突变)对字节码文件进行变异,并通过在不同的JVM实现上执行变异后的字节码文件,最终通过差异测试的方法来判断不同的JVM实现中是否存在缺陷。考虑到classfuzz主要针对JVM启动阶段的缺陷,为了更进一步测试JVM执行过程中可能存在的缺陷,研究人员在classfuzz的基础上提出了classming。classming通过定义Live Bytecode Mutation来捕获种子文件中的可执行代码行,并通过改变种子文件的数据流或控制流(语义突变)来实现对种子文件的变异,从而生成新的测试程序。classming中的变异方法可以有效的生成可执行、数据流/控制流多样的测试程序。然而,JVM的特性众多,仅仅通过改变数据流/控制流的方式很难覆盖JVM中其他的特性,因此局限性较高。综上所述,现有研究的变异方法多集中在较小的语法/语义突变上,导致现有研究方法产生的测试程序其多样性和有效性远远不够。
综合来看,相关现有技术存在以下缺点和不足:
1、基于语法变异设计的变异规则容易生成大量不合符Java语法的测试程序,测试程序在JVM启动阶段被拒绝而未得到真正的执行,导致测试效率低下的问题;
2、基于语义变异设计的变异规则所生成的测试程序比较单一,且数据流/控制流的多样性并不能代表测试程序的多样性,导致JVM并未得到充分的测试;
3、现有方法所设计的变异规则突变较小,其新生成的测试程序揭错能力较弱,往往需要大量的测试程序来触发JVM缺陷,测试成本较高。
发明内容
为了突破现有方法的技术局限性、解决JVM测试程序生成过程中有效性、多样性低下、测试成本较高以及测试效率低下的问题,本发明提出一种基于历史驱动的JVM测试程序生成方法,首次提出从JVM历史测试程序中提取更容易致错的代码片段,并将提取到的代码片段综合至新的种子文件中,以此来达到合成更多样且容易致错测试程序。
为实现本发明的目的所采用的技术方案如下:
一种基于历史数据驱动的JVM测试程序生成方法,整体运行过程依序包括程序综合阶段以及执行阶段,具体过程描述如下:
首先,进入所述程序综合阶段,具体包括以下步骤:
步骤1、对种子程序进行初始化,该过程具体包括:首先基于SOOT工具读取种子程序,分析其是否缺失依赖关系;随后,基于SOOT工具提取类文件以及函数中定义的变量信息并保存至对应的种子程序对象中,所有的种子程序初始化完成后,得到一个可执行且包含变量信息的种子程序池;
步骤2、以代码块作为提取的粒度进行代码片段的提取,设定5种代码片段的提取规则,最终将所有提取到的代码片段添加至代码片段池中;
步骤3、进行代码片段的选择,即从代码片段池中选择用于进行程序综合的代码片段,具体采用随机算法实现代码片段的选择;
步骤4、进行种子程序的选择,将代码片段对应的一段Jimple指令插入至适当的位置进行编译执行,即所有的代码片段都需插入至函数,以此保证合成程序的有效性,在选中种子文件之后,随机选择一个函数并在函数内部随机选择一个指令点来插入提取到的代码片段;
步骤5、将代码片段自插入点插入至种子程序中,得到合成的测试程序;
接着,进入所述执行阶段,具体包括以下步骤:
执行合成的测试程序并检查其是否能触发JVM错误。
所述步骤2中设定的5种代码片段的提取规则具体包括:
一系列顺序指令且没有任何分支的代码片段;
一个或多个条件分支以及对应代码体,即if、else if和else的代码片段;
while、do-while或for循环条件及对应循环体的代码片段;
switch条件与所有case及其对应代码体的代码片段;
try代码体以及用于处理捕获异常代码体的代码片段。
所述步骤2中,进行代码片段的提取之前,还包括以下处理:
通过SOOT工具来处理历史测试程序并将其转换为Jimple代码级别的控制流图,在控制流图中,每个节点代表一条或多条指令的基本块,节点之间的边则表示不同基本块之间的控制逻辑;定义的代码片段在控制流图中包含一个或多个基本代码块;
基于历史测试程序的控制流图,根据每个基本代码块中包含的指令类型识别代码片段的起始节点。
结合程序综合阶段以及执行阶段在内的整体流程中,如果合成的测试程序没有触发JVM不一致且可以正常执行,则会将其放入种子程序池中;新合成的测试程序用作后续程序综合的种子程序,从而生成一个结合了来自不同历史测试程序的多种代码片段的测试程序。
在所述步骤5中,对于缺失的变量声明,设计了两种修复策略,分别是重用种子程序中的变量和声明新的变量,即:查找种子程序中是否包含与缺失变量类型相同的变量声明。如果有,则优先选择将种子程序中的变量赋值给代码片段中缺失声明的变量。
在所述步骤5中,还包括构建新的变量声明来修复损坏的约束的步骤:首先,对于原始类型以及String类型的变量,例如int,float等,直接构造其相应类型的变量,并对其进行随机初始化;其次,对于对象类型的变量,首先通过获取该类型的所有构造函数,通过其构造函数声明新的类型的变量。
与现有技术相比,本发明提出的技术方法的有益效果是:
1)使用历史测试程序学习到历史上JVM错误的特征,从而指导后续的测试程序生成;
2)基于定义的代码片段提取规则很好地平衡了提取效率与代码片段有效性之间的关系;
3)基于设计的损坏约束修复策略,一方面增强了代码片段与种子程序间的交互,另一方面提升了合成程序的有效性。
附图说明
图1为本发明的基于历史数据驱动的JVM测试程序生成方法整体流程图;
图2为本发明的基于历史数据驱动的JVM测试程序生成方法框图;
图3为代码片段提取示意图;
图4为损坏约束修复示意图;
图5为种子程序示例图;
图6为历史测试程序示例图;
图7为合成程序示例图;
图8为OpenJ9 Bug#12552示例图。
具体实施方式
下面将结合附图对本发明的具体实施方式做详细说明。
考虑到本发明需要基于历史测试程序以及种子程序合成新的测试程序,因此在程序综合阶段需要对输入的历史测试程序及种子程序进行拆分及合成。程序综合阶段主要分为5个步骤,分别负责从历史测试程序中提取代码片段、对种子程序进行初始化、选择代码片段、选择种子程序以及最终将代码片段插入至种子程序中。程序综合阶段的输出为合成的测试程序,其将作为执行阶段的输入对JVM进行差异测试。执行阶段基于合成程序进行差异测试。差异测试的主要思想在于所有不同实现的JVM都应该符合《Java虚拟机规范》。因此,对于同一个测试程序,不同的JVM实现在相同规范下的执行结果应该是一致的。如果不同JVM的执行结果不一致则表明测试的JVM存在问题,本发明将对此类触发了JVM执行结果不一致的合成程序进行输出以进行进一步的分析。对于执行结果一致且合法的测试程序,则将其放回种子程序池中以在其合成的基础上进行更高阶的程序合成。本发明的输出为可以导致JVM差异的合成程序以及对应的差异报告,基于差异报告以及合成程序可以进一步的分析整理导致JVM出错的原因。
本发明的一种基于历史数据驱动的JVM测试程序生成方法(JavaTailor)主体分成两个阶段:程序综合阶段以及执行阶段,如图1所示,为本发明的基于历史数据驱动的JVM测试程序生成方法整体流程图。如图2所示,为本发明的基于历史数据驱动的JVM测试程序生成方法框图。
一、程序综合阶段,本阶段主要负责代码片段的提取以及合成,分成5个步骤,分别为:种子程序初始化、代码片段的提取、代码片段的选择、种子程序的选择以及插入。
具体步骤描述如下:
步骤1:对种子程序进行初始化,该过程具体包括:首先基于SOOT工具读取种子程序,分析所有的种子文件是否缺失依赖关系。具体说来,本发明通过Java反射技术获取所有种子文件的方法列表,在该过程中,若种子文件存在缺失的依赖则无法被Java虚拟机成功的加载,本发明则会将这些无法被成功加载的种子程序过滤掉;随后,基于SOOT工具提取类文件以及函数中定义的变量信息并保存至对应的种子程序对象中,所有的种子程序初始化完成后,得到一个可执行且包含变量信息的种子程序池。SOOT工具是在工业界以及学术界广泛被使用的Java字节码分析工具,提供了多种字节码分析和变换的功能。本步骤是为了确保种子程序是合法且依赖关系齐全、可以正常执行的种子程序。
分析种子程序的合法性以及提取种子程序中的变量信息,具体说来,本发明提取该种子程序中的所有全局变量,并保存变量的类型及变量值,以在后续的过程中基于变量类型匹配可以重复利用的变量来替换代码片段中的未定义的变量。首先,考虑到Java程序中往往存在大量复杂的依赖关系,例如,在种子程序P中包含类文件A与类文件B,其中类文件A中实例化了类文件B的对象,则类文件A存在对类文件B的依赖关系。在代码片段插入至种子程序的步骤中,本发明将会用种子程序中与代码片段中类型相同的变量进行赋值替换,以此加强代码片段与种子程序的交互。因此,对于给定的种子程序,需要对其进行加载并分析。
如表1所示,为种子程序的基本信息描述。其中,1~6为现有研究中广泛使用的测试程序,分别来自不同的Java开源项目,7~8为Hotspot及Openj9代码仓库中的测试程序。第二列为示例项目的项目名称;第三列为该示例项目所包含的种子程序数量;第四列则是所有种子程序中的Jimple指令的数量。以此来探索不同JVM之间的测试程序是否可以彼此增强合成程序的有效性。本发明选择可以被执行的测试程序作为种子程序。可被执行表明该程序中包含main函数或Junit的测试用例。不包括入口函数的测试程序因为无法被直接执行,因此不考虑在内。此外,选择丢弃直接触发JVM错误的测试程序以及在实验环境中无法正常执行的测试程序。最终,从Hotspot及Openj9的代码仓库中分别整理出630、1616个种子程序。
表1
Figure BDA0003643233110000081
步骤2:以代码块作为提取的粒度进行代码片段的提取(基于SOOT工具来实现),设定了5种代码片段的提取规则,最终将所有提取到的代码片段添加至代码片段池中;
代码片段的粒度直接影响代码片段提取的有效性以及提取效率。如果设定的代码片段粒度过粗,例如从文件级别衡量代码片段,则会导致合成的程序与种子程序交互较少很难包含有新的行为。如果设定的代码片段粒度过细,例如从代码行级别衡量代码片段,则会导致提取的代码片段破坏原有的可以触发JVM缺陷的模式且提取的成本较高。因此设计合适的粒度提取代码片段对后续提升合成程序的质量至关重要。为了提取代码片段,本发明采用代码块作为提取的粒度以此在提取效率和有效性之间进行平衡。具体说来,本发明系统地设计了5种代码片段的提取规则,如表2所示。
表2代码片段提取规则
Figure BDA0003643233110000091
上述5种代码片段类型可以涵盖SOOT工具支持的所有类型的字节码指令。本发明从类文件(Jimple代码)的角度而非源代码层面的角度提取代码片段。其原因在于:一方面,SOOT工具中提供了丰富的对Jimple代码进行操作的功能函数,有利于本发明的实现;另一方面,基于类文件进行操作有助于屏蔽前端编译器(如javac)中大量的语法约束,有利于生成更多语法、语义更丰富的测试程序。
为了实现代码片段的提取,通过SOOT工具来处理历史测试程序并将其转换为Jimple代码级别的控制流图(CFG)。在CFG中,每个节点代表一条或多条指令的基本块,节点之间的边则表示不同基本块之间的控制逻辑。定义的代码片段在CFG中包含一个或多个基本代码块。基于历史测试程序的CFG,首先根据每个基本代码块中包含的指令类型识别表2中2-5四种类型代码片段的起始节点。例如,一个基本块中包含一个Switch指令,则其将被视为SWITCH的起始节点。随后,基于此搜索与其相关的所有基本块来组成完整的代码片段。例如,在识别出包含Switch指令的基本块之后,会针对该指令的所有case分支进行分析,并将其提取组成一个完整的SWITCH代码片段。值得注意的是,IF与LOOP的提取稍有不同。如果一个基本块中包含if指令,并不能基于此判断其是IF片段的起始点还是LOOP的起始点。因为在SOOT工具中,循环表示为if和goto指令的组成。因此,在提取这两类代码片段时,需要检查后续的基本块中是否包含goto指令。如果后续的基本块中包含goto指令则该基本块为LOOP的起始节点,否则则是IF的起始节点。对于不能被识别为这四类代码片段的任何一种起点的基本块,JavaTailor将其视为顺序代码片段,即SEQ。
如图3所示,为代码片段提取示意图。其中,代码片段为for循环中嵌套if的CFG。对于CFG中的每个基本代码块,首先检查其是否为表2中不同代码片段的起点。例如,对于标记为2的基本块(块2),其包含一个if指令,表明块2可能是IF片段或LOOP片段的起点。为了确定其具体的类型,递归地获取其所有的后继块,发现在块5中包含有一条goto指令指向块2,表明这是一个LOOP代码片段,对应于图中标记为FOR的部分。对于块3,在所有后续块中都没有指向它的goto指令,因此它是一个IF码片段,对应于标记为IF的部分。依此类推,在提取过程执行结束之后,可以提取3种类型共计4个代码片段,如底部所示LOOP:{2,3,4,5}、IF:{,3,4,5}和SEQ:{4},{5}由于CFG的开始块(块1)与结束块(块6)的代码逻辑比较简单,因此在实现的过程中对此类基本块进行了过滤;
基于上述提取规则,从Hotspot的630个历史测试程序中共提取了33,002个代码片段,具体信息如表3所示。
表3代码片段提取信息
Figure BDA0003643233110000111
步骤3:进行代码片段的选择,即从代码片段池中将被选择的代码片段插入至种子程序中,具体采用随机算法实现代码片段的选择,即在代码片段池中随机选择一个代码片段作为后续插入种子程序中的代码片段。并且,在选择算法中仍留有相应的接口,以供在后续的开发中方便的添加更智能的选择方法;
步骤4:进行种子程序的选择,具体包括:同样基于随机算法进行种子程序的选择;具体包括,将代码片段对应的一段Jimple指令插入至适当的位置进行编译执行,即所有的代码片段都需插入至函数以此保证合成程序的有效性,因为如果将代码片段插入至函数外,则会导致合成的程序包含语法错误从而无法正确编译;在选中种子文件之后,随机选择一个函数并在函数内部随机选择一个指令点来插入提取到的代码片段。
与代码片段的选择不同的是,在选中种子程序之后仍需要对代码片段的插入位置进行选择。种子程序是一个类文件,类文件中包含定义的函数,函数体内部则包含对应的Jimple指令。因此,在选中种子文件之后,随机选择一个函数并在函数内部随机选择一个指令点来插入提取到的代码片段;
步骤5:将代码片段自插入点插入至种子程序中,具体为:根据步骤3选定的代码片段以及步骤4中选定的对应的种子程序和插入点,即可将对应的代码片段插入至种子程序中。然而,由于这种方法打破了代码片段中原始的语法和语义约束,例如代码片段中使用的变量或者函数调用在种子程序的上下文中并没有对应的定义。因此,为了保证合成程序的有效性,需要在合成的过程中修复这些被破坏语法/语义的约束。对于缺失的函数声明,由于函数内部包含复杂的依赖关系,为了降低修复的成本,本发明将代码片段中所有函数调用的声明都修改为public,以便在外部对其进行调用。对于缺失的变量声明,设计了两种修复策略,分别是重用种子程序中的变量和声明新的变量。
对于代码片段中缺失的变量声明,本发明更倾向于查找种子程序中是否包含与缺失变量类型相同的变量声明。如果有,则优先选择将种子程序中的变量赋值给代码片段中缺失声明的变量。这种做法可以加强代码片段与种子程序之间的交互,新的上下文将对历史代码片段产生更大的影响,从而使得生成的测试程序更具有多样性。具体说来,在步骤1的种子程序初始化过程中保存了程序内部的变量声明,因此在查找类型兼容的变量时只需对函数内部插入点之间声明的变量进行遍历,如果找到类型兼容的变量则将代码片段中的变量调用替换为种子内的变量。
对于代码片段中缺失且种子程序中同样不包含的变量声明,通过构建新的变量声明来修复损坏的约束。首先,对于原始类型以及String类型的变量,例如int,float等,直接构造其相应类型的变量,并对其进行随机初始化。其次,对于对象类型的变量,首先通过获取该类型的所有构造函数,通过其构造函数声明新的类型的变量。值得注意的是,构造函数在声明过程中往往需要其他类型的参数,因此对于其他类型的参数同样需要为其创建新的声明。这里通过递归的方式创建所有缺失的变量声明,直到所有的参数类型都是原始类型。特别的,为了避免构造方法陷入死递归,设置了最大递归深度作为终止条件,本发明中最大递归深度设置为5。如果超过最大递归深度,仍没有创建出对应类型的变量,将会把对应的参数初始化为null。
接下来阐述破坏约束的修复过程。如图4所示,为损坏约束修复示意图。在灰色代码片段Ingredient中调用了fun函数,其中fun函数的参数为类型为Class1的形参c1。为了创建Class1的声明,需要为其构造函数创建新的变量(即String str和Class2 c2)。对于String类型的变量,由于种子程序中包含字符串类型变量的定义(即s0),则在j处重新使用该变量(即将s0赋值给str)。对于种子程序中未定义的变量声明Class2,需要在下一层的递归(即Depth 2)中创建Class2类型的变量及int和float类型的参数。由于Class2构造函数的参数都是原始类型,在k和l处为它们随机生成一个变量值。随后便可在m处创建Class2的变量,最终在n处创建Class1 c1的定义。在修复了被破坏的约束之后,将处理过的代码片段插入到插入点之前。需要注意的是,对于包含return指令的代码片段,将其替换为goto指令,以避免过早终止综合测试程序。同时为每个goto指令分配一个标签,以此来进一步增强代码片段和种子程序之间的交互。
二、执行阶段,具体步骤描述如下:
在程序综合阶段执行结束之后,则会产生一个新的合成程序,并以此作为执行阶段的输入。执行阶段拿到合成的测试程序之后,执行合成的测试程序并检查其是否能触发JVM错误。在本发明中,采用差异测试作为测试语言。具体来说,通过比较不同JVM在同一个测试程序的输出来判断JVM是否出现错误,即通过匹配不同JVM在同一个测试程序上执行结果的一致性来判断是否触发JVM错误。在测试过程中,本发明采用了目前比较主流的JVM实现,即Hotspot和Openj9。如表4所示,为本发明中使用的测试对象的信息,本发明选择目前使用人数最多的两个OpenJDK版本(即OpenJDK及OpenJDK11)作为测试对象。对应的JVM及JVM版本如2-3列所示。
表4
Figure BDA0003643233110000131
基于差异测试可以有效的缓解JVM测试中缺少测试语言的问题。然而,由于测试程序中往往会包含大量的输出,其中可能包含非确定的输出结果(例如时间、随机数等)。此外,对于相同的异常,不同的JVM实现也可能会产生不同的调用堆栈,也增加了判断JVM不一致的难度。为此,本发明从三个角度设计检测规则来解决这些问题:
(1)在同一个测试程序上,如果一个JVM正常执行而另一个JVM崩溃,JavaTailor将其视为一个JVM不一致。
(2)在同一个测试程序上,如果两个JVM实现都发生了崩溃,JavaTailor通过正则表达式匹配调用的堆栈信息的异常消息(例如包含Exception、Error和Failure关键字的行),以此来减少不同JVM实现产生的不同风格堆栈信息的影响。如果提取的异常消息不同,JavaTailor将其视为一个JVM不一致。
(3)在同一个测试程序上,如果两个JVM都正常执行至结束,则其输出由该测试程序产生。为了不确定性输出对判断不一致带来的干扰,通过正则表达式过滤掉非确定性的输出(例如时间、随机、线程等关键字和常见的时间格式等),随后,过滤掉由测试程序使用的第三方库(如JUnit和Log4J)所产生的输出。如果过滤后的输出仍然不同,将其视为一个JVM不一致。
结合程序综合阶段以及执行阶段在内的整体流程中,如果合成的测试程序没有触发JVM不一致且可以正常执行,则会将其放入种子程序池中。这样,新合成的测试程序可以用作后续程序综合的种子程序,从而可以生成一个结合了来自不同历史测试程序的多种代码片段的测试程序,有利于生成更多样且更容易致错的测试程序。
本发明的输入分为两个部分,分别为历史测试程序以及种子测试程序。其中历史测试程序将被用来提取不同的代码片段,为了使得合成的测试程序更具揭错能力,提取的代码片段中应包含JVM易错的特征。因此,历史测试程序为开发人员在JVM历史版本中针对不同组件设计并实现测试程序。具体说来,本发明从Hotspot的代码仓库中收集历史测试程序。从Hotspot代码仓库收集测试程序主要因为其为Hotspot的每个Bug都提供了对应的测试程序,以及对应的Bug描述信息。这有利于从大量的测试程序中筛选出可以触发JVM错误的测试程序。此外,本发明过滤掉在实验环境中无法正常执行的测试程序,最终,本发明共集成了630个历史测试程序,并在后续的步骤中基于此提取代码片段;种子程序是为历史代码片段提供一个新的上下文环境,因此种子可以是任何合法的Java程序。为了与已有方法保持同步,本发明中收集了6个广泛使用于现有研究中的测试程序,以及从Hotspot以及OpenJ9历史仓库中整理的部分测试程序作为种子程序。
与现有技术相比,本发明有以下优点:
1)本发明通过对历史上触发JVM错误的测试程序进行挖掘与学习,相比于现有基于变异的方法,有历史经验的指导有助于生成更容易致错的测试程序。
2)本发明基于程序综合来合成新的测试程序,相比于现有方法设计的较小的变异算子,效率更高,生成的测试程序更具多样性及揭错能力。
3)本发明通过为代码片段修复缺失的依赖,提升了测试程序合成的有效性,有助于生成更多合法的测试程序,减少探索非法测试程序的开销。
以下为本发明的基于历史测试程序合成新JVM测试程序方法具体实施例的具体描述:
如图6所示,为历史测试程序示例图。该实施例包含以下步骤:
步骤1:对种子程序进行初始化。如图5所示,为种子程序示例图。首先进行种子程序示例初始化分析,该种子程序中只包含一个main函数,main函数中包含两行语句,同时在第二行声明了一个String类型的变量str;因此,该种子程序中未包含对其它类文件的引用,逻辑较为简单。
步骤2:提取历史测试程序中的代码片段。基于本发明中定义的5种代码片段提取规则从该给定的历史测试程序示例中进行代码片段的提取,提取到的代码片段分别为SEQ:{2、(5,6)、(8,9,10)},IF:{(7,8,9,10)};
步骤3:选择代码片段。基于步骤2中提取到的代码片段,假设随机选中的代码片段为SEQ:(5,6),即图5中标记为灰色部分。
步骤4:种子程序的选择。这里为了方便演示,图4中的种子程序较为简单,因此假设随机选中的函数为main函数,且选中的插入点为第三行。
步骤五:将选中的代码片段插入至种子程序中。基于步骤三中选中的代码片段分析可得,该代码片段中共缺少4个变量的声明,分别为MemoryNotificationInfo类型的变量mn、String类型的变量name、MemoryUsage类型的变量usage以及long类型的变量count。因此,为了保证合成程序的合法性,JavaTailor需要基于损坏约束修复策略为缺失的变量创建新的变量声明。首先,基于步骤一的初始化结果可知,种子程序中包含String类型的变量str。因此,对于代码片段中缺少的String类型变量声明name,可以通过将str赋值给name解决。如图6中第4行所示,JavaTailor直接将str赋值给name。对于其他种子程序中没有定义相关类型兼容的声明,JavaTailor需要为其创建新的变量声明。对于原始类型变量count,JavaTailor随机为其赋值,如第6行所示;对于变量mn则只需对其添加声明语句即可,如第7行所示;需要注意的是,在为usage变量创建声明语句时,由于其超过了最大的递归深度,导致创建新的变量声明失败,所以最终usage被赋值为null,如第5行所示。最终,在修复了代码片段中所有被破坏的约束之后,便可以将代码片段插入至对应的位置,如第8,9两行所示。
步骤六:合成程序的执行。如图7所示,为合成程序示例图。基于步骤5中合成的测试程序,JavaTailor将其分别运行在表4中的JVM实现上。最终的测试结果是,对于该测试程序,Hotspot都抛出NullPointerException异常,而Openj9均正常执行。为此,本团队将对应的测试用例提交给Openj9的开发人员,开发人员经过对测试程序执行分析最终确定这是Openj9中实现的缺陷,如图8所示,OpenJ9 Bug#12552示例图。在MemoryNotificationInfo的构造函数中,Openj9对传入的参数并未进行空指针检查,因此导致对于传入为null的poolName,程序却正常执行。最终,Openj9的开发人员修复了该缺陷。
综上所述,本发明相比于现有的JVM缺陷检测工具,可以从历史上触发JVM错误的测试程序中学习到了更易导致JVM错误的程序逻辑以及程序特征,因此可以生成更多质量更高且多样的测试程序。经验证,本发明合成的测试程序对主流的JVM实现进行测试,并向Hotspot、OpenJ9的开发人员提交了10个尚未被发现的JVM错误,目前已有6个错误已经被确认或修复。
以上所述仅为本申请的实施例,并不用于限制本申请。对于本领域技术人员来说,本申请可以有各种更改和变化。本领域技术人员可以在不脱离本发明的精神和范围的情况下做出各种任何修改、等同替换或变型等,凡在本申请的精神和原理之内所作的修改、等同替换或变型等均落入由所附权利要求所限定的本发明的保护范围之内。

Claims (6)

1.一种基于历史数据驱动的JVM测试程序生成方法,其特征在于,整体运行过程依序包括程序综合阶段以及执行阶段,具体过程描述如下:
首先,进入所述程序综合阶段,具体包括以下步骤:
步骤1、对种子程序进行初始化,该过程具体包括:首先基于SOOT工具读取种子程序,分析其是否缺失依赖关系;随后,基于SOOT工具提取类文件以及函数中定义的变量信息并保存至对应的种子程序对象中,所有的种子程序初始化完成后,得到一个可执行且包含变量信息的种子程序池;
步骤2、以代码块作为提取的粒度进行代码片段的提取,设定5种代码片段的提取规则,最终将所有提取到的代码片段添加至代码片段池中;
步骤3、进行代码片段的选择,即从代码片段池中选择用于进行程序综合的代码片段,具体采用随机算法实现代码片段的选择;
步骤4、进行种子程序的选择,将代码片段对应的一段Jimple指令插入至适当的位置进行编译执行,即所有的代码片段都需插入至函数,以此保证合成程序的有效性,在选中种子文件之后,随机选择一个函数并在函数内部随机选择一个指令点来插入提取到的代码片段;
步骤5、将代码片段自插入点插入至种子程序中,得到合成的测试程序;
接着,进入所述执行阶段,具体包括以下步骤:
执行合成的测试程序并检查其是否能触发JVM错误。
2.如权利要求1所述的一种基于历史数据驱动的JVM测试程序生成方法,其特征在于,所述步骤2中设定的5种代码片段的提取规则具体包括:
一系列顺序指令且没有任何分支的代码片段;
一个或多个条件分支以及对应代码体,即if、else if和else的代码片段;
while、do-while或for循环条件及对应循环体的代码片段;
switch条件与所有case及其对应代码体的代码片段;
try代码体以及用于处理捕获异常代码体的代码片段。
3.如权利要求1所述的一种基于历史数据驱动的JVM测试程序生成方法,其特征在于,所述步骤2中,进行代码片段的提取之前,还包括以下处理:
通过SOOT工具来处理历史测试程序并将其转换为Jimple代码级别的控制流图,在控制流图中,每个节点代表一条或多条指令的基本块,节点之间的边则表示不同基本块之间的控制逻辑;定义的代码片段在控制流图中包含一个或多个基本代码块;
基于历史测试程序的控制流图,根据每个基本代码块中包含的指令类型识别代码片段的起始节点。
4.如权利要求1所述的一种基于历史数据驱动的JVM测试程序生成方法,其特征在于,结合程序综合阶段以及执行阶段在内的整体流程中,如果合成的测试程序没有触发JVM不一致且可以正常执行,则会将其放入种子程序池中;新合成的测试程序用作后续程序综合的种子程序,从而生成一个结合了来自不同历史测试程序的多种代码片段的测试程序。
5.如权利要求1所述的一种基于历史数据驱动的JVM测试程序生成方法,其特征在于,在所述步骤5中,对于缺失的变量声明,设计了两种修复策略,分别是重用种子程序中的变量和声明新的变量,即:查找种子程序中是否包含与缺失变量类型相同的变量声明。如果有,则优先选择将种子程序中的变量赋值给代码片段中缺失声明的变量。
6.如权利要求1所述的一种基于历史数据驱动的JVM测试程序生成方法,其特征在于,在所述步骤5中,还包括构建新的变量声明来修复损坏的约束的步骤:首先,对于原始类型以及String类型的变量,例如int,float等,直接构造其相应类型的变量,并对其进行随机初始化;其次,对于对象类型的变量,首先通过获取该类型的所有构造函数,通过其构造函数声明新的类型的变量。
CN202210520621.7A 2022-05-13 2022-05-13 基于历史数据驱动的jvm测试程序生成方法 Active CN115098355B (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN202210520621.7A CN115098355B (zh) 2022-05-13 2022-05-13 基于历史数据驱动的jvm测试程序生成方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN202210520621.7A CN115098355B (zh) 2022-05-13 2022-05-13 基于历史数据驱动的jvm测试程序生成方法

Publications (2)

Publication Number Publication Date
CN115098355A true CN115098355A (zh) 2022-09-23
CN115098355B CN115098355B (zh) 2024-08-16

Family

ID=83287794

Family Applications (1)

Application Number Title Priority Date Filing Date
CN202210520621.7A Active CN115098355B (zh) 2022-05-13 2022-05-13 基于历史数据驱动的jvm测试程序生成方法

Country Status (1)

Country Link
CN (1) CN115098355B (zh)

Cited By (1)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN115576854A (zh) * 2022-11-25 2023-01-06 微科智检(佛山市)科技有限公司 一种安卓自动化单元测试方法

Citations (6)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN102117228A (zh) * 2011-02-28 2011-07-06 复旦大学 一种动静态结合的Java程序异常处理优化方法
US20130283101A1 (en) * 2012-04-18 2013-10-24 The Regents Of The University Of Michigan Trace-Driven Verification of Multithreaded Programs Using SMT-Based Analysis
CN113051161A (zh) * 2021-03-22 2021-06-29 大连理工大学 基于历史代码变更信息的api误用检测方法
US11237952B1 (en) * 2021-04-07 2022-02-01 State Farm Mutual Automobile Insurance Company Runtime class recompilation during mutation testing
CN114297666A (zh) * 2021-12-31 2022-04-08 上海安般信息科技有限公司 一种基于模糊测试的云部署自动化漏洞挖掘系统
CN114329478A (zh) * 2021-12-07 2022-04-12 复旦大学 一种安卓系统服务内存消耗类漏洞挖掘方法

Patent Citations (6)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN102117228A (zh) * 2011-02-28 2011-07-06 复旦大学 一种动静态结合的Java程序异常处理优化方法
US20130283101A1 (en) * 2012-04-18 2013-10-24 The Regents Of The University Of Michigan Trace-Driven Verification of Multithreaded Programs Using SMT-Based Analysis
CN113051161A (zh) * 2021-03-22 2021-06-29 大连理工大学 基于历史代码变更信息的api误用检测方法
US11237952B1 (en) * 2021-04-07 2022-02-01 State Farm Mutual Automobile Insurance Company Runtime class recompilation during mutation testing
CN114329478A (zh) * 2021-12-07 2022-04-12 复旦大学 一种安卓系统服务内存消耗类漏洞挖掘方法
CN114297666A (zh) * 2021-12-31 2022-04-08 上海安般信息科技有限公司 一种基于模糊测试的云部署自动化漏洞挖掘系统

Non-Patent Citations (1)

* Cited by examiner, † Cited by third party
Title
曹志钦;王轶辰;王坤: "基于故障传播路径覆盖的软件测试", 计算机科学与应用, no. 009, 31 December 2019 (2019-12-31) *

Cited By (2)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN115576854A (zh) * 2022-11-25 2023-01-06 微科智检(佛山市)科技有限公司 一种安卓自动化单元测试方法
CN115576854B (zh) * 2022-11-25 2023-03-28 微科智检(佛山市)科技有限公司 一种安卓自动化单元测试方法

Also Published As

Publication number Publication date
CN115098355B (zh) 2024-08-16

Similar Documents

Publication Publication Date Title
Han et al. CodeAlchemist: Semantics-aware code generation to find vulnerabilities in JavaScript engines.
Sridharan et al. Thin slicing
Zhao et al. History-driven test program synthesis for JVM testing
Weimer et al. Automatically finding patches using genetic programming
Khurshid et al. TestEra: Specification-based testing of Java programs using SAT
US7975256B2 (en) Optimizing application performance through data mining
US6085029A (en) Method using a computer for automatically instrumenting a computer program for dynamic debugging
US6928393B2 (en) Method and system for supporting negative testing in combinatorial test case generators
WO2010014981A2 (en) Method and apparatus for detection and optimization of presumably parallel program regions
US20070169023A1 (en) Restructuring computer programs
Davis et al. The reflective Milawa theorem prover is sound (down to the machine code that runs it)
Garg et al. Deepdev-perf: a deep learning-based approach for improving software performance
CN115098355B (zh) 基于历史数据驱动的jvm测试程序生成方法
Deng et al. Kiasan/KUnit: Automatic test case generation and analysis feedback for open object-oriented systems
Demange et al. Mechanizing conventional SSA for a verified destruction with coalescing
Bartha et al. One down, 699 to go: or, synthesising compositional desugarings
Saes Unit test generation using machine learning
CN115310095A (zh) 一种区块链智能合约混合形式化验证方法及系统
Palka Testing an Optimising Compiler by Generating Random Lambda Terms
JP4633203B2 (ja) シミュレートされたプログラムの実行エラーの検出方法および装置
Pit-Claudel Compilation using correct-by-construction program synthesis
Taghdiri et al. Lightweight extraction of syntactic specifications
Seesing Evotest: Test case generation using genetic programming and software analysis
Mennie et al. Giving meaning to macros
Van Thuy et al. Automated large program repair based on big code

Legal Events

Date Code Title Description
PB01 Publication
PB01 Publication
SE01 Entry into force of request for substantive examination
SE01 Entry into force of request for substantive examination
GR01 Patent grant
GR01 Patent grant