嵌入式软件动态运行时错误的检测

更新时间:2023-04-23 05:53:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

软件测试相关

嵌入式软件动态运行时错误的检测

刘艳会

1 概述

嵌入式软件是世界上重要应用软件的核心,目前已经广泛应用于国防、航空航天、医疗等重要行业中,确保它的稳定可靠是极为重要的任务。随着当前应用系统规模的增大和复杂度的增加,嵌入式软件测试的成本也直线上升,同时也突出了当前所用测试工具和测试方法的局限性。

和桌面系统不同,对于嵌入式软件系统,软件测试主要是发现以下类型的错误:

功能错误

主要依靠测试人员依据项目需求说明,编写功能测试用例并运行测试用例,从而验

证软件的功能

性能错误

一般需要放到真实的环境中,借助于硬件级别的工具,来衡量软件的性能是否达到

要求

运行时错误(Run-Time Error)

以前没有有效的检测技术,一般不会专门做运行时错误的检查

对于嵌入式系统软件来说,这些错误中,运行时错误是最难发现、又最具风险的错误。欧洲航天局阿里亚纳501号火箭在升空后不到20秒就发生爆炸,其原因就是因为控制飞行姿态的一段代码蕴含有一个变量溢出的运行错误,发生了溢出的变量控制着火箭急速转向而过载。

那么到底什么是软件运行时错误呢?运行时错误都包括什么类型呢?

1 什么是软件运行时错误?(Run-time Errors)

运行时错误(Run-time Errors)就是软件在动态运行时出现的错误,是所有的软件错误中最具风险的。相信熟悉Windows应用系统的人来说,一定经历过以下的情形:

软件测试相关

图1.1 Windows 2000操作系统上发生运行时错误的现象

图1.2 Windows XP操作系统上发生运行时错误的现象

图1.3 UNIX操作系统上发生运行时错误的现象

这种情况下,不管我们做什么选择,应用程序都会退出。可能对于一般的软件来说,出现这样的错误没关系,但对于航空航天、汽车以及医疗设备等安全级别要求非常高的系统来说,一旦出现这样的运行错误,损失就是不可估量的。

运行时错误由ANSI C定义,是指那些能导致预定义之外的不正确结果或者处理器

停机的错误,它是所有的软件错误中最具风险的,其后果包括:处理器停机,数据崩溃、安全保密受到破坏等。

软件测试相关

典型的运行时错误有以下几种:

企图读未初始的变量

对空指针和越界指针的引用

对超界数组的访问

非法类型转换(long to short, float to integer)

非法的算数运算 (例,除零错误,负数开方)

整数和浮点数的上溢出/下溢出

多线程应用中未保护数据的访问冲突

不可达到的代码

运行时错误属于潜在的威胁,广泛的存在各种软件中。根据Berkeley大学与 IBM Watson的研究报道指出所有IBM大型软件的漏洞中,30%-40%是运行时错误引起的。既然运行时错误如此广泛存在,那么我们应该怎样检测运行时错误呢?

2 普通软件测试技术的限制

传统的软件测试技术一般分成静态测试和动态测试,这两种测试方法在检测软件动态运行时错误方面有着很多的限制,如图2。

图2 普通测试技术的限制

静态测试技术可以检查软件代码的编程规范,分析程序的静态结构,对软件的质量进行度量。借助于静态测试技术,可以使代码更加规范,结构更加清晰,但由于静态测试技术不分析代码的动态行为,不分析各个变量之间的关系,因此普通的静态测试技术不能有效的检查出只有动态运行才会出现的错误,即运行时错误。

动态测试技术需要将被测的程序运行起来,最好能放到实际的软硬件环境中。动态测试技术主要有以下的特点:

z 不完全:测试的步骤一般是:测试计划——测试用例——测试执行——发现并提交

软件测试相关

BUG。这种方法只能发现一部分运行时错误,即测试用例所能覆盖到的错误,但

是完全的测试是不可能的(软件测试的原则之一),即不可能穷尽所有的输入,所

以依赖于测试用例的测试最终只能保证测试过的输入不会导致运行时错误,不敢保

证其他大部分的输入也能正常工作。

z 效率低:动态测试技术能发现一部分运行时错误,但它发现的只是现象,类似于图

1这样的现象,而不是问题的根源。测试人员提交BUG后,开发人员还需要重现

BUG,然后使用传统的调试工具来定位问题所在。对于一般的错误,定位并修复

一个错误大约需要10个小时,而对于偶尔死机这样的错误,则需要更多的时间去

调试。

3 PolySpace的语义分析技术

PolySpace使用的是目前最新的语义分析技术,它依靠大量的数学定理提供的规则去分析软件的动态行为。语义分析技术没有使用简单的穷举法,却有能力在更普通的模式下去表达程序的状态,还能提供规则巧妙的去处理它。

在以前的软硬件环境下,这个问题会非常复杂,很难去解决。随着计算机处理能力的不断增强,在当前的硬件条件下,语义分析技术已经完全可以高效的实现。当应用在运行时错误的检测时,语义分析技术会对所有危险的操作,执行一个详细的分析,在程序动态运行之前,最早在编码阶段,就能够发现其中的运行时错误。

举例说明,一个程序P,使用了两个变量:x和y。检查语句X = X / ( X-Y )的运行时错误。

z 第1步:为了保证穷尽,先列举该语句可能存在的所有运行时错误。

X和Y可能没有初始化

X – Y 可能会溢出

X和Y可能会相等,从而导致除数为0

X / ( X – Y )可能会溢出

下面以第3个为例,详细介绍一下语义分析如何检查除数为0

z 第2步:为了更好的理解语义分析,可以在二维的坐标系中表示X和Y所有可能

的取值。红线表示能够导致除数为0的X和Y的集合。

软件测试相关

图3.1 建立二维的坐标系

z 第3步:根据这个图,我们如何去判断是否会出现除数为零的状态?

最直观的方法就是穷举X和Y的每一个状态,检查它是否在红线上,这个活

动称为“测试”,并且我们能够很快了解该原理的局限性:

因为在实际的程序里肯定不只两个变量,所以将会有无数个状态。如果要穷举

所有的状态,花费的时间将会无限长

穷举法不能查出一些运行时错误,包括读取未初始化的变量

语义分析的方法是建立自己的规则来熟练处理所有的状态。如何对程序进行抽

象,可以检测程序的特性呢。如下是一个简单的例子:

间隔分析:根据坐标系中X,Y的坐标,得到X和Y的最小值和最大值,画

出一个相应的矩形图。这个矩形有以下特性:它包括X和Y所有可能的取值,

用四个数据表示(X的最小值,X的最大值,Y的最小值,Y的最大值)。什

么特性是最让人关心的?显然矩形和红线的交集是我们最关心的,就是说,如

果交集是空集的话,就说明除数不可能为0。

软件测试相关

图3.2 矩形中包含了X和Y的所有可能的取值,通过X和Y的最大和最小值绘制。这种类型的

抽象,也叫做间隔分析。编译器、链接器和一般的静态分析工具已经在使用这种技术。

z 第4步:根据语义分析的概念,我们怎样才能高效的用来进行运行时错误的检测

呢?第3步表现了一个从程序中得到的非常简单的抽象(一个矩形),该抽象采用

的是间隔分析的技术,目前大多数的编译器、链接器和一般的静态分析工具都已经

在使用这种技术。这种方法的问题是抽象出来的矩形不是一个很好的形状,因为它

包含了海量的不实际的X、Y的值,运行时错误的检测结果包含了大量的警告信息,不适合实际的分析。

语义分析技术能够依据自己的规则,建立非常精确的形状,包括网格或者多个

多边形,基于变量(X,Y)之间的关系,程序的控制结构(if-then-else, for, while loops, switch),内部过程之间的关系(函数调用),多任务分析,进行运行时错误的检测。

语义分析的技术原理不仅仅是计算数据类型和常量的值,象编译器和一般的静

态分析工具那样,语义分析包括很多方面。它起源于这些包括在程序里面的每一个

操作的相关的语义和操作数据的关系,并以此作为基础去详细审查源代码和细微的

运行时错误,举例如下:

传递给函数的参数不再是一个变量或者常量,而是一个用来约束函数内部局部

变量的集合

在多任务的程序中,全部共享变量的值随时都会改变,除非使用了保护机制

变量不仅仅是有一定取值范围的类型,还是用一套包括控制流关系去表示的方

程式

最后,由于使用了变量的方程式,语义分析能够解决运行时错误的检测

软件测试相关

图3.3 语义分析技术可以验证控制结构、内部过程和多任务的分析,从而可以测试程序的动态属性,高效的检测运行时错误。

4 例子

以下的例子是由语义分析测试的结果,标成红色的代码表示错误。以下的例子请仔细的思考,这里不再做详细的说明。

4.1 控制结构分析:for循环结束后指针访问越界

借助于语义分析,以上指针访问越界(Out of Bounds pointer de-referencing)的错误被自动检测到,而其他的操作都是安全没有错误的。如果不修复,该错误将导致内存冲突,需要大量的调试时间。程序员对这种错误非常熟悉。语义分析是唯一能够在编译时就能发现这些错误的解决方法。

软件测试相关

4.2 控制结构分析:在嵌套的for循环中数组访问越界

借助于语义分析,数组访问越界(Out of Bounds array access)被自动检测到,而其他的操作都是安全没有错误的。

这个例子表现了语义分析对控制结构分析的能力。如果不修复,该错误将导致数据冲突,需要大量的调试时间。

4.3 内部过程分析:除数为

借助于语义分析,函数foo中的除数可能为0的错误被自动检测到,而其他的操作都是安全没有错误的。这个例子表现了语义分析对内部过程分析的能力,能够明确的标示出错误的函数调用和正确的函数调用。如果不修复,除数为0的错误可能导致处理器宕机,同时由于递归的使用,要调试解决该错误,需要大量的调试时间。

软件测试相关

5为什么PolySpace可以取代传统的代码覆盖率的测试? 传统的测试理论中,测试结果一般都对代码覆盖率作了要求,要求必须达到90%或者更高。让我举一个简单的例子来说明代码覆盖率的局限。

static void Recursion (int* depth)

97 /* if depth<0, recursion will lead to pision by zero */

98 { float advance;

99

100 *depth = *depth + 1;

101 advance = 1.0/(float)(*depth - 6); /* potential pision by zero */

102 ……………..

}

在做代码覆盖率测试时,只需要一个测试用例

*depth = 10;

就可以使以上的代码覆盖率达到100%。如果只是追求高覆盖率尽快达到要求的话,就很可能漏过 *depth = 5 会导致除数为0这样的致命的错误。

当然实际的代码可能会更复杂,不会那么简单。假如GlobalFlag是一个被多个任务使用的全局变量,*depth = 10这一个测试用例可能也会让代码覆盖达到100%,但这个100%能保证下面代码中的除数不为0吗?

static void Recursion (int* depth)

97 /* if depth<0, recursion will lead to pision by zero */

98 { float advance;

99

100 *depth = *depth + 1;

101 advance = 1.0/(float)(*depth - GlobalFlag); /* potential pision by zero */ 103 ……………..

}

我认为覆盖应该分为两种,一是传统意义上说的结构覆盖,即逻辑覆盖(语句覆盖、分支覆盖等)和以及基本路径覆盖,第二是数据输入覆盖。

什么是数据输入覆盖呢?就是测试用例的输入占软件所能接受的全部输入的百分比。还用以上例子来说,

int *depth能接受的全部输入为65536个(-32768到+32767)

当前测试用例输入的个数为1(*depth = 10)

这样的结果是:

语句覆盖率为100%

但输入覆盖率仅仅为1/65536(接近于0)

其实这个问题也就是软件测试的完全性的讨论。软件测试原则之一就是不可能做完全的测试。由于工作量、时间、以及资金等等的限制,传统的覆盖率测试方法无法做到完全的测

软件测试相关

试,所以就有了测试用例的优化方法,比如只使用基本路径覆盖,循环当作一次判断选择等等,采用这些方法很容易漏掉一些致命的问题。

有了PolySpace以后,情况完全不一样了。前面我们提到PolySpace采用类似于穷举的语义分析技术,它可以将所有可能的情况都做一遍检查,只要有一个输入存在问题,PolySpace结果中就会有提示这里可能存在问题。

仔细想想,一个用例都能让代码覆盖达到100%,那么PolySpace所有可能的输入都做了,它的代码覆盖能低于一个用例的覆盖率吗?

希望用下图对PolySpace的覆盖情况和传统的覆盖情况作一个简单的总结。

由此从技术上来讲,PolySpace完全可以取代传统的覆盖率测试。

6 语义分析的优势

使用了语义分析技术的PolySpace有很多优势。

z 完全自动化分析,只需要源代码

——不需要测试用例,

——不需要代码插装

——不需要运行程序

因此传统的测试活动可以完全省略

软件测试相关

图6.1 有了PolySpace,传统的测试活动完全可以省略

z 代码写完就可以分析,无论处在任何软件开发的阶段

用PolySpace分析只需要代码,不会改变软件开发的过程。如图

4

图6.2 PolySpace可以用在软件开发的任何阶段

z 直接找到错误的根源,而不是错误的现象,排错更快

软件测试相关

图6.3 PolySpace的测试结果:用不同的颜色提示不同的含义

图6.4 PolySpace直接找到错误的根源,不需要调试

软件测试相关

图6.5 PolySpace分析结果中的多任务共享变量访问关系图

z 分析所有变量组合,比普通的技术找到更多的运行时错误

图6.6 PolySpace能够找到更多的运行时错误

7 PolySpace的价值

使用PolySpace来检查运行时错误,不仅能节省大量的时间成本,还能够节省大量的财

软件测试相关

力成本。

图7.1 PolySpace可以节省的时间成本

在软件开发的不同阶段修复一个错误需要的成本如图10。

图7.2 修复一个错误的经济成本

根据马里兰州大学与南加州大学的研究报道,每千行代码中至少有一个错误。以一个5万行的程序为例,50千行代码 × 1个错误/千行代码 = 50 错误。

软件测试相关

图7.3 PolySpace可以节省的经济成本

8 总结

z PolySpace使用语义分析技术,通过静态验证的方法,专注于发现动态运行时才会

出现的错误

z 不需要写测试用例,不需要将代码执行起来,需要的只是自动的数学建模

z 使用PolySpace时,只需要提供符合ANSI语法的代码,大约30分钟的配置,其

他的全部是完全自动化的分析

z PolySpace运行只会占用大约30分钟的人力时间,其他占用的全部是机器时间 z PolySpace主要检查代码中的逻辑运算是否会有运行时错误,对重要的编程规则做

检查,而一般的静态规则检查工具只检查表面的编程规则,而不检查动态的运行时错误

9 参考资料

1. 奥索公司()PolySpace技术白皮书

2. PolySpace公司网站

软件测试相关

10作者其他文章

LoadRunner中文使用手册 ACT、WAS的使用说明

Web测试方法

大家对我的文章有什么意见和建议,可以用MSN或者hotmail邮箱联系,欢迎大家的意见和建议。

本文来源:https://www.bwwdw.com/article/crvq.html

Top