文章快速检索    
  同济大学学报(自然科学版)  2018, Vol. 46 Issue (7): 972-981.  DOI: 10.11908/j.issn.0253-374x.2018.07.016
0

引用本文  

江建慧, 吴捷程, 孙亚. 一种基于异常控制流的错误程序行为分析方法[J]. 同济大学学报(自然科学版), 2018, 46(7): 972-981. DOI: 10.11908/j.issn.0253-374x.2018.07.016.
JIANG Jianhui, WU Jiecheng, SUN Ya. An Approach to Analyzing Erroneous Program Behavior Based on Exception Control Flow[J]. Journal of Tongji University (Natural Science), 2018, 46(7): 972-981. DOI: 10.11908/j.issn.0253-374x.2018.07.016

基金项目

国家自然科学基金(61432017, 61772199)

第一作者

江建慧(1964—),男,工学博士,教授,博士生导师,主要研究方向为可信系统与网络,软件可靠性工程,VLSI/SoC测试与容错. E-mail: jhjiang@tongji.edu.cn

通信作者

吴捷程(1993—),男,博士生,主要研究方向为软件可靠性工程.E-mail: 1510794@tongji.edu.cn

文章历史

收稿日期:2017-07-17
一种基于异常控制流的错误程序行为分析方法
江建慧 , 吴捷程 , 孙亚     
同济大学 软件学院,上海 201804
摘要:通过静态分析程序显式异常控制流收集到程序中可引起异常的差错信息,采用故障注入实验,分析了程序的“故障差错异常”传播过程.结合函数级异常控制流的描述,对异常相关的差错及其对程序行为的影响进行了分析,建立了基于异常控制流的错误程序行为模型,开发了相应的分析工具.以OpenStack核心组件为对象进行实验,结果表明从异常层次对错误程序行为进行分析是合理而有效的.该方法为具有异常处理机制的大规模程序的错误行为自动分析和差错数据的收集提供了新手段.
关键词错误程序行为    软件差错    异常控制流    故障注入    
An Approach to Analyzing Erroneous Program Behavior Based on Exception Control Flow
JIANG Jianhui , WU Jiecheng , SUN Ya     
School of Software Engineering, Tongji University, Shanghai 201804, China
Abstract: In this paper, the propagation process of "fault-error-exception" chain in programs is analyzed by fault injection experiments. With representation of the exception control flow at function level, the error and its impact on program behavior are analyzed, a model of erroneous program behavior is established. An automatic analysis tool based on the proposed approach is developed and is used to analyze the erroneous behaviors of the significant components in OpenStack. The experimental results validate the validity and rationality of the proposed approach, which provides a new means to automatically analyze the erroneous behavior and collect the valid error set for large-scale programs with exception handling mechanism.
Key words: erroneous program behavior    software error    exception control flow    fault injection    

错误程序行为(erroneous program behavior, EPB)分析主要是获取程序差错及其对程序行为的影响,包括差错类型、差错发生位置、差错发生时机,以及各个差错在程序中的表象.

软件故障注入技术通过人为地对软件或计算机等目标系统注入软件故障来加速其失效,从而实现目标系统的容错机制有效性验证、可靠性评估[1-2].然而,如何得到真实的软件故障或差错数据是该技术实现的前提[1-4].

具有代表性的软件差错数据获取需要分析软件的功能特性及其在某个故障/缺陷被激活后所表现出的错误行为[2-3, 5],工作量很大,且分析周期很长[1, 3].常见的正交缺陷分类(orthogonal defect classification, ODC)所属故障被激活后所产生的程序差错形式随程序实现的不同而不同[1],可能以返回码或共享变量的形式存在于应用程序与其使用的库函数之间[2, 6],也可能以异常的形式在程序中进行传播[5-6],或以跳转指令的组合形式出现[7].

在具有异常处理机制的程序中,故障激活后所产生的差错若被异常处理机制侦测,则异常发生[8-9].若对该异常处理不当,则可引起程序失效[9].这一过程将形成“故障-差错-异常-失效”链.

通过对程序异常控制流(exception control flow,ECF)的分析可收集能够引起异常的差错信息,包括:由异常类型获取差错内容;由异常引发点获取差错被侦测时的物理位置;由异常传播路径获取差错的影响范围;由异常捕捉情况获取差错是否得到修正的信息.这些信息可指导软件故障注入方案生成所需的差错类型、注入位置、注入时机的选择[4-5, 10-11].该过程通过源代码的静态分析,可在相对较短的时间内完成.

本文的主要工作是通过故障注入实验分析程序的“故障-差错-异常”的传播机理;结合函数级ECF(function-level ECF,FECF)的描述,分析了同一差错与多个程序ECF之间的关系,利用ECF对程序中潜在可引起异常的差错进行分析,建立了EPB模型;实现了一个自动分析工具软件,并以OpenStack的核心组件为对象进行了实验验证.

1 相关工作

Christmansson等[3]认为只有注入具有代表性的差错才能有效地模拟真实故障的发生,有效地验证软件的容错机制.考虑应用程序和函数库之间的潜在错误的故障注入工具LFI(库文件故障注入器)改善了应用程序的健壮性测试技术[2].基于穷尽异常模拟模式的测试方法结合异常发生的时序特点在异常引发点注入异常,简化了异常处理结构有效性验证的过程[5].上述工作强调了故障/差错数据的有效性,讨论了如何利用差错数据进行软件容错机制的验证,但其尚未涉及如何有效地收集合理的差错数据.

关于软件故障激活后的传播过程及证明故障激活后能否转化为差错的研究,有基于“故障-差错-失效”的传播关系选择具有代表性的差错集合的方法[5].通过代码内部的故障注入和接口差错注入的实验研究结果表明,两者对程序的影响存在一定的差异[4].代码故障激活后的差错不仅能以返回值、错误码、传入/传出参数的方式进行传播,还能以共享内存数据为载体对程序造成影响[11].应从多维度对故障注入后的EPB进行收集、分析、归类,从而使得各层次的EPB研究可得到相关数据的支撑,实验表明,故障激活后所产生的差错能以返回值或异常形式进行传播,且该差错被侦测的情况和是否引起程序失效取决于程序的实现[6].这些工作均未深入研究具有异常处理机制的程序中的“故障-差错-异常”过程.

大多数与程序ECF相关的研究工作主要聚焦在它对程序分析、开发、测试的影响上.如基于异常传播分析的C++程序数据流分析方法[12]、依赖性分析方法[13].Harrold等[14-15]在对异常处理机制的测试标准进行研究时, 考虑了异常处理机制和异常传播对程序控制流、数据流、控制依赖性等分析技术的影响.用于程序开发、维护、测试的系统化方法通过分析显式ECF,总结了与异常处理结构实现相关的编程错误模式,可指导开发者如何有效地避免异常处理机制的错误使用[16].面向切面机制在一些情景下会使异常处理结构的实现复杂化,从而降低系统的可靠性[17].Jo等[18]提出了一种基于集合分析的函数级别上的Java未捕捉异常分析方法.结合运行时异常的静态测试方法通过交替执行缺陷检测及控制流扩展,提高了测试充分度,还分析了运行时异常对软件静态测试的影响[19].这些与程序ECF相关的研究工作并未考虑到异常本身就是程序错误的一种表象,所以没有利用异常相关的信息来分析潜在的EPB.

2 程序“故障-差错-异常”机理分析 2.1 ODC故障注入实验分析

异常处理机制(exception handling mechanism,EHM)是程序错误处理的常用手段之一.当程序中EHM侦测到差错且引发异常之后,程序的“故障-差错-异常/失效”过程如图 1所示.

图 1 采用EHM的“故障-差错-异常/失效”传播过程 Fig.1 "fault-error-exception/failure" process with EHM

对Android上约800个应用组件所进行的600万次基于外界输入的健壮性测试实验数据表明,约10%的应用组件发生了崩溃且均由未捕捉到的异常所造成[6].

本文以分布式网络基准程序Ipbench和OpenStack中的核心组件nova为对象,结合ODC故障类型及其分布数据,以代码变异的方式实施故障注入实验,实验结果见表 1(表中的E表示可引起异常的故障数量).

下载CSV 表 1 故障注入实验结果 Tab.1 Results of fault-injection experiments

在Ipbench中共注入348个故障,其中255个故障导致了程序崩溃,且249次崩溃是由未捕捉异常引起.

在nova中共注入1 018个故障,未出现程序崩溃现象.由表 1可知,419个故障激活后的差错得到程序的修正,可能的原因有:①有差错的数据在被使用之前重新得到初始化;②有差错程序的执行结果与无差错程序的执行结果一致;③差错引起异常且被捕获,异常处理例程提供了正确的服务.

2.2 传播机理分析

以nova的_validate_cell函数为例,程序的“故障-差错-异常”传播过程分析如下:

(1) 故障激活所发生的差错直接引发异常.如图 2所示,故障激活后的差错直接在当前函数内使raise语句得到执行,从而引发异常.同时,图 2中场景a)与b)的故障激活后均引起了同一异常,说明程序中故障与异常存在多对一关系,即多个故障激活后的差错最终均以同一异常在程序中进行传播.

图 2 差错直接引起异常例子 Fig.2 Example of an error raises exception directly

(2) 故障激活所发生的差错经过传播后引发异常.图 3所示的故障激活后的差错通过数据流影响控制流,最终使得raise语句得到执行,引发异常.

图 3 差错经过传播而引起异常的例子 Fig.3 Example of an error raises exception with certain propagation

(3) 在程序无故障的情况下,外界输入或外界资源违反软件规约而导致异常发生.

因此,可从“异常”层次出发,分析程序中可引起异常的差错信息及其对程序行为的影响,达到分析与异常相关的EPB的目的.

3 基于ECF的EPB分析

为了获取可引起异常的差错的信息,本文方法并不需要构建带有程序ECF的完整过程间控制流[20],而只需获取独立的ECF信息,包括:ECF起始与中止位置信息;ECF对应的异常类型;异常是否被捕捉;ECF所影响到的函数;ECF所影响到的程序功能或服务.这是一种基于源代码静态分析的方法,如何选择起始函数作为分析的起始点,则直接影响到了分析结果的有效性.

3.1 函数级ECF的描述

图 4所示的是函数级异常传播过程,其中nk(1≤kmm≥1)表示函数,其他符号的意思在图中已经加以说明.以函数为粒度的异常传播过程可分为A,B和C三类.

图 4 函数级的异常传播过程 Fig.4 Exception propagation process at function level

(1) 过程A:异常在当前函数nm被引发,而且被捕捉.

(2) 过程B:异常在当前函数nm被引发,且沿着函数调用栈经过m-k逆向传播后在函数nk中被捕捉,1≤km.

(3) 过程C:异常在当前函数nm被引发,且沿着函数调用栈经过m-1次逆向传播,变成未捕捉异常.

定义1  程序P中由raise/throw语句显式引起的FECF可表示为5元组Ψ= < otzπe>,其中:

(1) ol =(n, lo)为Ψ的起始位置,表示Ψ由函数no中代码行号为lo的raise/throw语句所引发.

(2) tΨ所对应的异常类型.

(3) z=(nz, lz)为Ψ的终止位置.若Ψ属于过程A或B,则nzΨ被捕捉时所在的函数,其对应的异常捕捉语句的行号为lz;若Ψ属于过程C,则Ψ在程序P的函数nz处终止且传播出去,lz为Ψ进入函数nz时的异常入口所对应的语句的代码行号.

(4) π以路径的形式给出了Ψ的传播过程.若Ψ属于过程A或B,则πnonm→…→n1nz,其中nonznk(k=1, 2, …, mm≥1)为Ψ所影响到的函数;若Ψ属于过程C,则πnonm→…→n1nznout,其中nout为占位符,表示该路径对应的ECF未被捕捉.

(5) eΨ被引发时程序P的栈底函数,表示被Ψ影响到的程序功能或服务.

与文献[4, 20]一致,本文也将入口函数(entry function, EF)作为函数调用链的分析起点,计算程序中所有潜在的FECF.

3.2 利用程序ECF表示EPB

同一差错可引起不同的FECF.这些FECF组成的集合称为FECF簇,对于其中的任意ΨiΨj,它们具有相同的ot值.

根据“故障-差错-异常”传播机理,若故障激活后的差错引起异常,则该故障对程序带来的影响完全可由相应的FECF簇表示.所以,可通过分析各FECF簇,反向推导程序中可引起异常的各个差错及其对程序行为的影响,从“异常”层次分析与异常相关的EPB,收集可引起异常的差错集合.

3.3 基于程序ECF的EPB分析

定义2  设ΩΨ分别为程序P的基于ECF的EPB和FECF,集合DPEPB由P的m(m≥1)个Ω构成,集合DPFECF由P的n(n≥1)个Ψ构成.属性ot表示可引起FECF簇的差错,如下属性表示差错对程序行为造成的影响:

(1) DclusterFECF={Ψ|∀(∈DPFECF, Ψ.o=Ω.oΨ.t =Ω.t},表示由具有相同ot值的FECF组成的FECF簇.

(2) SimpactEF={s|∀ΨDclusterFECF, s=Ψ.e}为DclusterFECF中各FECF发生时所对应的功能EF集合.

(3) w为一权值,即:

$ w = \sum\limits_1^{\left| {D_{{\rm{cluster}}}^{{\rm{FECF}}}} \right|} {\varphi ({\mathit{\Psi }_i}), {\rm{ }}{\mathit{\Psi }_i} \in D_{{\rm{cluster}}}^{{\rm{FECF}}}} $ (1)

φ(Ψi)为Ψi的异常传播距离,即Ψi在传播过程中影响到的函数数量.若它属于过程A,则φ(Ψi)=1;若属于过程B,则φ(Ψi)=m-k+1;若属于过程C,则φ(Ψi)=m+duserduser为指定的阈值,一般大于程序允许的函数调用栈最大深度.若φ(Ψi)>duser,则Ψi为未捕捉的ECF.wDclusterFECF中所有FECF所影响到的函数数量的总和,该值可用来衡量差错引起异常后对程序控制流、数据流、控制依赖所造成的影响大小.

观察1  w值越大,则EPB对程序可靠性造成危害的风险越大.

异常传播过程B与C的发生会使程序的过程间控制流中产生潜在的非返回调用[15],它的数量(即w值)越大,就意味着异常的传播范围越大,所以程序的控制流、数据流在错误状态下受到的影响越大.

基于ECF的EPB分析的核心是获取程序P中可引起异常的差错信息及各差错对程序行为所造成的影响,关键在于计算DPEPBDPFECF.算法Gen_FECF可计算一个FECF实例.

算法  Gen_FECF:计算一个FECF实例

输入s:函数m中的raise/throw语句

m:语句s所位于的函数

输出Ψ:一个FECF实例

声明callstack:遍历分析函数调用链时所维护的函数调用栈

CTS(m):函数m的调用者(caller)调用函数m时所使用的函数调用语句

开始Gen_FECF(s, m)

1.新建Ψ实例并设置其属性: o←(m, s的行号), ts对应的异常类型, π设置为m, e←callstack的栈底函数;

2. If SLEGS(s)存在c可捕捉由s引发的异常then

3.   z←(m, c的代码行号), m加入π: mm;

4.   return Ψ;

5. else then //当前函数m无法捕捉该异常

6.   temp_stack为callstack的副本;

7.   sends; //用于暂存Ψ终止时的语句

8.   While temp_stack不为空:

9.   mtop←temp_stack.top();

10.   temp_stack.pop();

11.   if m!= mtop then将mtop加入π endif

12.   if temp_stack is empty: //未捕捉异常

13.   z←(mtop, send的行号);

14.   if m!= mtop then

15.     将nout加入π;

16.    else

17.     将mnout加入π;

18.   endif

19.   return Ψ;

20.   else if SLEGS(CTS(mtop))有c可捕捉then

21.   z←(temp_stack.top(), c的行号);

22.   将temp_stack.top()加入π;

23.     return Ψ;

24.   endif

25.   sendCTS(mtop);

26.   endwhile

27. endif

结束Gen_FECF

在计算所有FECF时,e可用于表示FECF所影响到的程序功能或服务.

若在函数递归过程中出现异常,则异常在该部分的传播路径呈现一定的模式.所以,可根据该模式制定合理的裁剪规则,对递归部分的调用链进行裁剪,使得分析过程能够有效地继续执行下去.

在算法Gen_FECF中,函数调用栈是根据函数调用链生成的.由于调用链经过了递归调用链裁减处理,并且调用链中所有函数都属于程序P,函数调用栈的深度最多为2·|FDS(P)|-2,其中FDS(P)为定义于程序P中且在程序P中实现的函数集合.算法Gen_FECF在最好情况下的时间复杂度为O(1),在最坏情况下为O(|FDS(P)|).

根据EHM的性质[14],对于异常的传播及其处理过程,异常保护区z(try语句所保护的代码块)的异常保护序列EGS(z)指的是z对应的异常处理例程所组成的有序序列(c1, c2, …, cn),n≥1,其顺序由z对应的异常处理例程的顺序决定.假如zj嵌套于zi之中,那么传播到zj中的异常首先会被EGS(zj)中的异常处理例程尝试捕捉处理,若该异常未被捕捉,则EGS(zi)再尝试对其捕捉处理,则LEGS(zj)=(cj1, cj2, …, cjn, ci1, ci2, …, cim),若zi不嵌套于任何异常保护区中,则LEGS(zi)=EGS(zi)=(ci1, ci2, …, cim),m≥1,n≥1.若语句s位于z中,则语句s受到LEGS(z)的保护,记为SLEGS(z).若有异常在s(raise/throw语句)处被抛出或异常传播至s(函数调用语句)处,若SLEGS(s)能捕捉该异常,则该异常在s语句所在的函数中被捕捉,否则该异常从当前函数传播出去且沿着函数调用栈逆向传播.

在形如(m1, m2, …, mn-1, mn)所表示的调用栈中,m1表示栈底,mn表示栈顶,n≥1.suc(mi)是mi的后继mi+1ni≥1,suc(mn)=null.CTS(suc(mk))为函数mk中调用函数suc(mk)时的函数调用语句,且CTS(null) =null.为不失一般性,调用链的递归裁剪遵循如下规则.

规则1  设当前函数调用栈具有以下形式:(mbottom, …, M1, M2, …, Mn, M11, M12, …, M1n, …, Mk1, Mk2, …, Mkn, …, Mq1, Mq2, …, Mqp, mother, …),其中,Mkn表示函数Mn的第k次递归调用,k≥1.Mqp表示函数Mpq次递归调用, q>k, np≥0.若∀i, j, ki, j≥1,式(2)得到满足,则调用栈可裁剪为(mbottom, …, M1, M2, …, Mn, Mq1, Mq2, …, Mqp, mother, …),且FECF的otze属性值保持不变,π中受到FECF影响的函数及其顺序不变.

$ \begin{array}{l} {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{i1}})) = {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{j1}})) = \\ {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{q1}})){\rm{ }}\\ {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{i2}})) = {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{j2}})) = \\ {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_2}))\\ {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{in}})) = {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_{jn}})) = \\ {\rm{SLEGS}}({C_{{\rm{TS}}}}({M_n})) \end{array} $ (2)

证明.

情形一:若在Mkn中无异常发生,n≥1,则无FECF的生成受到裁剪动作的影响.

情形二:假如异常x在函数Mkn中由raise/throw语句引发,k≥1,n≥1:①若xMkn中捕捉,则会有与异常x相同的异常在函数Mn中由相同的raise/throw语句引发且被相同catch/except语句捕捉,因为MknMn为相同函数.②若xMkn传播出去,则会有与异常x相同的异常在函数Mn中由相同的raise/throw语句引发且从相同的异常出口从Mn传播出去,因为MknMn为相同函数.

情形三:假如异常x通过CTS(suc(Mkn))语句从suc(Mkn)函数传播至Mkn中,k≥1,n≥1:①若xMkn中捕捉,则会有与异常x相同的异常y通过CTS(suc(Mn))语句从suc(Mn)传播至Mn,且在Mn中由相同的catch/except语句捕捉.因为suc(Mn)与suc(Mkn)为相同函数,CTS(suc(Mn))与CTS(suc(Mkn))为同一函数调用语句,且SLEGS(CTS(suc(Mn))) =SLEGS(CTS(suc(Mkn))).②若xMkn中传播出去,则会有异常x相同的异常yMn中的相同异常出口传播出去.因为suc(Mn)与suc(Mkn)为相同函数,CTS(suc(Mn))与CTS(suc(Mkn))为同一函数调用语句,且SLEGS(CTS(suc(Mn))) =SLEGS(CTS(suc(Mkn))).

根据情形二与三可知,对于在Mkn中被引发或是传播至Mkn的异常x,无论该异常是在Mkn中被捕捉或是传播出去,在(M1M2,…,Mn)中总有等同的异常与其对应并具有相同的异常传播过程.所以,将“M1M2,…,MnM11M21,…,Mn1,…,Mk1Mk2,…,Mkn”部分裁剪成“M1M2,…,Mn”后,FECF实例的otzeπ中受到影响的函数及其顺序不变.证毕.

计算DPFECF的算法如下:

算法  Gen_ DPFECF:计算所有FECF实例数据

输入SPEF:程序P的入口函数集合

输出DPFECF:程序P中所有潜在的FECF

开始Gen_ DPFECF (SPEF)

1. for each m in SPEF:

2.   将调用栈callStack初始化为空;

3.   Traverse_Invocation_Chain(m);

4. endfor

结束Gen_ DPFECF

/*遍历以m开始的函数调用链,用Gen_FECF算法生成FECF实例*/

声明Traverse_Invocation_Chain (m)

1. callstack.push(m); //维护调用栈,m入栈

2. for each语句s in m then

3.   if s是raise/throw语句then

4.     Ψ←Gen_FECF(s, m);

5.     将Ψ加入DPFECF;

6.   else if s是调用函数mcallee的语句then

7.      if mcallee在callstack出现过两次and

8.   SLEGS(CTS(mcallee))=SLEGS(s) then

9.     continue; //满足要求,裁剪

10.     else then //继续分析调用链

11.     Traverse_Invocation_Chain (mcallee);

12.   endif

13.   endif

14. end for

15. Callstack.pop(m) //维护调用栈,m出栈

算法Gen_DPFECFSPEF中的每一个函数为入口的函数调用链进行了分析,该调用链的深度最多为2·|FDS(P)|-2.若一个函数中最坏情况下拥有C个函数调用点且都调用了Gen_FECF算法,则算法Gen_DPFECF在最坏情况下的时间复杂度为O(C·|FDS(P)|· |FDS(P)|·| SPEF |).

计算DPEPB的算法如下:

算法   Gen_ DPEPB:计算所有EPB实例数据

输入   DPFECF:程序P中所有潜在FECF

输出   DPEPB:程序P所有EPB错误行为实例

开始   Gen_ DPEPB (DPFECF)

1. for each FECF Ψ in DPFECF:

2.   existEPB← null

3.   for each EPB Ω in DPEPB: //寻找相应FECF簇

4.     If Ψ.o =Ω.o and Ψ.t =Ω.t then

5.     existEPB ←Ω; break;

6.   endif

7.   endfor

8.   if null = existEPB then //暂无相应FECF簇

9.创建新的EPB实例Ωnew,且初始化其属性:Ωnew.o←Ψ.o; Ωnew.t←Ψ.t; Ωnew.wφ(Ψ), 其DclusterFECFSimpactEF为空

10.   将Ψ加入Ωnew.DclusterFECF;

11.   将Ψ.e加入Ωnew.SimpactEF;

12.   将Ωnew加入DPEPB;

13. else then //将Ψ加入对应簇的EPB实例

14.   existEPB.w ← existEPB.w +φ(Ψ);

15.   将Ψ加入existEPB.DclusterFECF;

16.   将Ψ.e加入existEPB.SimpactEF;

17.   endif

18. endfor

结束Gen_ DPEPB

算法Gen_ DPEPB在计算EPB过程中,需要判断是否已经存在相应的EPB条目用于处理当前的FECF,最坏情况下的时间复杂度为O(|DPEPB|).同时,算法Gen_ DPEPB需要处理程序P的DPFECF中所有元素,所以最坏情况下的时间复杂度为O(|DPFECF |·|DPEPB|).

4 基于Understand的自动分析工具 4.1 EPB自动分析工具的结构

采用本文提出的方法,用Python语言编写了一个基于Understand的EPB自动分析工具,其架构如图 5所示.Understand是一款代码审核工具,提供了可操作源代码相关数据的API,如词法、语法、函数调用关系等信息.

图 5 EPB自动分析工具架构 Fig.5 Structure of automatic analysis tool of EPB

分析控制器:控制整个分析过程,包括:调用链的遍历分析、局部视图数据生成、FECF数据生成、EPB数据生成、函数调用栈的维护等.

局部视图生成器:函数代码中与FECF生成相关的数据以文件形式存储,内容包括:① raise/throw语句的位置、对应的异常类型;②在当前函数被引发的异常捕捉情况,记录捕捉该异常的catch/except语句的位置及其对应的异常类型;③当前函数的函数调用关系信息;④当前函数中的函数调用语句是否处于异常保护区中,若是,则记录相应的catch/except语句位置及其对应的异常类型.

FECF生成器:当异常以raise/throw形式被引发时,结合相应的局部视图数据,根据Gen_FECF算法生成相应FECF数据.

EPB生成器:当DPFECF计算完毕后,根据Gen_ DPEPB算法生成DPEPB数据.

4.2 EPB自动分析工具的使用步骤

自动分析工具进行EPB分析的主要步骤如下:

(1) 以源代码为输入,使用Understand软件生成程序的词法、语法、函数调用关系等数据.

(2) 以s为分析起点,sSEF,迭代分析其函数调用链,检查函数内是否有被raise/throw语句引发的异常.若存在异常,则转(3);否则,继续按照(2)分析函数调用链中未被分析的函数,直到所有调用链分析完毕,转(4).

(3) 结合当前函数调用栈及其相应函数的局部视图数据,计算该FECF实例,并将其加入DPFECF,返回(2).

(4) 结合(3)中得到的DPFECF,计算所有EPB数据,得到DPEPB.

5 实验及其分析

为了验证本文方法及所开发的程序EPB自动分析工具的合理性和有效性,以OpenStack的核心组件作为对象进行了实验.

5.1 实验环境

实验在虚拟机上进行,操作系统为内核版本2.6.32的32位Linux,硬件配置为1GB内存,Intel(R) Core(TM) i3-3217U@1.80GHz单核CPU.实验负载为OpenStack(G版本)中的5个核心组件,该软件用Python语言编写,各组件的SEF数据均根据相应的应用程序编程接口(application programming interface, API)文档人工收集而得.

5.2 实验结果与分析

对27 876行OpenStack组件的错误行为的实验结果见表 2,其中duser=10 000表示用户指定的阈值.

下载CSV 表 2 OpenStack组件错误行为分析结果 Tab.2 Analysis results of erroneous OpenStack component behavior

调用链中函数调用点总量与函数数量的比值为10 408/1 426=7.3,说明每个函数平均被调用了7.3次,若差错在函数中引起异常,则该异常可潜在引起由7个不同ECF组成的FECF簇,表明从异常角度对差错及其对程序行为影响进行分析是可行的.

|DPFECF|/DPEPB|=4.875,说明每个差错平均可引起近5个不同的FECF,故障激活后的差错若被异常处理机制侦测,则该差错对程序行为的影响可通过对相应的FECF簇进行评估来分析,同时,以“故障-差错-异常”过程作为切入点,基于ECF对EPB进行分析是合理的和有效的.

图 6显示了nova中以w排序的前十个EPB实例数据.其中,纵轴左侧数值表示为DclusterFECF的值,右侧数值表示为SimpactEF的值.横坐标为nova具体EPB所对应的差错.以nova中排序第一的EPB实例数据Ωonenova为例,Ωonenova所代表的差错在nova.utils.execute函数中引起了类型为nova.exception.NovaException的异常,该差错是函数解析输入命令时出现了未定义关键字.由于该函数用于解析输入命令,并根据命令创建新进程提供相应的服务,所以该函数被其他众多函数所调用.Ωonenova的|SimpactEF|=13,说明nova的所有EF所对应的调用链均含有此函数,若该函数中存在故障且该故障激活后产生的差错引起异常,则会影响到nova所有功能的正常执行.Ωonenova所代表的差错可潜在地引起近100个FECF,w≈640,小于duser,所以其DclusterFECF中不存在未捕捉异常,但仍然需要小心应对该差错.该数据也说明了观察1的合理性.

图 6 nova组件中以w排序的前十个EPB实例数据 Fig.6 Top ten EPB Ω ordered by w in nova components
6 结束语

本文通过故障注入实验研究了具有EHM的程序的软件故障、差错、异常之间的传播机理.结合FECF的描述,说明了如何利用ECF收集与异常相关的差错信息,建立了基于ECF的EPB模型,开发了基于该模型的EPB自动分析工具,并用该工具对OpenStack核心组件进行了错误行为分析,结果表明了基于ECF的EPB分析的合理性和有效性.该方法及其工具为具有EHM的大规模程序的EPB自动分析,指导程序异常处理结构的重构,使程序可精确捕捉异常,提高程序可靠性,并为构造合理的外部激励精确激活差错,缩短故障注入实验周期提供了有效手段.

参考文献
[1]
DURAES J A, MADEIRA H S. Emulation of software faults: a field data study and a practical approach[J]. IEEE Transactions on Software Engineering, 2006, 32(11): 849 DOI:10.1109/TSE.2006.113
[2]
MARINESCU P D, CANDEA G. LFI: a practical and general library-level fault injector[C]//Proceedings of the 39th IEEE/IFIP International Conference on Dependable Systems and Networks. Lisbon: IEEE, 2009: 379-388.
[3]
CHRISTMANSSON J, CHILLAREGE R. Generation of an error set that emulates software faults based on field data[C]//Proceedings of the 26th IEEE Symposium on Fault Tolerant Computing. Sendai: IEEE, 1996: 304-313.
[4]
MORAES R, BARBOSA R, DURAES J, et al. Injection of faults at component interfaces and inside the component code: are they equivalent[C]//Proceedings of the 6th European Dependable Computing Conference. Coimbra: [s. n. ], 2006: 53-64.
[5]
ZHANG P, ELBAUM S. Amplifying tests to validate exception handling code[C]//Proceedings of the 34th International Conference on Software Engineering. Zurich: IEEE, 2012 : 595-605.
[6]
ARLAT J, MORAES R. Collecting, analyzing and archiving results from fault injection experiments[C]//Proceedings of 5th Latin-American Symposium on Dependable Computing. [S. l. ]: Sao Jose dos Campos, 2011: 100-105.
[7]
张丹青, 江建慧, 陈林博. 一种对程序故障行为和失效行为的聚类有效性验证方法[J]. 中国科学(信息科学), 2014, 44(10): 1323
ZHANG Danqing, JIANG Jianhui, CHEN Linbo. A method for validating the effectiveness of fault clustering and failure clustering of programs[J]. Science China (Information Science), 2014, 44(10): 1323
[8]
SHAH H B, GORG C, HARROLD M J. Understanding exception handling: Viewpoints of novices and experts[J]. IEEE Transactions on Software Engineering, 2010, 36(2): 150 DOI:10.1109/TSE.2010.7
[9]
CRISTIAN F. Exception handling and software fault tolerance[J]. IEEE Transactions on Computers, 1982, 100(6): 531
[10]
LEI B, LI X, LIU Z, et al. Robustness testing for software components[J]. Science of Computer Programming, 2010, 75(10): 879 DOI:10.1016/j.scico.2010.02.005
[11]
SHAHROKNI A, FELDT R. A systematic review of software robustness[J]. Information and Software Technology, 2013, 55(1): 1
[12]
JOHANSSON A, SURI N, MURPHY B. On the impact of injection triggers for OS robustness evaluation[C]//Proceedings of 18th IEEE International Symposium on Software Reliability. Trollhattan: IEEE, 2007: 127-136.
[13]
姜淑娟, 徐宝文, 史亮. 一种基于异常传播分析的数据流分析方法[J]. 软件学报, 2007, 18(1): 74
JIANG Shujuan, XU Baowen, SHI Liang. An approach of data-flow analysis based on exception propagation analysis[J]. Journal of Software, 2007, 18(1): 74
[14]
SINHA S, HARROLD M J. Analysis and testing of programs with exception handling constructs[J]. IEEE Transactions on Software Engineering, 2000, 26(9): 849 DOI:10.1109/32.877846
[15]
HARROLD M J, ROTHERMEL G, SINHA S. Computation of interprocedural control dependence[J]. ACM SIGSOFT Software Engineering Notes, 1998, 23(2): 11 DOI:10.1145/271775
[16]
SINHA S, ORSO A, HARROLD M J. Automated support for development, maintenance, and testing in the presence of implicit flow control[C]//Proceedings of the 26th IEEE International Conference on Software Engineering. Edinburgh: IEEE, 2004: 336-345.
[17]
COELHO R, RASHID A, GARCIA A, et al. Assessing the impact of aspects on exception flows: an exploratory study[C]//Proceedings of the 22nd European Conference on Object-Oriented Programming. Berlin Heidelberg: Springer, 2008: 207-234.
[18]
JO J W, CHANG B M, YI K, et al. An uncaught exception analysis for Java[J]. Journal of Systems and Software, 2004, 72(1): 59 DOI:10.1016/S0164-1212(03)00057-8
[19]
金大海, 宫云战, 杨朝红, 等. 运行时异常对软件静态测试的影响研究[J]. 计算机学报, 2011, 34(6): 1090
JIN Dahai, GONG Yunzhan, YANG Zhaohong, et al. Research on the effect of runtime exception in software static testing[J]. Journal of Computers, 2011, 34(6): 1090
[20]
姜淑娟, 徐宝文, 史亮. 一种基于异常传播分析的依赖性分析方法[J]. 软件学报, 2007, 18(4): 832
JIANG Shujuan, XU Baowen, SHI Liang. An approach to analyzing dependence based on exception propagation analysis[J]. Journal of Software, 2007, 18(4): 832