林广栋 耿锐 赵香
摘要:覆盖率统计是软件白盒测试的重要手段。BWDSP处理器为其软件用户实现了一个代码覆盖率统计工具。该工具可以统计语句覆盖率、分支覆盖率,还可以用累加方式统计覆盖率。该工具通过调试信息得到被测软件的代码行号信息。它使被测软件在BWDSP模拟器上运行,并同时搜集被测软件的覆盖信息,最后把覆盖信息与行号信息结合得到覆盖率。该覆盖率统计工具已经被BWDSP操作系统等大型软件在测试时使用,对这些软件的测试工作起到了重要作用。
关键词:软件测试;覆盖率;BWDSP
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)05-0066-05
Abstract: Coverage testing is an important test mothed for whilte-box testing.A coverage testing tool for BWDSP software is developed. The tool can generate statement coverage rate, branch coverage rate, and can accumulatively generate coverage rate. It get line number information from debug information. It runs the software for testing in BWDSP simulator, and collects coverage information at the same time. It combines line number information and coverage information to get coverage rate. The tool has been used in testing BWDSP operating system, and played an important role in the testing.
Key words: software test; coverage rate; BWDSP
软件测试指用人工或自动的手段对软件进行运行或试验的过程,目的是检验软件是否满足规定的需求。软件测试不仅要找出错误,还要找出错误所在位置。随着软件复杂程度的日益增加,软件测试变得越来越重要[1-2]。一般,软件测试应占软件开发整个过程的40%以上。在某些对可靠性要求高的领域,如航天、金融领域,软件测试所占的时间更长。
如果一段代码在测试过程中没有被运行到,那就无法确保这段代码是正确的。如果一个条件判断语句的某一种条件在测试过程中从未被满足,那就无法确保该条件满足时软件是正确的。覆盖率测试就是一种针对软件代码和分支是否被覆盖而设计的一种测试方法,是软件测试的一种重要手段。它需要得到被测软件的源代码,是一种白盒测试方法。覆盖测试包括语句覆盖、条件覆盖、分支覆盖、条件组合覆盖、路径覆盖等等。语句覆盖测试统计源代码中每条语句是否被运行。测试过程中已经运行的语句行数和全部语句行数的比率称为软件测试的语句覆盖率。显然,软件测试的语句覆盖率越高,软件测试就越充分。条件覆盖测试检查软件代码中条件结果的覆盖情况,目的是使源代码中每个条件取得所有可能的值。分支覆盖测试检查每个分支处的可能跳转的覆盖情况。条件组合覆盖测试检查源代码分支判断中各个条件的所有可能组合的覆盖情况。路径覆盖测试检查测试过程对软件执行流程中所有可能的路径的覆盖情况。理想的测试方案应使以上所有种类的覆盖率都达到100%。但是为了实现这个目标需要设计大量测试用例,代价非常高。一般情况下,只需要覆盖率满足一定条件即可。无论如何,覆盖率测试作为一种有用的信息,可以对设计有效的测试用例进行指导。
统计被测软件的覆盖率一般分为两类方法。第一类方法适用于运行于主机上的通用软件。这类方法把被测软件的行号、条件、分支等信息记录下来,并在被测程序的源代码中需要统计覆盖率的位置插入记录覆盖率的代码(这个过程称为“插桩”)。被测软件执行时,这些插桩的代码也会执行,并把覆盖信息记录下来。执行完毕后,再把覆盖信息与被测软件的行号、条件、分支等信息结合,得到被测软件的覆盖率信息[3-5]。第二类方法适用于嵌入式系统。这类方法同样要执行插桩过程。不同的是,由于被测软件运行于嵌入式系统上,覆盖信息需要通过网线、串口线等物理介质发送给主机。测试用例执行完毕后,在主机上统计分析得到覆盖率信息。第二类方法往往需要覆盖率统计软件提供插桩、通信、统计等一整套工具[6-14]。
常见的覆盖率统计工具包括gcov、Magellan[5]、Logiscope[6]、Bullseye[3,7]、CutteleITE[9]、Testbed[10]等等。gcov是一款Linux系统自带的与gcc协同工作的覆盖率测试工具。使用gcov时,需要要在编译程序时在gcc后加上特定的编译选项。gcc会自动在编译生成的可执行文件的特定位置插入特定代码,并把源代码的行号、分支等信息储存在一个特殊的文件中。当程序执行时,这些插入的代码一并执行,把覆盖信息记录下来。最后调用gcov命令,gcov会结合被测程序实际执行中的覆盖信息与源代码信息,给出其测试过程中的覆盖率信息。Bullseye工作原理与gcov类似,不同的是,它是在源代码级别上向被测软件中插入记录覆盖率信息的代码,然后调用一般的编译器进行编译。它既可以在Linux系统下工作,也可以在windows环境下工作,而且支持多种编译器。Logiscope支持对嵌入式软件的覆盖率测试。它提供了运行于目标机操作系统上的收集覆盖信息并与主机进行通信的软件包。用户需要在主机端使用Logiscope向被测程序中插入代码。被测软件在嵌入式目标机上执行时,把覆盖信息通过特定物理通道(如网线、串口线)发送给主机。被测软件执行完后,在主机端通过Logiscope事后分析工具得到被测软件的覆盖率信息。
BWDSP是一款我国自主研发的高性能DSP,其配套的基础软件也是自主研发的[15-16]。随着BWDSP芯片的应用领域越来越广泛,基于BWDSP芯片开发的软件也变得更加复杂,也需要进行覆盖率测试。因此,必须开发BWDSP配套的覆盖率统计工具。BWCoverageTest是一款借鉴其他覆盖率统计工具自主研发的覆盖率统计工具,既支持统计C程序的覆盖率,也支持统计汇编程序的覆盖率。BWCoverageTest可以统计被测软件的语句覆盖率和分支覆盖率,并支持以累加方式统计覆盖率。本文将对BWCoverageTest的使用方法、实现原理、算法流程、下一步工作等内容进行介绍。
1 简介
BWDSP是一款国内自主研发的数字信号处理器(DSP),属于嵌入式芯片。在BWDSP上运行的软件也属于嵌入式软件。这些软件需要在主机上进行编译,生成可执行文件,然后通过某种物理介质加载到处理器中运行。进行覆盖率测试必须要运行被测软件。如果被测软件属于嵌入式软件,则只能使用前述第二种方法,使被测软件的覆盖信息通过某种物理介质发送到主机上。这种测试方法需要有实际的板卡和物理介质,代价较大。通过这种方法开发的覆盖率统计工具一般也比较复杂。BWDSP处理器配套有一个成熟的周期精确的芯片模拟器。可以通过使被测软件在该芯片模拟器上运行,直接在主机上收集覆盖信息。这种方法对硬件环境要求不高,且简单易用、测试速度较快。利用这种方法开发的覆盖率统计工具功能相对比较单纯,易于实现。
BWCoverageTest基于一个定制的BWDSP芯片模拟器(BWSimulator)实现。BWCoverageTest的工作过程分为两步。第一步调用BWSimulator执行被测软件,直到被测软件运行结束。BWSimulator记录下程序运行过程中每个程序地址(PC)上经历的次数。BWSimulator内部分析汇编指令,判断是否为分支指令,并把分支所在程序地址、进行跳转的方向等信息记录下来。BWCoverageTest的第二步是结合被测程序的行号信息和BWSimulator生成的覆盖记录,分析得到被测软件的语句覆盖率和分支覆盖率,并给出未覆盖到语句和分支。BWCoverageTest工具的整体工作过程如图1所示。目前,BWCoverageTest只能统计语句覆盖率和分支覆盖率。
2 使用方法
BWCoverageTest有两种使用方式:非累加方式和累加方式。以非累加方式使用时,只统计本次测试的覆盖率;以累加方式使用时,把过去所有以累加方式测试的覆盖情况累加起来统计覆盖率。对于复杂的软件,一个测试用例很难也没有必要覆盖所有的代码。一般把被测软件划分为若干模块,每个测试用例测试其中一个或若干个模块。最终测试的目的是使这些测试用例全部运行后覆盖到尽可能多的语句或分支。显然,单纯统计一次测试过程中覆盖率的工具已经不满足要求,要求覆盖率测试工具提供累加测试功能。
2.1 非累加方式
需要以累加方式统计代码覆盖率的代码不能位于main.c文件中和app文件夹中,且每次累加统计时不能改变被测部分的代码。例如,有a.c、b.c两个.c文件中的代码需要以累加方式统计代码覆盖率,可以在main.c中调用a.c和b.c中的函数,或在app文件夹中建立源文件,调用a.c和b.c中的函数。且每次进行累加统计时,a.c和b.c中的代码不能改变。因为若代码改变,则无法准确判断一行代码是否被覆盖到。被测试文件在不同统计过程中可以位于不同的代码段,甚至不必在每次累加测试时都链接到工程中。例如:第一次统计过程中可执行文件中包含a.c中的代码,第二次统计过程可执行文件中包含b.c中的代码,累加统计时,会统计a.c和b.c总体的覆盖率。
3 软件模拟器
BWDSP芯片配套有周期精确的软件模拟器。该模拟器用C++语言基于SystemC库开发。BWSimulator是一个专为统计覆盖率而设计的软件模拟器,在普通BWDSP软件模拟器基础上增加了记录程序地址和分支判断结果的功能。
被测软件运行结束后,m_cond_pc中所有关键字即是被测软件所经历的分支地址,该关键字对应的键值记录了在该分支处进行跳转和不进行跳转的次数。其中cnttrue记录进行跳转的次数,cntfalse记录不进行跳转的次数。
4行号信息分析
BWCoverageTest使用被测文件的调试信息得到其程序地址和行号信息的对应关系[18]。BWCoverageTest利用BWDSP调试系统中分析行号调试信息的模块,同时支持对STABS格式和DWARF格式调试信息的分析。
5 算法
5.1 非累加方式
以非累加方式统计覆盖率时,BWCoverageTest将被测文件调试信息中存储的程序地址构成一个STL关联容器set(集合),记为AllSet。该集合为被测软件可能经过的需要进行统计的程序地址集。以BWSimulator实际执行过程中记录的程序地址构成一个集合,记为RunTimeSet。
被测软件会包含一些C语言标准库,这些库不带调试信息,也不需要统计覆盖率,但在实际执行过程中会执行到。这些库执行过程中经过的程序地址会出现在RunTimeSet中,但不会出现在AllSet中。在统计C语言程序的覆盖率时,AllSet中记录的是被测软件每一行C语言代码的程序地址,而一行C语言代码会被编译成为多行汇编代码。这些汇编代码行的程序地址都可能出现在RunTimeSet中。所以RunTimeSet中包含很多AllSet中不包含的程序地址,RunTimeSet并不是AllSet的子集。
RunTimeSet与AllSet的合集,就是被测软件执行过程中执行到的代码行的程序地址集。显然,该合集的大小与AllSet集合的大小的比率,就是被测软件的代码覆盖率。而AllSet与RunTimeSet的差集,就是未被覆盖到的代码行。
BWCoverageTest统计分支覆盖率的方式较为简单,将分支跳转记录中两种跳转数都不为0的分支数,除以该分支跳转记录中的总分支数,即为分支覆盖率。如果一个分支从未被执行,那BWSimulator也不会记录该分支的跳转记录。只有当语句覆盖率为100%时,BWCoverageTest得到的分支覆盖率才准确。当语句覆盖率小于100%时,只有通过编译器记录下全部分支的程序地址,才有可能准确得到哪些分支未被覆盖到,此时得到的分支覆盖率才是准确的分支覆盖率。这也是BWCoverageTest下一步的工作方向。
5.2 累加方式
对累加方式进行覆盖率统计的需求来源于对BWDSP操作系统的测试。BWDSP操作系统为应用程序提供一组API,应用程序通过调用这些API来使用操作系统,应用程序本身就相当于测试用例。但应用程序和BWDSP操作系统都会被编译到同一个可执行文件中。一般很难在一个应用程序中调用所有BWDSP操作系统的API,所以很难用一个应用程序使BWDSP操作系统的语句覆盖率达到规定的要求。而以累加方式统计时,测试人员可以使用多个应用程序,分次进行覆盖率测试。每个应用程序测试操作系统的一个功能或一个模块。每次测试时,甚至可以把本次测试不需要的操作系统源代码文件从软件工程中排除。唯一的要求是操作系统的代码不能改变。
BWCoverageTest规定,应用程序可以位于main.c源文件中,或位于app文件夹下。以累加方式进行覆盖率统计时,对这两个位置的源代码行,不再进行统计。BWCoverageTest以累加方式进行测试时,还可以统计每个文件的累加覆盖率。
累加方式对每次测试时被测源代码文件是否全部被包含不作要求,因此源代码中的一行在每次测试时对应的程序地址不同。所以不能再利用程序地址是否被经过作为覆盖率统计的依据。累加方式统计累加覆盖率的依据是每个源文件中一行代码是否被覆盖到。
RunFileLine也是map数据结构,关键字为源文件名,而键值则是累加测试过程中经历的程序地址对应的行号。
根据BWSimulator一次测试过程中记录下来的程序地址记录,以及该次测试过程中可执行文件中的调试信息,分析得到源文件中的某个行号在本次测试中是否被覆盖。若被覆盖,则加入到RunFileLine数据结构中。
其中tdi是一个分析可执行文件中行号信息的C++类的实例。累加测试过程中,由于每次测试时被测软件在可执行文件中的位置不一定相同,每分析一次测试过程产生的记录信息,tdi都要重新分析一次该次测试过程中的可执行文件。
最后,以覆盖到的行号数量除以全部源文件的行号数量,即是被测软件的累加覆盖率。累加方式覆盖率统计的流程如图3所示。
6 下一步工作
BWCoverageTest目前已经可以进行语句覆盖率和分支覆盖率测试,但仍存在一些不足。下一步工作计划如下:
1) 支持准确分析分支覆盖率。利用编译器得到被测软件分支的程序地址记录。即使语句覆盖率达不到100%,分支覆盖率也是准确的。
2) 支持分析条件覆盖率。借助于编译器得到生成条件结果时的程序地址,并得到条件结果(“真”或“假”)存储的位置。这种条件结果一般应存放于某个寄存器中。BWSimulator在生成该条件结果时通过结果所在位置得到条件结果实际取值,再记录下来。BWCoverageTest借助BWSimulator生成的条件覆盖信息得到条件覆盖率。
3) 支持分析条件组合覆盖率。借助于编译器得到生成分支判断结果时的程序地址,以及生成条件结果时的程序地址。BWSimulator记录下分支判断取真或假的结果,以及条件的结果。最后由BWCoverageTest分析得到分析条件组合覆盖率。
4) 集成到开发环境。目前BWCoverageTest以命令行的方式使用,对用户不太友好。下一步应考虑将BWCoverageTest集成进行BWDSP集成开发环境,成为该软件的一个附属工具。并以图形界面的方式把语句覆盖率、分支覆盖率等结果显示出来。
7 总结
BWCoverageTest是一个自主创新实现的覆盖率测试工作,可以测试BWDSP处理器上的软件覆盖率。BWCoverageTest使用软件模拟器记录被测软件的运行信息,结合被测软件的行号信息,得到覆盖率信息。BWCoverageTest还支持以累加方式使用,使多次统计过程的覆盖情况可以累加。但BWCoverageTest仍存在很多不足之外。它不能统计条件覆盖率、条件组合覆盖率等更高级的覆盖率信息。这些都是BWCoverageTest下一步研究的方向。
参考文献:
[1] Paul C Jorgensen. Software Testing: A Craftsmans Approach[M]. 2nd ed. USA:CRC Press, 2002.
[2] Robert Culbertson, Chris Brown, Gary Cobb. RAPID Testing[M]. USA: Posts and Telecom Press, 2002.
[3] 顾惟祎.基于代码覆盖统计的BMC回归测试用例选择系统的设计与实现[D].南京:南京大学,2013.
[4] 李列锋.基于二进制可执行文件代码覆盖测试技术研究[D].郑州:解放军信息工程大学,2007.
[5] 胡飞,朱佳.用Magellan测试代码覆盖率[J].科学技术与工程,2006,6(12),1710-1712.
[6] 陈丽蓉,熊光泽,罗蕾,等.嵌入式软件的覆盖测试[J].单片机与嵌入式系统应用,2002,2(11):8-11.
[7] 苏华龙,陆松年.代码覆盖分析工具在嵌入式软件测试中的应用[J].单片机与嵌入式系统应用,2007,7(5),9-11。
[8] 金维佳,施小敏.基于嵌入式软件的覆盖测试问题研究[J].信息技术,2011(4):117-120.
[9] 杨俊,张倩,林依刚.一种嵌入式软件覆盖测试方法[J].指挥信息系统与技术,2010,1(6),24-26,69.
[10] 刘颖,王英,刘漫丹.嵌入式软件的覆盖测试[J].自动化仪表,2012,33(6),63-66.
[11] 周雷.嵌入式代码覆盖率统计方法[J].计算机应用与软件。2014,31(5),326-327.
[12] 浦云明,丁跃潮.基于DD路径的代码覆盖技术及应用[J].2007,28(14),3306-3309.
[13] 浦云明,张杰敏,林颖贤。代码覆盖测试技术在MODE-S应答机中的应用[J].计算机应用与软件,2008,25(7):131-133,148.
[14] 于炳霞.基于嵌入式软件的覆盖测试技术研究[D].南京:南京航空航天大学,2010.
[15] 余锋林,刘小明,朱艳,等.BWDSP100集成开发环境设计与实现[J].中国集成电路,2012,21(6):25-29.
[16] 刘小明,朱艳.BWDSP100数字信号处理器的指令缓存器设计[J].中国集成电路,2013,22(4):48-50,56.
[17] 李普曼,拉乔伊,默.C++ Primer中文版[M].王刚,杨巨峰,译.5.北京:电子工业出版社,2013.
[18] 林广栋,刘谷,王强,等.一种DWARF格式行号调试信息解析方法[J].现代计算机,2014,482:3-9.