CN104794059B - 一种基于函数调用记录的缺陷定位方法及装置 - Google Patents
一种基于函数调用记录的缺陷定位方法及装置 Download PDFInfo
- Publication number
- CN104794059B CN104794059B CN201510233119.8A CN201510233119A CN104794059B CN 104794059 B CN104794059 B CN 104794059B CN 201510233119 A CN201510233119 A CN 201510233119A CN 104794059 B CN104794059 B CN 104794059B
- Authority
- CN
- China
- Prior art keywords
- function
- call
- value
- information
- test case
- 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.)
- Expired - Fee Related
Links
Landscapes
- Debugging And Monitoring (AREA)
Abstract
本发明涉及一种基于函数调用记录的缺陷定位方法及装置,属于软件测试技术领域。本发明方法利用面向切面编程AOP技术,针对每一个测试用例获取被测程序的函数调用记录,并以函数为单位对该记录进行统计分析,获取函数在成功或失败的测试用例执行中的测试用例覆盖信息、调用次数差异信息、数据传递差异信息以及调用关系差异信息,然后利用这些信息计算得到函数的缺陷嫌疑值,最后对所有函数的缺陷嫌疑值进行排序即可得到缺陷定位结果。对比现有技术,本发明能够在不改变现有测试流程的前提下,方便地进行缺陷定位,并具有更高的定位准确度。
Description
技术领域
本发明涉及一种缺陷定位方法,特别涉及一种利用软件测试或执行过程中产生的函数调用记录进行缺陷定位的方法及装置,属于软件测试技术领域。
背景技术
缺陷在软件系统中往往是不可避免的,为了提高软件的安全性、可靠性,软件开发团队必须投入足够的资源用于寻找与修复软件中存在的缺陷。然而传统的寻找缺陷的方法(如手工调试)存在耗时长、可靠度低、重现条件要求高等缺点,为了减少在这个过程投入的成本,研究人员一直致力于开发自动化或半自动化的软件缺陷定位技术,一批统计学缺陷定位(Statistical Fault Localization,SFL)方法被相继提出。这类方法将软件在测试通过和测试失败两种情况下的运行信息进行比较,利用它们之间的差异来判断缺陷的存在位置。
经典的统计学缺陷定位算法,如Tarantula,通过利用执行了语句单元s的失败和通过的测试用例数量,以及失败与通过的测试用例的总数量,计算得到语句单元s存在缺陷的嫌疑程度。
其中,fail(s)以及pass(s)是执行了语句单元s的失败和通过的测试用例数量,fail和pass是全部的失败和通过的测试用例的数量。
Tarantula算法在更细的划分上属于基于软件运行频谱(spectra-based)的缺陷定位算法研究,这类算法以程序中语句单元在软件测试过程中的覆盖情况(是否被执行过)与对应的测试结果作为分析的材料,来判断语句单元存在缺陷的嫌疑度。在Tarantula之后又有许多这类算法被相继提出,有学者对这些算法的效果进行过比对,其中表现较好的算法有Jaccard,原应用于生物领域的Ochiai,以及特别为面向对象程序设计的Ample。
除了基于软件运行频谱,学术界还开辟了利用其他信息进行缺陷定位的方法,其中研究较为深入的有基于谓词的缺陷定位算法。基于谓词的缺陷定位算法通过在代码中的谓词(如条件判断)所在处设置监测点,分析谓词执行结果的不同与运行结果之间的关系来判定缺陷存在的位置。
经典的基于谓词的缺陷定位算法有CBI、SOBER,它们证明了根据谓词的执行结果能够有效地进行缺陷定位。此后的学者又进行了很多相关的研究,比如从谓词的执行信息量(是指程序运行时谓词实际可以提供信息的数量)和利用强度(对谓词执行信息提取多与少的衡量)入手,提高了定位准确率的Pesla和Safla算法。
然而这些算法或多或少都存在一些问题。比如基于软件运行频谱的缺陷定位算法,并没有将语句单元执行次数这一重要信息加入考虑,然而在实际运行中,存在缺陷的语句单元的执行次数却往往与软件的运行结果有着较高的相关性。
比如,一个有一定几率会触发缺陷的语句单元,如果该缺陷会立即造成软件运行终止,那么这个语句单元在失败的测试用例运行中应该执行次数是更低的;如果该缺陷不会立即造成软件运行终止,而是在软件完整运行后缺陷才能被触发,那么那些执行了更多次该语句单元的测试用例,结果失败的比例将会更高。
而无论是这两种情况中的哪一种,在正确与错误的软件运行中,语句单元的执行次数都将会出现明显的差别。基于软件运行频谱的缺陷定位算法无法从这种区别得出有利于提高定位结果的信息,因为对这些算法来说,这个语句单元在两种结果的运行中都是“执行过”,不存在差别。
基于谓词的算法也同样存在一些不足。比如,这类算法定位的精确度将取决于谓词的密度,如果谓词密度很小,或是缺陷前后并不存在谓词,则难以根据谓词找到缺陷位置。此外在函数互相调用比较复杂的位置,仅指出某一谓词前后可能存在缺陷,有时并不能判断缺陷究竟存在于哪个函数。基于谓词的算法在验证有效性时,往往需要剔除那些不能明确与缺陷进行联系的谓词,这也在一定程度上说明这类算法在实际使用中的局限性。
缺陷的定位粒度也是一个值得研究的课题,绝大多数的研究中,定位粒度被设定为语句。然而这些将定位粒度精确到语句的定位算法,在实际使用中的价值往往有限。因为开发人员不可能根据单一的语句来判断其是否存在缺陷,充分地了解该语句的上下文在这个过程中是极其必要的,然而这些算法在验证过程中往往忽略了这一点。比如一个程序有三百行代码,缺陷位于第二百行。使用一种缺陷定位算法后,该语句的嫌疑度排名位于第一百行。那么将该缺陷定位算法的实际效果声称为减少了百分之五十的代码检查量是不合理的,因为实际调试过程中,开发者不可能根据单一的语句来评判其是否存在缺陷,阅读该语句的上下文是无法避免的工作。所以这类算法实际的实用价值要比数据体现的更低。
为了寻找到更加合适的定位粒度,有些学者将目光放在了更大的语句粒度上,如以文件片段和类为单位进行缺陷定位。使用这种较大的语句单元粒度,让用户一般不需要阅读额外的上下文,也没有对语句单元的结构、类型上的限制,使其理论效果的可信度更高。但过大的粒度却可能迫使用户阅读大量无关缺陷的代码,降低了这类算法的使用价值。因此,为了解决现有缺陷定位方法中存在的这些问题,本发明做出了相应的改进,将语句单元执行次数、函数的参数值和返回值在算法中加以利用,并设定函数作为定位粒度的大小,以求得到定位结果更加准确、也更适于实际使用的缺陷定位算法。
发明内容
本发明的目的是为了解决经典缺陷定位方法中存在的问题,提出一种基于函数调用记录的缺陷定位方法及装置,从实用性出发,充分利用函数调用记录的各种信息,获得更为准确的缺陷定位结果。
本发明的思想是利用面向切面编程AOP(Aspect-Oriented Programming)技术,针对每一个测试用例获取被测程序的函数调用记录,并以函数为单位对该记录进行统计分析,根据函数在测试用例运行通过和失败两种情况下测试用例覆盖率(执行该函数的通过/失败测试用例占所有通过/失败测试用例的比例)、执行次数(测试用例中执行该函数的次数)、数据传递(执行该函数时传入的参数值与返回的返回值)、调用关系(函数间的调用关系)上的差异,计算得到其基础嫌疑值、执行次数差异值、数据传递差异值及调用关系差异值,然后根据这些值计算出函数的最终嫌疑值,最后对所有函数的缺陷嫌疑值进行排序即可得到缺陷定位结果。
本发明的目的是通过以下技术方案实现的:
一种基于函数调用记录的缺陷定位方法,包括以下步骤:
步骤一、对被测程序执行测试用例,获取函数调用记录文件;
步骤二、读取函数调用记录文件,将函数调用信息整理为后续分析需要的格式;
步骤三、根据函数在通过与失败两种测试用例中各自的覆盖率,计算函数的基础嫌疑值;
步骤四、根据函数在通过与失败两种测试用例中执行次数的差异,计算函数的执行次数差异值;
步骤五、根据函数在通过与失败两种测试用例中数据传递的差异,计算函数的数据传递差异值;
步骤六、根据函数在通过与失败两种测试用例中调用关系的差异,计算函数的调用关系差异值;
步骤七、将函数的基础嫌疑值、执行次数差异值、数据传递差异值以及调用关系差异值相结合,计算得到函数最终的缺陷嫌疑值;
步骤八、根据函数的缺陷嫌疑值对函数由高到低排序后输出。
一种基于函数调用记录的缺陷定位装置,包括函数调用记录生成模块、函数调用记录读取模块、调用记录分析模块、缺陷嫌疑度计算模块以及排名输出模块;所述调用记录分析模块包括基础嫌疑值分析模块、调用次数差异分析模块、数据传递差异分析模块和调用关系差异分析模块;这些模块的作用如下:
所述函数调用记录生成模块用于在使用测试用例对被测程序进行测试时生成函数调用记录文件;
所述函数调用记录读取模块用于从函数调用记录文件中读取并整理函数调用记录,获取被测程序每一个函数在各测试用例执行时的相关信息,以便之后利用其进行分析;
所述调用记录分析模块用于根据整理后的函数调用记录对函数在程序运行正确和错误两种情况下表现的差异进行分析;其中,基础嫌疑值分析模块用于根据函数在通过与失败测试用例中的覆盖情况计算函数的基础嫌疑值;执行次数分析模块用于根据函数在不同测试用例运行期间执行次数的差异计算函数的执行次数差异值;数据传递分析模块用于根据函数在不同测试用例运行期间被调用时传入的参数值和返回值的差异计算函数的数据传递差异值;调用关系分析模块用于根据函数在不同测试用例运行期间所处调用关系的差异计算函数的调用关系差异值。
所述缺陷嫌疑度计算模块用于根据所述调用记录分析模块的分析结果,为每个函数计算最终的缺陷嫌疑值;
所述排名输出模块用于首先根据所述缺陷嫌疑度计算模块输出的所有函数的缺陷嫌疑值进行排序得到缺陷定位结果,然后输出该缺陷定位结果。
有益效果
本发明方法和装置,对比已有技术,能够在不改变现有测试流程的前提下,方便地进行缺陷定位,并具有更高的定位准确度。
附图说明
图1为本发明实施例基于函数调用记录的缺陷定位方法流程示意图。
图2为本发明实施例生成函数调用记录文件流程示意图。
图3为本发明实施例读取函数调用记录文件流程示意图。
图4为本发明实施例基于函数调用记录的缺陷定位装置结构示意图。
图5为本发明实施例实际差异程度与差异值关系示意图。
图6为本发明实施例试验结果中本发明方法与Combine及Upper算法在replace上的运行结果对比示意图。
具体实施方式
下面将结合附图和实施例对本发明加以详细说明,同时也叙述了本发明技术方案解决的技术问题及有益效果,需要指出的是,所描述的实施例仅旨在便于对本发明的理解,而对其不起任何限定作用。
实施例1
如图1所示,基于函数调用记录的缺陷定位方法,包括以下步骤:
步骤一、对被测程序执行测试用例,生成函数调用记录;
所述函数调用记录可以采用文件、数据库或内存形式以及其它任何可被识别的形式存储,本实施例采用测试时惯用的文件形式存储。
所述函数调用记录包括被测程序函数调用的相关信息,如函数签名、传入参数、返回值、测试用例执行成功或失败等等。
所述一个函数调用记录对应一次测试用例执行。
举例来说,可以通过如图2所示过程生成函数调用记录:
本示例采用AOP技术,在不更改传统测试流程的情况下,通过预编译方式和运行期动态代理实现在不修改原代码的情况下生成函数调用记录,具体如下:
编写调用捕获AOP文件,该文件由2部分内容组成:设置切入点以及切入点处应完成的功能;
首先需要获取函数调用记录作为缺陷分析的原材料,函数调用记录是记录着程序运行期间程序内部函数调用相关信息的记录文件,包括每次函数调用所在的线程号、被调用函数的签名信息、调用中传入的参数值以及调用结束后返回的返回值,这些信息按发生的先后顺序依次存储于调用记录文件中。函数调用记录文件中一条信息的格式如下:
Thd>Sig|arg1|arg2|…|argn
或
Thd<Sig|rtn
其中,前者为调用记录,表明这一时刻签名为Sig的函数在线程Thd中被调用,并传入n个值为arg1到argn的参数值(如果没有传入参数则这部分空缺);后者为返回记录,表明这一时刻签名为Sig的函数在线程Thd中执行完毕,并返回值为rtn的返回值(如果没有返回值则这部分空缺)。
在软件测试过程中,针对每一个测试用例,在其执行过程中都应生成一个函数调用记录文件,用以记录该测试用例运行过程中的函数调用信息。为了尽可能地不影响测试人员原本的测试流程,提高该系统的易用性,此处选用面向切面编程(AOP)技术,在无需修改源代码的前提下,在测试过程中自动生成函数调用记录文件。
使用AOP技术在测试期间自动生成函数调用记录文件的具体方法如下:
在切面文件中,设置3个切入点(pointcut,用于截取指定类型的函数的调用、执行或返回,并在这些动作发生前后插入定制代码):用于截取测试用例入口函数执行的切入点pointcutTestCase、用于截取被测程序所有函数执行的切入点pointcutFunction、和用于截取代表测试用例执行失败的函数执行的切入点pointcutFail。
在以上切入点处分别完成以下功能:
●当pointcutTestCase截取的函数执行时,生成一个对应该测试用例调用记录文件,准备写入调用记录。
●当pointcutFunction截取的函数执行时,在文件中写入该函数调用记录,记录包括线程号、函数签名、传入参数值。
●当pointcutFunction截取的函数返回时,在文件中写入函数返回记录,记录包括线程号、函数签名、返回值。
●当pointcutFail截取的函数执行或返回时,根据参数值、返回值(取决于具体函数是什么)来判断测试用例执行状态,如果执行失败,则在调用记录文件进行标记。如果pointcutTestCase截取的函数以异常形式返回,也记为用例执行失败,关闭文件。
●当pointcutTestCase截取的函数正常返回时,关闭文件。
然后,将实现了上述功能的切面文件与被测源程序进行编织,得到编织后的被测程序。
最后,对编织后的被测程序进行编译,运行测试用例,得到每个测试用例对应的函数调用记录文件。
步骤二、读取函数调用记录文件,获取函数调用记录;
一轮测试活动结束后,将生成一系列对应每个测试用例的函数调用记录文件。在对程序进行缺陷分析之前,需要将这些函数调用记录文件读入系统,并整理为易于分析的格式。
首先创建一个函数调用记录列表recordList,该列表存储所有函数调用记录文件对应的函数调用记录,它的形式如下:
recordList={record1,record2,…,recordn}
其中,recordi是对应于第i个测试用例(或函数调用记录文件)的函数调用记录,i为介于1~n之间的自然数,n为测试用例(或函数调用记录文件)的总个数。
函数调用记录record包括一个测试用例执行过程中自身所有函数的所有执行信息的序列,其中每次执行信息又包括函数签名、传入参数值和返回值。可以形式化的将函数调用记录表示如下:
record={e1,e2,…,en,rst}
其中ei为该测试用例运行中的第i个函数调用信息,rst为该测试用例运行的结果(通过或失败),i为介于1~n之间的自然数,n表示该测试用例运行中被测程序中函数被调用的总次数。函数调用信息e又可表示如下:
e={sig,arg1,arg2,…,argn,rtn}
其中sig为函数签名,argi为该次调用传入的第i个参数的值,i为介于1~n之间的自然数,n为该函数的参数个数,rtn为该次调用结束后的返回值。
由于还未读取任何函数调用记录文件,此时它是一个空的列表。
之后,依次读取每个函数调用记录文件,其中每一个函数调用记录文件的读取过程如下:
创建一个新的函数调用记录record,首先根据函数调用记录文件中存储的测试用例运行结果信息,为其rst字段赋值;然后,如图3所示,通过下述过程获取函数调用信息:
创建一个调用栈池,其中的调用栈以线程名为键值,一个线程对应一个调用栈,调用栈池的形式如下:
stackPool={[thd1,stack1],[thd2,stack2],…,[thdn,stackn]}
其中thdi为第i个线程的线程号,stacki为其对应的调用栈,i为介于1~n之间的自然数,n为该调用记录文件对应的测试用例在运行中所创建的线程数量。其中stack的格式如下:
stack={e1,e2,…,en}
其中ei为上文中定义的函数调用信息,i为介于1~n之间的自然数,n为该调用栈中现存的函数调用信息个数。作为一个栈,调用栈只能从后端压入或弹出函数调用信息。从逻辑上来说,ei被ei-1调用,同时又调用ei+1。
调用栈池创建完毕后,依次读取文件中的所有记录,对每一条记录执行下述工作:
1.根据该条调用记录中的线程名,在调用栈池中取出对应的调用栈;
2.如果是函数执行记录,则创建一个函数调用,存入函数签名、参数值等信息,压入调用栈;
3.如果是函数返回记录,则比对这条记录中的函数签名与调用栈顶的函数调用中存储的函数签名是否一致,如果一致,弹出栈顶的函数调用,存入返回值信息,并将这个函数调用信息e存入函数调用记录record;如果不一致,说明调用记录文件存在错误,终止本发明缺陷定位过程;
重复上述工作,直到该函数调用记录文件读取完毕。最后,将函数调用记录record存入函数调用记录列表recordList。
所有函数调用记录文件读取完毕后,分析所需的全部信息即存入函数调用记录的列表recordList,之后根据各种分析的需要,从中提取所需信息,整理为相应的格式即可。
步骤三、根据函数在通过与失败两种测试用例中各自的覆盖率,计算函数的基础嫌疑值;
基于频谱的缺陷定位算法非常之多,在近十几年间已涌现出数十种。这些算法以一种简单朴素的思想——在失败的测试用例运行中出现次数更多的代码块被视为更可能存在缺陷(在通过的测试用例中反之),通过不同的计算方式计算出代码块的嫌疑值。
这些算法证明了通过这种简单的信息、简单的计算方式,就能得到代码块的大致缺陷嫌疑信息。虽然对于更高的准确度要求来说,这样简单的计算并不总是能满足要求,但将其作为一个基础值,并根据其他信息进行调整,则是一个比较适合的选择。因此,本算法首先借鉴基于频谱的缺陷定位方法的思想,利用函数在通过和失败的测试用例中的覆盖情况,计算出一个基础值,这个基础值在后续的步骤将接受其他因素带来的调整。
首先定义测试用例集合T:
T={t1,t2,…,tn}
其中ti(1≤i≤n,i为自然数,n为全部测试用例的数量)为集合中的第i个测试用例。对于每个测试用例t,均对应一个执行结果——通过或失败,所有结果为通过的测试用例组成通过测试用例集合Tp,所有结果为失败的测试用例组成失败测试用例集合Tf。
Tp={t|t∈T∧result of t=pass}
Tf={t|t∈T∧result of t=fail}
对于一个待测程序,其所包含的所有的函数组成一个集合F:
F={f1,f2,…,fn}
其中fi(1≤i≤n,i为自然数,n为程序所含有的全部函数的数量)为程序中的第i个函数。
对于一个测试用例ti,其在运行过程中会执行若干个函数,这些函数组成了测试用例ti的覆盖信息列表Coverage(ti):
Coverage(ti)={fi1,fi2,…,fim}
其中fij(1≤i≤n,1≤j≤m,i、j为自然数,n为测试用例的总数量,m为第i个测试用例在运行中所执行过的全部函数的数量)为第i个测试用例在运行中所执行过的函数中的第j个函数。
对于一个函数fi,运行过该函数的通过测试用例和失败测试用例分别又构成集合Tp(fi)和Tf(fi):
Tp(fi)={t|t∈Tp∧fi∈Coverage(t)}
Tf(fi)={t|t∈Tf∧fi∈Coverage(t)}
那么对于每一个函数fi,执行了该函数的通过测试用例数量pass(s),执行了该函数的失败测试用例数量fail(s)为:
pass(s)=|Tp(fi)|
fail(s)=|Tf(fi)|
全部通过测试用例数量totalPass和totalFail:
totalPass=|Tp|
totalFail=|Tf|
其中|A|代表集合A中元素的数量。
那么根据这四个值,可以计算出函数f的基础嫌疑值basic:
基础嫌疑值大致表示了函数f存在缺陷的嫌疑值。从公式内容可以看出,一个函数,如果在失败的测试用例中出现越多,在成功的测试用例中出现越少,那么它就越有存在缺陷的嫌疑。因为一个测试用例如果失败,那么它必须执行过至少一次存在缺陷的函数,所以基础嫌疑值的计算方式符合对缺陷表现的直观印象。
基础嫌疑值所能达到的最大值为1,其未达到1的部分将接受后续的调整。
步骤四、对函数调用记录进行调用次数差异分析,获取调用次数差异值;
不仅通过和失败测试用例中的覆盖信息非常重要,函数在这些用例中执行次数的多少也是非常有价值的缺陷分析信息。一个函数,如果在通过与失败的测试用例中执行次数有着明显的差异,也就预示着它内部有很大的可能存在缺陷。
对于每一个测试用例t,其在运行过程中每一次调用的函数,组成了该测试用例的函数调用序列Sequence(t)
Sequence(t)=(f1,f2,…fn)
其中fi(1≤i≤n,i为自然数,n为测试用例t在运行过程中所执行过函数的总次数)为测试用例t在运行中第i次执行的函数。
对于函数f,计算出其在每个测试用例ti中执行的次数time(f,ti),将其组成f的执行次数序列TimeList(f):
TimeList(f)=(time(f,t1),time(f,t2),…,time(f,tn))
从执行次数序列中,分别提取出通过执行次数序列TimeListp(f)和失败执行次数序列TimeListf(f)。
TimeListp(f)=
{time(f,ti)|time(f,ti)∈TimeList(f)∧ti∈Tp∧time(f,ti)>0}
TimeListf(f)=
{time(f,ti)|time(f,ti)∈TimeList(f)∧ti∈Tf∧time(f,ti)>0}
根据两个执行次数序列,计算函数f的通过执行密度Densityp(f)和失败执行密度Densityf(f):
其中Lp、Lf分别表示TimeListp(f)和TimeListf(f)的长度。
执行密度代表了函数在各测试用例中执行频率的大致水平,因为一次测试用例的运行中,一个函数执行次数多少只可能影响该次测试用例的结果,所以此处用先连乘再开方的办法削弱单次过多或过少执行次数对最终数值的影响。
得到通过执行密度和失败执行密度后,再通过计算熵的方式,计算函数的执行次数差异值ExeTimeDif:
ExeTimeDif=-Pp*log2Pp-Pf*log2Pf
函数的执行次数差异值取值范围为0到1之间,取值越大,代表函数在通过和失败的测试用例运行期间执行的次数差异越大,也就预示着更有存在缺陷的可能。
步骤五、对函数调用记录进行数据传递差异分析,获取数据传递差异值。
函数作为能实现特定功能的独立代码块,在有输入参数的情况下,其执行的情况往往依赖于这些输入参数,以至于不同的输入参数会带来不同的运行结果。假如一个函数的输入参数的不同取值之间,与程序的运行结果有着较高的关联度,则意味着该函数的输入参数值在一定取值范围内会造成程序的运行错误,也就意味着函数内存在着特定参数值才会触发的缺陷。
所以,通过对函数的数据传递分析,也就是对函数输入参数值、返回值与程序运行结果之间的关联性进行分析,得到表示这种关联性的数据传递差异值,即可将其用于缺陷定位分析,发现由数据传递而触发的缺陷。
此处使用信息增益率来量化函数的数据传递与测试用例执行结果之间的关联度大小。
首先,对于每一个测试用例t,定义其数据传递信息列表DataInfoList(t)格式如下:
DataInfoList(t)=(di1,di2,…,din)
数据传递信息列表记录一个测试用例在执行过程中,所有函数被调用时的传入参数值,以及函数执行结束后的返回值信息。其中dii(1≤i≤n,i为自然数,n为测试用例t在执行过程中调用函数的总次数)为测试用例t在运行中第i次调用函数时该函数的数据传递信息。数据传递信息di形式如下:
di={f,arg1,arg2,…,argn,rtn}
其中f为该次调用中所调用的函数,f∈F,argi(1≤i≤n,i为自然数,n为该函数的参数值个数)为该次调用中传入的第i个参数的值,rtn为被调用函数执行结束后的返回值。
对于每一个函数f∈F,依次在每一个测试用例的数据传递信息列表中提取该函数的数据传递信息,结合该测试用例的运行结果,生成该函数的调用信息列表CallInfoList(f):
CallInfoList(f)={c1,c2,…,cn}
其中每一条调用信息ci(1≤i≤n,i为自然数,n为函数f在所有测试用例的运行过程中被调用的总次数)记录了该次调用时函数的传入参数,返回值以及所在测试用例的运行结果信息。其具体格式如下:
c={arg1,arg2,…,argn,rtn,rst}
其中argi(1≤i≤n,i为自然数,n为该函数的参数值个数)为该次调用中传入的第i个参数的值,rtn为被调用函数执行结束后的返回值,rst为该次调用所在的测试用例的最终运行结果(成功或失败)。
之后,对于每一个函数,以它的调用信息列表作为数据集S,以测试用例的运行结果rst作为标签类,以每一个参数argi以及返回值rtn依次作为属性,计算它们的信息增益率。
首先计算这个数据集S的熵。
Entropy(S)=-pp*log2(pp)-pf*log2(pf)
其中S为该函数的调用信息列表,pp和pf分别为运行结果为通过和失败的调用信息条目在调用信息列表所占的比例。
之后依次将该函数的参数值或返回值作为属性A,根据属性值将调用信息列表中的条目划分为若干个子集合:
S1:(pp1,pf1),S2:(pp2,pf2),…,Sn:(ppn,pfn)
Si表示第i个分组,其中每一个分组中的所有函数调用信息,它们的当前分析的那个参数值或返回值是相等的;而任意两个分组中的函数调用信息,它们的当前分析的那个参数值或返回值都是不等的。ppi和pfi分别表示第i个分组中测试用例运行结果值为通过和失败情况下函数运行占该组全部条目个数的比例;1≤i≤n,i为自然数,n表示分组的个数(也即当前考察的参数值或返回值所拥有不同取值的个数),n≥1。
然后计算各子集合的熵,接着根据集合S与各子集合Si的熵,计算该属性A(参数或返回值)的信息增益:
其中,|S|表示集合S的元素个数,|Si|表示第i组元素的个数,n表示分组的个数,Entropy(S)表示集合S的熵,Entropy(Si)表示子集合Si的熵,1≤i≤n,i为自然数。
之后,根据集合S与各子集合Si中元素的个数,计算该属性A的分裂信息度量:
分裂信息度量的实质就是以参数或返回值为属性计算得到的调用集合的熵,用它来修正信息增益,可避免集合中元素过于分散时信息增益过高的现象。
将信息增益除以分裂信息度量,得到属性的信息增益率:
一个函数针对每一个参数值和返回值,都会计算得到一个信息增益率值。因为缺陷的触发有时仅与多个参数中的一个相关,所以为了避免无关参数或返回值对分析结果造成影响,取所有信息增益率中最高者,作为该函数的信息传递差异值DataDif。如果函数并没有参数值和返回值,其数据传递差异值为0。
步骤六、对函数调用记录进行调用关系差异分析,获取调用关系差异值;
除传入的参数值外,函数的运行状况还与许多其他因素有关,比如函数执行的时机或函数间调用的顺序等。这些不可预知的因素难以一一进行监测,但它们往往与函数之间的调用关系,也就是函数在调用栈中所处的位置有着一定的联系。因此,通过考察函数之间的调用关系与测试用例的运行结果之间的联系,就有机会找到更可能存在缺陷的函数。
为了判定函数中存在这种缺陷的可能性,需要量化函数的不同调用关系与测试用例执行结果的关联程度,这里依然选择信息增益率作为量化标准。
首先,对于每一个测试用例t,都存在一个调用关系信息列表
CallRelationList(t):
CallRalationList(t)={cr1,cr2,…,crn}
其中每一条调用关系信息cri(1≤i≤n,i为自然数,n为测试用例中调用函数的总次数)记录了该次调用中的被调用函数,以及调用该函数的函数。其具体格式如下:
cr={caller,callee}
其中caller为本次调用中的调用函数,而callee为本次调用中的被调用函数。一个条目的存在就代表在测试用例的执行中,caller函数对callee函数进行了一次调用。如果callee函数没有调用者(比如入口函数),caller此处为空。
有了所有测试用例中的调用关系信息列表后,针对每一个函数f,提取其所有的调用关系。所提取的信息分为两种,一种是在所有该函数作为被调用者
callee的调用,以及在所有该函数作为调用者caller的调用。这两类信息分别构成两个列表,AsCallerList(f)与AsCalleeList(f):
AsCallerList(f)={cle1,cle2,…,clem}
AsCalleeList(f)={clr1,clr2,…,clrn}
其中clei或clrj(1≤i≤m,1≤j≤n,i、j为自然数,m为所有测试用例运行中的全部以函数f作为调用者的调用的出现次数,n为所有测试用例运行中的全部以函数f作为被调用者的调用的出现次数)分别为该次调用中的被调用函数或调用函数(可能为空),加上该调用所在测试用例的运行结果:
cle={callee,rst}
clr={caller,rst}
其中callee与caller的意义与上文中定义的一致,分别为该次调用中的被调用函数以及调用函数,rst为该次调用所在测试用例的最终运行结果(通过或者失败)。
因为由函数在调用关系中所处的位置而触发的缺陷本身存在多种情况,比如在作为调用者时,其内部的缺陷让它在错误的时机调用一个本身并没有缺陷的函数,从而导致程序运行错误,或者在作为被调用者时,其内部的缺陷仅在特定函数对其进行调用时被触发(这可能是由于这些函数传入了会触发错误的参数)。所以,对在调用关系中处于不同位置的情况,要进行分别的考察。
分别以AsCallerList(f)和AsCalleeList(f)为数据集,以测试用例的运行结果rst作为标签类,以被调用函数callee或调用函数caller作为属性,计算它们的信息增益率。计算过程与计算数据传递的信息增益率时一致,在此不再赘述。
基于上面的计算,一个函数在作为调用者和被调用者时,会分别得到一个信息增益率值。取其中值最大者,作为该函数的调用关系差异值CallDif。
步骤七、计算函数缺陷嫌疑值;
缺陷定位的输出结果是一个依据函数中存在缺陷的可能性排序的一个函数列表,为了生成该列表,必须有一个统一的标准来衡量各函数存在缺陷的嫌疑程度。因此,在得到各个函数的基础嫌疑分值basic、执行次数差异值ExeTimeDif、信息传递差异值DataDif以及调用关系差异值CallDif后,还需要将其结合成为最终嫌疑值Final。
为便于说明,这里再次列出基础嫌疑值的公式:
可见,这个分值由两部分组成,第一部分为执行过该函数的失败测试用例在全部失败测试用例中的比例,第二部分为执行过该函数的通过测试用例在全部通过测试用例中的比例。
一个测试用例运行的失败,必然由其触发的缺陷所导致。也就是说,一个与缺陷相关的函数,必定有失败用例执行过它。而且,执行过该函数的失败测试用例越多,也就证明它与测试用例的失败关联性越高,也就更有存在缺陷的嫌疑。因此,基础嫌疑值中的第一部分对取值起着最主要的影响。
然而这并不能成为判断函数存在缺陷可能性的唯一标准,因为程序中存在着很多并不存在缺陷,但会经常被调用的函数,如果仅仅考虑基础嫌疑值的第一部分的话,这些函数会得到与缺陷函数相同甚至更高的值。所以仍需将其他因素加入考虑。
对于一个执行就一定会触发缺陷的函数,很明显,执行过它的测试用例就一定会失败,那么就不会存在运行过它而结果为成功的测试用例;对于在执行时只是有一定几率触发缺陷的缺陷函数,执行过它的通过测试用例,相较于其他函数而言也应该是更少的。
所以,在基础嫌疑值中,以执行过函数的通过测试用例占全部通过测试用例的比例作为次要评判依据。在第一部分相同的情况下,该比例值越小,基础嫌疑值越大。
基础嫌疑值的计算是基于一种简单的、理想化的假设,它能给予真正存在缺陷的函数较高的分值,但对于更高的定位准确度要求而言,仅靠基础嫌疑值并不够,还需要根据其他因素在基础嫌疑值的基础上进行调整。
根据公式可以看出,函数的基础嫌疑值最大值为1,基础嫌疑值到1之间的这个空间,可以根据其他因素(三个差异值)进行调整,从而得到更能真实体现函数存在缺陷可能性的分值。以公式来表示,也就是:
AdjustedBasic=basic+(1-basic)*adjustment
其中adjustment是以一定方式得到的调整值。
可以看出,仅能对基础嫌疑值不足1的部分进行调整,且调整幅度的大小正比于这部分空间的大小。而如果基础嫌疑值已经达到1,就不再对其进行调整。这样使用基础嫌疑值的原因有两个。
第一点,一个函数,如果所有执行过它的测试用例都失败了,而所有没有执行过它的函数都运行通过,那么它将得到最大的基础嫌疑值1。这是因为它的执行与否已经与测试用例运行结果关联到最大程度——只要该函数执行测试用例就会失败,只要该函数不执行测试用例就会通过。所以,给这样的函数最高可能的嫌疑值是合理的,并且也无需再对其嫌疑值进行任何调整。
第二点,本算法将使用的三个用于调整最终嫌疑值的数值:调用次数差异值、数据传递差异值、调用关系差异值,都是由函数在通过、失败测试用例的运行间表现的差异计算得到的,如果函数仅在一种运行结果的测试用例中执行过,该差异就无法计算得出。而以上述方式调整嫌疑值分数,在函数执行仅存在于一种测试用例的情况下恰好无需用到差异值(在函数仅存在于失败测试用例中时,基础嫌疑值已达到最大值1,没有调整空间;在函数仅存在与成功测试用例中时,函数不存在含有缺陷的可能)。
在基础嫌疑值上所进行的调整的幅度,取决于该函数的三个差异值,然而因为各个差异值之间,以及差异值与基础嫌疑值之间,计算的依据不同,分值大小的评定标准也不同,难以进行简单累加或相乘。因此,对于每一个差异值dif,要通过一定的计算,得到调整后差异值,以便各个差异值与基础嫌疑值能够相结合。定义调整后差异值形式如下:
Adjusted(dif)=x*dify
其中dif为运行次数差异值ExeTimeDif、数据传递差异值DataDif以及调用关系差异值CallDif中的一种。x、y为两个待定系数。
加入x系数的原因是,用x来控制该差异值所最大能进行调整的空间有多大,因为差异值dif的最大值为1,所以调整后差异值的最大大小其实由x来决定,也就是调整后差异值最大将被限定为x。
加入y系数的原因是,如以实际差异程度reality为自变量,差异值dif为因变量,那么函数dif=f(reality)的曲线如图5所示,从整体上来看,实际差异程度越高,差异值越大,差异值大小能正相关地体现实际差异程度。然而多大的实际差异程度转化为多大的差异值,这个标准是难以界定的。因此,调整后的差异值对差异值进行y次方计算,在曲线的两个端点不变(也就是没有差异时差异值为0,差异程度最大时差异值为1)的前提下,用y来调整图中函数曲线的形状(也就是实际差异程度与差异值之间的转化关系),以求更合理地使用差异值对最终嫌疑值进行调整。
确定差异值的调整方式后,需要确定如何将三种调整后差异值与基础嫌疑值相结合。因为没有明确的理由说明三种差异(执行次数间的差异、数据传递间的差异、调用关系间的差异)之间有着重要性上的区别,所以对于三种差异,在结合过程中应予以同等地位。所以,用以下方式来结合基础嫌疑值与三个调整后的差异值:
AdjustedBasic=basic+(1-basic)*Adjusted(ExeTimeDif)
Temp=AdjustedBasic+(1-AdjustedBasic)*Adjusted(DataDif)
Final=Temp+(1-Temp)*Adjusted(CallDif)
上述公式可以理解为,每一步都是对当前的嫌疑值(最开始是基础嫌疑值)不足1的部分,使用一个调整后的差异值与之相乘,再与当前嫌疑值累加,累加后的和作为新的嫌疑值,反复进行上面的步骤三次(因为只有三个差异值),最后得到最终的缺陷嫌疑值。
可以证明,即使交换三个调整后嫌疑值的位置,运算结果依然不变,也就保证了它们在运算中所处的实际地位相同。
确定最终嫌疑值的公式之后,仍要解决待定系数的选定问题。对于每个调整后差异值Adjusted(dif)来说,有两个系数需要选定,分别记为x(dif)与y(dif),所以对于最终嫌疑值Final来说,共有六个系数需要选定。通过对比多种系数组合在算法实际使用中的效果,最终选定如表所示的系数组合:
表1 最终选定系数
差异值\参数 | x(dif) | y(dif) |
ExeTimeDif | 1 | 0.5 |
DataDif | 0.5 | 1 |
CallDif | 1.43 | 0.125 |
步骤八、排名并输出函数缺陷定位结果。
依据各个函数最终的缺陷嫌疑度,按从大到小的顺序将所有函数进行排序,即为本发明方法最终给出的函数缺陷嫌疑度排名列表;然后采用如文件输出或控制台输出等需要的输出形式将其输出给使用者,以辅助指导调试过程。
实施例2
如图4所示为本发明提出的一种基于函数调用记录的缺陷定位装置结构示意图,包括函数调用记录生成模块、函数调用记录读取模块、函数调用记录分析模块、缺陷嫌疑度计算模块以及排名输出模块;所述函数调用记录分析模块包括基础嫌疑值分析模块、调用次数差异分析模块、数据传递差异分析模块和调用关系差异分析模块;
所述函数调用记录生成模块用于在使用测试用例对被测程序进行测试时生成函数调用记录文件;
所述函数调用记录读取模块用于从函数调用记录文件中读取并整理函数调用记录,获取被测程序每一个函数在各测试用例执行时的相关信息,以便之后利用其进行分析;
所述调用记录分析模块用于根据整理后的函数调用记录对函数在程序运行正确和错误两种情况下表现的差异进行分析;其中,基础嫌疑值分析模块用于根据函数在通过与失败测试用例中的覆盖情况计算函数的基础嫌疑值;执行次数分析模块用于根据函数在不同测试用例运行期间执行次数的差异计算函数的执行次数差异值;数据传递分析模块用于根据函数在不同测试用例运行期间被调用时传入的参数值和返回值的差异计算函数的数据传递差异值;调用关系分析模块用于根据函数在不同测试用例运行期间所处调用关系的差异计算函数的调用关系差异值。
所述缺陷嫌疑度计算模块用于根据所述调用记录分析模块的分析结果,为每个函数计算最终的缺陷嫌疑值;
所述排名输出模块用于首先根据所述缺陷嫌疑度计算模块输出的所有函数的缺陷嫌疑值进行排序,然后输出函数排名结果。
所述输出方式可以是文件输出或控制台输出。
测试结果:
试验一:
首先选择同样以函数作为定位粒度的COMBINE和UPPER算法作为比较。这两种算法通过比较正确运行与失败运行中条件判断语句执行结果的差别,应用数据挖掘技术,分析得到各个函数存在缺陷的嫌疑程度。
实验选择在replace测试集上进行。replace是Siemens测试集的一个子项目,Siemens测试集是缺陷定位领域应用最为广泛、认可度最高的测试集。Siemens测试集被西门子研究院用于研究控制流和数据流覆盖准则的缺陷检测能力,它使用C语言编写,共有7个子项目,分别为航空器防撞系统tcas、优先级调度器schedule和scedule2、统计信息生成模块tot_info、词法分析器print_tokens和print_tokens2以及模式匹配和替换器replace。这几个子项目规模均比较小,其中属replace相对最大,共有512行代码,20个函数,拥有5542个测试用例,最为适合用做以函数为粒度的缺陷定位的效果验证。
replace共有32个版本,这里选择和所对比论文中一致的30个版本进行对比(其中第27个版本未在原论文中被选用),对比结果如表所示。表中的数字代表真正存在缺陷的函数在使用相应算法得到的排名中的位置,也即用户发现真正缺陷所需检查的函数数量。其中版本12因缺陷不存在于函数中(存在于宏中),所以选取与缺陷相关联的函数中排名最高的makepat为缺陷函数。
表2本发明方法与Combine及Upper算法在replace上的运行结果表
根据表2中的数据可知,在30个版本中的19个版本里(灰色背景的单元格),本发明方法的结果均优于对比论文中提出的两种算法,比例达到63.3%。从发现缺陷平均所要检查的函数数量上,本发明方法(2.87)也要优于所对比的两种算法(3.6和3.17)。
如图6所示为本发明方法与Combine及Upper算法在replace上的运行结果对比示意图,图中的横坐标为发现缺陷所要检查的函数数量占总函数数量的百分比值,纵坐标为所发现的缺陷占总缺陷数量的百分比值。很明显,对应曲线越陡、越向左上角靠近,算法的效果也就越好。可以发现,本发明方法在检测15%的函数的情况下,就可以发现高达80%的缺陷,显著高于COMBINE算法的63.3%和UPPER算法的66.7%。
实验二
基于频谱的缺陷定位是缺陷定位领域内的一大热点,因此与该类别中的优秀算法进行比较显得尤为必要。基于频谱的算法在验证时大多选用语句或基本块(一定会一起执行的若干条语句)为定位粒度,然而从相关文献的表述中可以发现,这些算法的定位粒度并不仅仅局限于语句和基本块。大多数算法在描述时都以代码块(code blocks)或程序实体(program entities)来作为分析单元,这意味着不同大小的粒度都可以使用这些算法来进行定位。许多学者也已经进行了这样的尝试,比如Santelices等人将Tarantula算法在语句、分支、定义\值对三个不同的粒度上分别实验了算法效果,Bo Jiang等人对比了Tarantula、CBI、Jaccard、Ochiai算法在语句、分支、MC\DC(修订条件\判定覆盖)粒度下的运行效果,Chung Man Tang更是在指令级别粒度上对各种缺陷定位的算法的效果进行了测试。
在这些不同的粒度上,基于频谱的缺陷定位算法都证明了自己有较好的缺陷定位能力,然而以函数为粒度的尝试在基于频谱的缺陷定位中却仍然不够广泛。将Ochiai算法首先应用于缺陷定位的Rui Abreu在自己的几篇论文中提到将进行这样的尝试,但却并没有付出行动。
在这里我们选取Tarantula算法、Ochiai算法以及Naish2算法分别作为经典算法、优秀算法和新算法的代表,将其应用于函数粒度,并与本文中提出的算法相比较。以试图证明基于频谱的缺陷定位算法在函数粒度上的有效性以及本发明方法的优越性。
实验集的选择上,这里以Apache-xml-security作为用来验证的数据集。Apache-xml-security也是由SIR提供的一个广泛应用于缺陷定位领域的数据集,是一款用于xml格式文件加密的工具。该项目由java编写而成,相较Siemens和Space测试集,Apache-xml-security项目规模更大,从这个意义上来讲,在其上进行实验会更接近算法在实际使用场景的效果。
Apache-xml-security共有三个软件版本,每个版本中又有若干缺陷版本,它的一些详细信息如表所示。
表3 apache-xml-security信息表
软件版本 | 函数数量 | 测试用例数量 | 代码行数 |
xml-security v1 | 1278 | 92 | 21613 |
xml-security v2 | 1275 | 94 | 22138 |
xml-security v3 | 1173 | 84 | 19895 |
此处分别选取三个软件版本,挑选出其中能表现出缺陷的缺陷版本(即能产生错误失败用例的缺陷版本,具体版本如表4-6所示)对上述算法进行验证。
为避免多个函数获取相同分数的情况下,函数排名因与算法无关的因素出现上下变动,在每个算法的全部结果得出后,先根据签名对函数进行排序,之后再根据算法的得分对函数排序。
验证的效果如表4所示。其中,表中的数字代表相应算法在相应的缺陷版本中生成的缺陷函数排名中,真正存在缺陷的函数所拥有的排名,也即用户找到真正缺陷前所要检查的函数数量。显然,该数值越低代表算法越优秀。其中的平均与最多表示使用相应算法,检查出每一个缺陷所要平均和可能最多检查的函数数量占程序所有函数数量的百分比值,能大致体现一个算法的整体效果。
表4 各算法在xml-security v1上的效果
缺陷版本 | Tarantula | Ochiai | Naish2 | Final |
XSI_AK_1 | 108 | 104 | 107 | 7 |
CE_HD_3 | 1 | 1 | 1 | 1 |
CHP_AK_1 | 1 | 1 | 1 | 4 |
CN2_AK_2 | 7 | 6 | 7 | 1 |
平均 | 2.27% | 2.19% | 2.26% | 0.25% |
最多 | 8.45% | 8.13% | 8.37% | 0.54% |
可见,在xml-security v1的四个缺陷版本中,本发明方法都能以非常高的排名排出真正缺陷所在的函数,而其他三种算法虽然在后三个缺陷版本中表现也非常优异,但在第一个缺陷版本的排名中,它们均给予缺陷真正所在函数超过一百名的排位,远远多于本发明方法(Final)给出的排位(第7名)。因此,在这个软件版本的试验中,本发明方法体现出了明显的优势。
表5 各算法在xml-security v2上的效果
缺陷版本 | Tarantula | Ochiai | Naish2 | Final |
CB_HD_2 | 12 | 2 | 2 | 6 |
CB_HD_3 | 1 | 1 | 1 | 2 |
RF_HD_2 | 135 | 45 | 45 | 30 |
C2E_AK_1 | 13 | 13 | 13 | 7 |
CHP_AK_1 | 79 | 1 | 1 | 7 |
CH_HD_1 | 78 | 1 | 1 | 7 |
EP_AK_1 | 18 | 2 | 2 | 2 |
平均 | 3.76% | 0.72% | 0.72% | 0.68% |
最多 | 10.58% | 3.52% | 3.52% | 2.35% |
在对xml-security v2的试验中共使用了7个缺陷版本,本发明方法仅在三个版本中能够给出高于或等于其他算法的排名。然而由于在第三个版本(RF_HD_2)中,其他三个算法均较差,而本发明方法表现尚可,因此在最终的平均代码检查量以及最多代码检查量上,本发明方法仍然优于其他三种算法。
表6 各算法在xml-security v3上的效果
缺陷版本 | Tarantula | Ochiai | Naish2 | Final |
XU_HD_2 | 124 | 41 | 78 | 10 |
AC_HD_1 | 40 | 1 | 1 | 12 |
RF_HD_1 | 86 | 23 | 23 | 26 |
平均 | 7.1% | 1.84% | 2.89% | 1.36% |
最多 | 10.57% | 3.49% | 6.64% | 2.21% |
在对xml-security v3的3个缺陷版本实验中,由于其他三种算法在第一个缺陷版本中表现较差,而本发明方法效果较好,因此本发明方法虽然在其他两个缺陷版本中没有表现出明显优势,仍在最终的平均代码检查量以及最多代码检查量上优于其他三种算法。
根据以上数据可见,各种算法均能使用户以较少的代码检查量来检测出真正缺陷所存在的位置。而相比其他三种算法,本文提出的方法则更具优势,在三个版本中,其平均代码检查量和最多代码检查量均优于其他三种算法,这表示本发明方法不仅能使用户的平均发现缺陷时间有效减少,还能避免在个别缺陷的检查上投入过于多的精力(对于最难于发现的缺陷——xml-security v2中的RF_HD_2,使用本发明方法也只需要检查2.35%的代码量)。
以上所述的具体描述,对发明的目的、技术方案和有益效果进行了进一步详细说明,所应理解的是,以上所述仅为本发明的具体实施例而已,并不用于限定本发明的保护范围,凡在本发明的精神和原则之内,所做的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。
Claims (4)
1.一种基于函数调用记录的缺陷定位方法,其特征在于,包括以下步骤:
步骤一、对被测程序执行测试用例,获取函数调用记录文件;
步骤二、读取函数调用记录文件,将函数调用信息整理为后续分析需要的格式;
步骤三、根据函数在通过与失败两种测试用例中各自的覆盖率,通过下式计算函数的基础嫌疑值basic:
其中,fail(s)表示执行了函数f的失败测试用例数量,pass(s)表示执行了函数f的成功测试用例数量,totalPass表示全部成功的测试用例数量,totalFail表示全部失败的测试用例数量;
步骤四、根据函数在通过与失败两种测试用例中执行次数的差异,通过下式计算函数的执行次数差异值ExeTimeDif:
ExeTimeDif=-Pp*log2Pp-Pf*log2Pf;
TimeListp(f)=
{time(f,ti)|time(f,ti)∈TimeList(f)∧ti∈Tp∧time(f,ti)>0};
TimeListf(f)=
{time(f,ti)|time(f,ti)∈TimeList(f)∧ti∈Tf∧time(f,ti)>0};
其中,Tp表示全部执行成功的测试用例集合,Tf表示全部执行失败的测试用例集合,time(f,ti)表示函数f在测试用例ti中执行的次数,n1表示全部测试用例的数量,Lp、Lf分别表示集合TimeListp(f)和集合TimeListf(f)的长度;
步骤五、根据函数在通过与失败两种测试用例中数据传递的差异,通过以下过程计算函数的数据传递差异值DataDif:
5.1取函数f的调用信息列表CallInfoList(f):
其中,每一条调用信息ci记录了该次调用时函数的传入参数,返回值以及所在测试用例的运行结果信息;其中,1≤i≤n2,i为自然数,n2为函数f在所有测试用例的运行过程中被调用的总次数;其具体格式如下:
其中,argi为该次调用中传入的第i个参数的值,rtn为被调用函数执行结束后的返回值,rst为该次调用所在的测试用例的最终运行结果;其中,1≤i≤n3,i为自然数,n3为该函数的参数值个数;
5.2以函数f的调用信息列表作为数据集S,以测试用例的运行结果rst作为标签类,以每一个参数argi以及返回值rtn依次作为属性,通过下述过程计算它们的信息增益率:
5.2.1通过下述公式计算这个数据集S的熵:
Entropy(S)=-pp*log2(pp)-pf*log2(pf);
其中S为函数f的调用信息列表,pp和pf分别为运行结果为通过和失败的调用信息条目在调用信息列表所占的比例;
5.2.2以函数f的参数值或返回值作为属性A,根据属性值将调用信息列表中的条目划分为如下若干个子集合,然后计算各子集合的熵:
其中,Si表示第i个分组,对于每一个分组中的所有函数调用信息,它们的当前分析的那个参数值或返回值是相等的;而任意两个分组中的函数调用信息,它们的当前分析的那个参数值或返回值都是不等的;ppi和pfi分别表示第i个分组中测试用例运行结果值为通过和失败情况下函数运行占该组全部条目个数的比例;1≤i≤n4,i为自然数,n4表示分组的个数,也即当前考察的参数值或返回值所拥有不同取值的个数,n4≥1;
5.2.3根据集合S与各子集合Si的熵,计算该属性A的信息增益,A表示参数或返回值:
其中,|S|表示集合S的元素个数,|Si|表示第i组元素的个数,n4表示分组的个数,Entropy(S)表示集合S的熵,Entropy(Si)表示子集合Si的熵,1≤i≤n4,i为自然数;
5.2.4根据集合S与各子集合Si中元素的个数,计算该属性A的分裂信息度量:
5.2.5根据信息增益和分裂信息度量通过下式计算信息增益率:
5.3经过步骤5.2后,函数f针对每一个参数值和返回值,都会计算得到一个信息增益率值,取所有信息增益率中最高者,作为该函数的数据传递差异值DataDif;如果函数并没有参数值和返回值,其数据传递差异值为0;
步骤六、根据函数在通过与失败两种测试用例中调用关系的差异,通过以下过程计算函数的调用关系差异值CallDif:
6.1取函数f的调用关系信息列表CallRelationList(t):
其中每一条调用关系信息cri记录了该次调用中的被调用函数,以及调用该函数的函数;其中,1≤i≤n5,i为自然数,n5为测试用例中调用函数的总次数;其具体格式如下:
cr={caller,callee}
其中caller为本次调用中的调用函数,而callee为本次调用中的被调用函数;如果callee函数没有调用者,caller为空;
6.2针对每一个函数f,提取其所有的调用关系;所提取的信息分为两种,一种是在所有该函数作为被调用者callee的调用,以及在所有该函数作为调用者caller的调用;这两类信息分别构成两个列表:AsCallerList(f)与AsCalleeList(f):
其中clei或clrj分别为该次调用中的被调用函数或调用函数,加上该调用所在测试用例的运行结果rst:
cle={callee,rst}
clr={caller,rst}
其中,1≤i≤m1,1≤j≤n6,i、j为自然数,m1为所有测试用例运行中的全部以函数f作为调用者的调用的出现次数,n6为所有测试用例运行中的全部以函数f作为被调用者的调用的出现次数;
6.3以函数f的AsCallerList(f)与AsCalleeList(f)分别作为数据集S,以测试用例的运行结果rst作为标签类,以被调用函数或调用函数作为属性,通过下述过程计算它们的信息增益率:
6.3.1通过下述公式计算这个数据集S的熵:
Entropy(S)=-pp*log2(pp)-pf*log2(pf);
其中pp和pf分别为运行结果为通过和失败的条目在列表中所占的比例;
6.3.2以函数f的被调用函数或调用函数为属性A,根据属性值将S中的条目划分为如下若干个子集合,然后计算各子集合的熵:
其中,Si表示第i个分组,ppi和pfi分别表示第i个分组中测试用例运行结果值为通过和失败的条目占该组全部条目个数的比例;1≤i≤n4,i为自然数,n4表示分组的个数,n4≥1;
6.3.3根据集合S与各子集合Si的熵,计算该属性A的信息增益:
其中,|S|表示集合S的元素个数,|Si|表示第i组元素的个数,Entropy(S)表示集合S的熵,Entropy(Si)表示子集合Si的熵,1≤i≤n4,i为自然数;
6.3.4根据集合S与各子集合Si中元素的个数,计算该属性A的分裂信息度量:
6.3.5根据信息增益和分裂信息度量通过下式计算信息增益率:
6.4经过步骤6.3后,函数f针对被调用函数和调用函数,会计算得到两个信息增益率值,取其中最高者,作为该函数的调用关系差异值CallDif;
步骤七、将函数的基础嫌疑值、执行次数差异值、数据传递差异值以及调用关系差异值相结合,通过下式计算得到函数最终的缺陷嫌疑值Final:
Final=Temp+(1-Temp)*Adjusted(CallDif);
Temp=AdjustedBasic+(1-AdjustedBasic)*Adjusted(DataDif);
AdjustedBasic=basic+(1-basic)*Adjusted(ExeTimeDif);
Adjusted(dif)=x*dify;
其中,dif为所述运行次数差异值ExeTimeDif、数据传递差异值DataDif以及调用关系差异值CallDif中的一种;x、y为两个待定系数;当dif表示ExeTimeDif时,x=1,y=0.5;当dif表示DataDif时,x=0.5,y=1;当dif表示CallDif时,x=1.43,y=0.125;Temp为中间计算值;
步骤八、根据函数的缺陷嫌疑值对函数由高到低排序得到缺陷定位结果后输出。
2.根据权利要求1所述的一种基于函数调用记录的缺陷定位方法,其特征在于:所述步骤一进一步通过以下步骤完成:
1.1编写调用捕获AOP文件,该文件由2部分内容组成:设置切入点以及切入点处应完成的功能;
设置3个切入点pointcut:
用于截取测试用例入口函数执行的切入点pointcutTestCase、用于截取被测程序所有函数执行的切入点pointcutFunction和用于截取代表测试用例执行失败的函数执行的切入点pointcutFail;
在以上切入点处分别完成以下功能:
当pointcutTestCase截取的函数执行时,生成一个对应该测试用例调用记录文件,准备写入调用记录;
当pointcutFunction截取的函数执行时,在文件中写入该函数调用记录,记录包括线程号、函数签名、传入参数值;
当pointcutFunction截取的函数返回时,在文件中写入函数返回记录,记录包括线程号、函数签名、返回值;
当pointcutFail截取的函数执行或返回时,根据参数值、返回值来判断测试用例执行状态,如果执行失败,则在调用记录文件进行标记;如果pointcutTestCase截取的函数以异常形式返回,也记为用例执行失败,关闭文件;
当pointcutTestCase截取的函数正常返回时,关闭文件;
1.2将AOP文件与被测源程序进行编织得到编织后的被测程序;
1.3对编织后的被测程序进行编译,运行测试用例,得到每个测试用例对应的函数调用记录文件。
3.根据权利要求1所述的一种基于函数调用记录的缺陷定位方法,其特征在于:
所述步骤2进一步通过以下步骤完成:
2.1首先创建一个函数调用记录列表recordList,该列表存储所有函数调用记录文件对应的函数调用记录record;
2.2遍历所有函数调用记录文件,对每个函数调用记录文件通过以下过程进行读取:
2.2.1创建一个新的函数调用记录record,首先根据函数调用记录文件中存储的测试用例运行结果信息,为其测试用例运行的结果rst字段赋值;然后,通过下述过程获取函数调用信息:
2.2.1.1创建一个调用栈池,其中的调用栈以线程名为键值,一个线程对应一个调用栈,调用栈中存储函数调用信息;
2.2.1.2依次读取文件中的所有记录,对每一条记录执行下述工作:
2.2.1.2.1根据该条调用记录中的线程名,在调用栈池中取出对应的调用栈;
2.2.1.2.2如果是函数执行记录,则创建一个函数调用,存入函数签名、参数值信息,压入调用栈;
2.2.1.2.3如果是函数返回记录,则比对这条记录中的函数签名与调用栈顶的函数调用中存储的函数签名是否一致,如果一致,弹出栈顶的函数调用,存入返回值信息,并将这个函数调用信息存入函数调用记录record;如果不一致,说明调用记录文件存在错误,终止程序运行,放弃本次缺陷定位;
2.2.2将函数调用记录record存入函数调用记录列表recordList。
4.一种基于函数调用序列的缺陷定位装置,其特征在于:函数调用记录生成模块、函数调用记录读取模块、函数调用记录分析模块、缺陷嫌疑度计算模块以及排名输出模块;所述调用记录分析模块进一步包括基础嫌疑值分析模块、调用次数差异分析模块、数据传递差异分析模块和调用关系差异分析模块;
所述函数调用记录生成模块用于在使用测试用例对被测程序进行测试时生成函数调用记录文件;
所述函数调用记录读取模块用于从函数调用记录文件中读取并整理函数调用记录,获取被测程序每一个函数在各测试用例执行时的相关信息,以便之后利用其进行分析;
所述函数调用记录分析模块用于根据整理后的函数调用记录对函数在程序运行正确和错误两种情况下表现的差异进行分析;其中,基础嫌疑值分析模块用于根据函数在通过与失败测试用例中的覆盖情况计算函数的基础嫌疑值;执行次数分析模块用于根据函数在不同测试用例运行期间执行次数的差异计算函数的执行次数差异值;数据传递分析模块用于根据函数在不同测试用例运行期间被调用时传入的参数值和返回值的差异计算函数的数据传递差异值;调用关系分析模块用于根据函数在不同测试用例运行期间所处调用关系的差异计算函数的调用关系差异值;
其中,基础嫌疑值basic通过下式计算:
其中,fail(s)表示执行了函数f的失败测试用例数量,pass(s)表示执行了函数f的成功测试用例数量,totalPass表示全部成功的测试用例数量,totalFail表示全部失败的测试用例数量;
执行次数差异值ExeTimeDif通过下式计算:
ExeTimeDif=-Pp*log2Pp-Pf*log2Pf;
TimeListp(f)=
{time(f,ti)|time(f,ti)∈TimeList(f)∧ti∈Tp∧time(f,ti)>0};
TimeListf(f)=
{time(f,ti)|time(f,ti)∈TimeList(f)∧ti∈Tf∧time(f,ti)>0};
其中,Tp表示全部执行成功的测试用例集合,Tf表示全部执行失败的测试用例集合,time(f,ti)表示函数f在测试用例ti中执行的次数,n1表示全部测试用例的数量,Lp、Lf分别表示集合TimeListp(f)和集合TimeListf(f)的长度;
数据传递差异值DataDif通过以下过程计算:
(1)取函数f的调用信息列表CallInfoList(f):
其中,每一条调用信息ci记录了该次调用时函数的传入参数,返回值以及所在测试用例的运行结果信息;其中,1≤i≤n2,i为自然数,n2为函数f
在所有测试用例的运行过程中被调用的总次数;其具体格式如下:
其中,argi为该次调用中传入的第i个参数的值,rtn为被调用函数执行结束后的返回值,rst为该次调用所在的测试用例的最终运行结果;其中,1≤i≤n3,i为自然数,n3为该函数的参数值个数;
(2)以函数f的调用信息列表作为数据集S,以测试用例的运行结果rst作为标签类,以每一个参数argi以及返回值rtn依次作为属性,通过下述过程计算它们的信息增益率:
(2.1)通过下述公式计算这个数据集S的熵:
Entropy(S)=-pp*log2(pp)-pf*log2(pf);
其中s为函数f的调用信息列表,pp和pf分别为运行结果为通过和失败的调用信息条目在调用信息列表所占的比例;
(2.2)以函数f的参数值或返回值作为属性A,根据属性值将调用信息列表中的条目划分为如下若干个子集合,然后计算各子集合的熵:
其中,Si表示第i个分组,对于每一个分组中的所有函数调用信息,它们的当前分析的那个参数值或返回值是相等的;而任意两个分组中的函数调用信息,它们的当前分析的那个参数值或返回值都是不等的;ppi和pfi分别表示第i个分组中测试用例运行结果值为通过和失败情况下函数运行占该组全部条目个数的比例;1≤i≤n4,i为自然数,n4表示分组的个数,也即当前考察的参数值或返回值所拥有不同取值的个数,n4≥1;
(2.3)根据集合S与各子集合Si的熵,计算该属性A的信息增益,A表示参数或返回值:
其中,|S|表示集合S的元素个数,|Si|表示第i组元素的个数,n4表示分组的个数,Entropy(S)表示集合S的熵,Entropy(Si)表示子集合Si的熵,1≤i≤n4,i为自然数;
(2.4)根据集合S与各子集合Si中元素的个数,计算该属性A的分裂信息度量:
(2.5)根据信息增益和分裂信息度量通过下式计算信息增益率:
(3)经过上一步骤后,函数f针对每一个参数值和返回值,都会计算得到一个信息增益率值,取所有信息增益率中最高者,作为该函数的数据传递差异值DataDif;如果函数并没有参数值和返回值,其数据传递差异值为0;
调用关系差异值CallDif通过以下过程计算:
(1)取函数f的调用关系信息列表CallRelationList(t):
其中每一条调用关系信息cri记录了该次调用中的被调用函数,以及调用该函数的函数;其中,1≤i≤n5,i为自然数,n5为测试用例中调用函数的总次数;其具体格式如下:
cr={caller,callee}
其中caller为本次调用中的调用函数,而callee为本次调用中的被调用函数;如果callee函数没有调用者,caller为空;
(2)针对每一个函数f,提取其所有的调用关系;所提取的信息分为两种,一种是在所有该函数作为被调用者callee的调用,以及在所有该函数作为调用者caller的调用;这两类信息分别构成两个列表:AsCallerList(f)与AsCalleeList(f):
其中clei或clrj分别为该次调用中的被调用函数或调用函数,加上该调用所在测试用例的运行结果rst:
cle={callee,rst}
clr={caller,rst}
其中,1≤i≤m1,1≤j≤n6,i、j为自然数,m1为所有测试用例运行中的全部以函数f作为调用者的调用的出现次数,n6为所有测试用例运行中的全部以函数f作为被调用者的调用的出现次数;
(3)以函数f的AsCallerList(f)与AsCalleeList(f)分别作为数据集S,以测试用例的运行结果rst作为标签类,以被调用函数或调用函数作为属性,通过下述过程计算它们的信息增益率:
(3.1)通过下述公式计算这个数据集S的熵:
Entropy(S)=-pp*log2(pp)-pf*log2(pf);
其中pp和pf分别为运行结果为通过和失败的条目在列表中所占的比例;
(3.2)以函数f的被调用函数或调用函数为属性A,根据属性值将S中的条目划分为如下若干个子集合,然后计算各子集合的熵:
(3.3)根据集合S与各子集合Si的熵,计算该属性A的信息增益;其中,被调用函数或调用函数:
其中,|S|表示集合S的元素个数,|Si|表示第i组元素的个数,n4表示分组的个数,Entropy(S)表示集合S的熵,Entropy(Si)表示子集合Si的熵,1≤i≤n4,i为自然数;
(3.4)根据集合S与各子集合Si中元素的个数,计算该属性A的分裂信息度量:
(3.5)根据信息增益和分裂信息度量通过下式计算信息增益率:
(4)经过上一步骤后,函数f针对被调用函数和调用函数,会计算得到两个信息增益率值,取其中最高者,作为该函数的调用关系差异值CallDif;
所述缺陷嫌疑度计算模块用于根据所述函数调用记录分析模块的分析结果,通过下式为每个函数计算最终的缺陷嫌疑值Final:
Final=Temp+(1-Temp)*Adjusted(CallDif);
Temp=AdjustedBasic+(1-AdjustedBasic)*Adjusted(DataDif);
AdjustedBasic=basic+(1-basic)*Adjusted(ExeTimeDif);
Adjusted(dif)=x*dify;
其中,dif为所述运行次数差异值ExeTimeDif、数据传递差异值DataDif以及调用关系差异值CallDif中的一种;x、y为两个待定系数;当dif表示ExeTimeDif时,x=1,y=0.5;当dif表示DataDif时,x=0.5,y=1;当dif表示CallDif时,x=1.43,y=0.125;Temp为中间计算值;
所述排名输出模块用于首先根据所述缺陷嫌疑度计算模块输出的所有函数的缺陷嫌疑值进行排序得到缺陷定位结果,然后输出该缺陷定位结果。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201510233119.8A CN104794059B (zh) | 2015-05-08 | 2015-05-08 | 一种基于函数调用记录的缺陷定位方法及装置 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201510233119.8A CN104794059B (zh) | 2015-05-08 | 2015-05-08 | 一种基于函数调用记录的缺陷定位方法及装置 |
Publications (2)
Publication Number | Publication Date |
---|---|
CN104794059A CN104794059A (zh) | 2015-07-22 |
CN104794059B true CN104794059B (zh) | 2017-08-04 |
Family
ID=53558868
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN201510233119.8A Expired - Fee Related CN104794059B (zh) | 2015-05-08 | 2015-05-08 | 一种基于函数调用记录的缺陷定位方法及装置 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN104794059B (zh) |
Families Citing this family (15)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN107291586B (zh) * | 2016-04-01 | 2021-04-27 | 腾讯科技(深圳)有限公司 | 一种应用程序的分析方法和装置 |
CN106649120A (zh) * | 2016-12-28 | 2017-05-10 | 中国银联股份有限公司 | 一种数据获取方法、分析方法及系统 |
CN108073513A (zh) * | 2017-04-21 | 2018-05-25 | 富士通株式会社 | 对基于区块链的智能合约进行测试的装置和方法 |
CN107608339B (zh) * | 2017-09-30 | 2020-02-25 | 北京奇虎科技有限公司 | 汽车车机的接口防护方法及装置 |
CN108563503A (zh) * | 2018-01-02 | 2018-09-21 | 郑州云海信息技术有限公司 | 一种用于程序测试的数据流记录方法和数据流记录系统 |
CN108228232B (zh) * | 2018-01-12 | 2021-04-30 | 扬州大学 | 一种针对程序中循环问题的自动修复方法 |
CN110109816A (zh) * | 2018-02-01 | 2019-08-09 | 华为技术有限公司 | 测试用例选择方法和装置 |
CN109032918B (zh) * | 2018-05-31 | 2021-06-18 | 长安大学 | 一种基于异常任务函数轨迹的感知节点程序异常诊断方法 |
CN109977637A (zh) * | 2019-01-17 | 2019-07-05 | 阿里巴巴集团控股有限公司 | 辅助确定垂直越权、确定垂直的方法、装置及电子设备 |
CN110134582A (zh) * | 2019-04-03 | 2019-08-16 | 口碑(上海)信息技术有限公司 | 测试用例的处理及数据处理方法及装置 |
CN111831541B (zh) * | 2019-04-22 | 2022-10-28 | 西安邮电大学 | 一种基于风险轨迹的软件缺陷定位方法 |
CN111723016B (zh) * | 2020-06-24 | 2024-06-04 | 湖南国科微电子股份有限公司 | 文件关闭方法、装置、电子设备和存储介质 |
CN113626332B (zh) * | 2021-08-13 | 2023-03-10 | 北京百度网讯科技有限公司 | 调试方法、装置、设备、存储介质以及计算机程序产品 |
CN113886251B (zh) * | 2021-09-30 | 2023-04-11 | 四川大学 | 基于热力图的热点函数确定方法 |
CN115629992B (zh) * | 2022-12-16 | 2023-04-07 | 云筑信息科技(成都)有限公司 | 一种对使用Spring技术栈构建的应用系统进行调试的方法 |
Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN101226502A (zh) * | 2008-02-03 | 2008-07-23 | 中兴通讯股份有限公司 | 一种自动化测试方法及系统 |
CN101625065A (zh) * | 2009-08-17 | 2010-01-13 | 武汉阜成科技有限公司 | 一种耐高温多层隔热复合材料及其制作方法 |
Family Cites Families (1)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
JP2001016346A (ja) * | 1999-06-30 | 2001-01-19 | Pfu Ltd | Isdn通信制御装置の遠隔操作方法および遠隔操作機能を有するisdn通信制御装置並びに記録媒体 |
-
2015
- 2015-05-08 CN CN201510233119.8A patent/CN104794059B/zh not_active Expired - Fee Related
Patent Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN101226502A (zh) * | 2008-02-03 | 2008-07-23 | 中兴通讯股份有限公司 | 一种自动化测试方法及系统 |
CN101625065A (zh) * | 2009-08-17 | 2010-01-13 | 武汉阜成科技有限公司 | 一种耐高温多层隔热复合材料及其制作方法 |
Non-Patent Citations (1)
Title |
---|
基于程序语义的软件故障定位技术研究;何国琴;《中国优秀硕士论文全文数据库 信息科技辑》;20130715;全文 * |
Also Published As
Publication number | Publication date |
---|---|
CN104794059A (zh) | 2015-07-22 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN104794059B (zh) | 一种基于函数调用记录的缺陷定位方法及装置 | |
US10678673B2 (en) | Software program fault localization | |
CN105893256A (zh) | 基于机器学习算法的软件故障定位方法 | |
CN105897517A (zh) | 一种基于svm的网络流量异常检测方法 | |
CN112417176B (zh) | 基于图特征的企业间隐性关联关系挖掘方法、设备及介质 | |
CN101706749B (zh) | 基于软件安全缺陷检测的综合处理方法 | |
CN109886284B (zh) | 基于层次化聚类的欺诈检测方法及系统 | |
CN107132266A (zh) | 一种基于随机森林的水质分类方法及系统 | |
US10761961B2 (en) | Identification of software program fault locations | |
Dia et al. | An empirical evaluation of the effectiveness of smart contract verification tools | |
CN115407753A (zh) | 一种多变量加权集成学习的工业故障诊断方法 | |
Alariqi et al. | Modelling dynamic links among energy transition, technological level and economic development from the perspective of economic globalisation: Evidence from MENA economies | |
Huang et al. | A novel collaborative diagnosis approach of incipient faults based on VMD and SCN for rolling bearing | |
CN105784340A (zh) | 基于混合智能技术的气阀故障诊断方法 | |
CN109902731A (zh) | 一种基于支持向量机的性能故障的检测方法及装置 | |
CN102103539A (zh) | 基于z规格的测试用例生成方法 | |
Neela et al. | Modeling Software Defects as Anomalies: A Case Study on Promise Repository. | |
CN109815108A (zh) | 一种基于权重的组合测试用例集优先化排序方法及系统 | |
CN114665986B (zh) | 一种蓝牙钥匙的测试系统及方法 | |
Zhang et al. | Generating optimal class integration test orders using genetic algorithms | |
Amankwah et al. | Fast bug detection algorithm for identifying potential vulnerabilities in juliet test cases | |
Tamtama et al. | Increasing Accuracy of The Random Forest Algorithm Using PCA and Resampling Techniques with Data Augmentation for Fraud Detection of Credit Card Transaction | |
Golo et al. | An experimental pursuit of sustainable economic growth | |
Lee et al. | An investigation of essential topics on software fault-proneness prediction | |
Martin et al. | A Case Study on Designing Evaluations of ML Explanations with Simulated User Studies |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
C06 | Publication | ||
PB01 | Publication | ||
EXSB | Decision made by sipo to initiate substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
GR01 | Patent grant | ||
GR01 | Patent grant | ||
CF01 | Termination of patent right due to non-payment of annual fee | ||
CF01 | Termination of patent right due to non-payment of annual fee |
Granted publication date: 20170804 Termination date: 20180508 |