安全相关的C代码测试方法

2020-12-03 21:45:32·  来源:莎益博工程系统开发(上海)有限公司  
 
C是嵌入式系统开发的主流语言,其直接设备读写能力,既保证了代码的执行效率,同时也带来了调试和测试方面的困难。比如内存读写错误、溢出错误、除零错误等待,
C是嵌入式系统开发的主流语言,其直接设备读写能力,既保证了代码的执行效率,同时也带来了调试和测试方面的困难。比如内存读写错误、溢出错误、除零错误等待,都是经常发生的情况。对于安全相关的C代码,不仅要验证软件的运行结果是否符合设计要求,而且还要满足安全相关的标准,比如汽车领域的ISO26262,或者航空工业的DO178C标准。

本文以汽车巡航系统为例,使用美国Reactive-systems公司的Reactis for C(下面简称RFC)软件,就控制策略的C代码,探讨动态测试方法。在“安全相关的软件测试方法”一文中,说明了基于模型的控制系统的测试方法。使用代码生成器,将模型自动转换成C代码后,我们也需要对C代码进行测试,这既是ISO26262的要求,也可以验证C代码和模型的一致性。同样在生成二进制执行文件后,使用相同的测试用例,可以验证C代码和二进制的一致性。

Reactis for C是C代码的白盒子动态测试工具,用于自动生成测试用例,并在验证C代码的功能和安规(ISO26262)的同时,发现代码的运行时错误:
- 整数溢出。
- 浮点数错误,比如0.0/0.0,+inf,-inf等。
- 内存错误,比如数组越界,无效指针等。
- 除零错误。
- 非法移位。

下面就功能测试和安规测试两方面进行说明,主要关注在测试验证方面,以避免内容重复。如果需要了解基本规则,比如ISO26262在软件测试方面的条例,可以参考“安全相关的软件测试方法”。

准备工作
RFC测试C代码的准备工作分两步,第一步是建立编译脚本,设定编译涉及的文件,目录,自动检测缺失的extern变量定义和函数定义,并自动打桩,如图_1所示。第二步是设定Harness,即输入输出,测试内容,以及错误处理方式等等。如果C代码是从模型转出来的,这一步也可以使用Reactis在模型阶段测试时使用的设定。由于其设定方式,与模型测试的设定基本一致,这里不详细说明。

图_1 创建编译脚本

功能测试

RFC的功能测试方式不同于基于模型的方法,在软件的菜单栏,也没有专门的“Validate”菜单。而是在图_1的“Instrumentation Code”中添加。下面就其原理,作简要说明:

C语言的编译,可以分为Debug方式和Release方式。在Debug方式中,一般会加入一些调试代码。当编译Release结果时,C的预编译器会根据“NDEBUG”等定义,将这些调试代码去除后,再编译成二进制。

RFC做功能测试的方法,是作为Debug代码的一部分。RFC提供了一个头文件(reactis_validator.h),在这个文件中定义了两个函数,分别针对模型测试中介绍的User Defined Target和Assertion。
reactis_target()
reactis_assertion()

用户有两种方式来添加UDT和Assertion:
- 手动添加到被测代码中。如下所示:
#ifdef __REACTIS__
…… // 加入UDT和Assertion
#endif
- 也可以使用图_1中的“Instrumentation Code”窗口,在被测代码的指定行号后添加。如图_2所示。

图_2 添加UDT和Assertion

从图_2可以看到,使用RFC提供的“C Code Editor”窗口添加的测试代码,在RFC主窗口中显示时,其编号是独立的,而且高亮显示,以区别实际的被测。

UDT和Assertion的作用,跟模型测试时一样,UDT用来设计场景,Assertion用来发现违反设计要求的测试用例。

通常的做法是,测试工程师会根据设计需求,编写验证用的C文件和头文件。并如图_2所示,将UDT函数和Assertion函数,分别加入到reactis_target()和reactis_assertion()函数中。然后用RFC的自动生成测试用例,来覆盖这些UDT和Assertion。
- UDT函数的返回值为0,表示该场景没有覆盖到,不为0,表示已经覆盖。
- Assertion函数的返回值位0,表示反例已经被找到,不为0,表示未发现违反。

对于下面的设计要求:
在车速小于30km/h时,巡航系统应保持不工作状态。

其UDT函数大致这样:
int cruise_target_lowspeedon(int onOff, double speed)
{
return (onOff && (speed < 30));
}

Assertion函数可以如下:
int cruise_assert_speed(double speed, int active)
{
return !((speed<30) && active);
}

从上述例子可以看到,return语句中的内容,跟模型测试中使用的C代码基本一致。

对于下面的设计要求,就要测试工程师写比较长一点的UDT和Assertion了:
当巡航系统在工作状态,不能有连续3个时序,车辆速度和设定速度相差大于1mph。

由于代码比较长,这里就不列出了。重点是返回值一定要符合上述覆盖度评价的要求。

安全测试
ISO26262标准在C代码测试方面的内容,除了功能测试外,还有下面一些项目的覆盖度要求:
- Statement Coverage
- Branch Coverage
- MC/DC
- Function Coverage
- Call Coverage

对于这些目标的覆盖度要求,RFC提供了一键生成测试用例的功能,如图_3所示。

图_3 自动生成测试用例

背靠背测试
背靠背测试是验证模型及其对应的C代码的一致性。Reactis和RFC的测试用例文件使用了相同的格式(rst文件),所以可以直接在Reactis或RFC软件中打开对应的模型和C代码的测试用例文件进行测试,验证一致性。

而对于其他软件或HIL平台取得的测试用例和结果,Reactis和RFC提供了导入csv文件的功能。当发现结果发生偏差时,RFC会根据设定的误差范围,提供Warning信息,工程师也可以直接用“Difference Scope”图表显示背靠背测试中发现的偏离,如图_4所示。


图_4 背靠背测试中发现偏差

RFC不仅提供全局误差范围设定,也可以针对每一个输出,设定误差范围。

在“安全相关的软件测试方法”一文中提到,为了提高覆盖度,对于输入的精确设定是必要的,这不仅需要一些时间,也要求工程师对这些量的物理含义有比较清楚的认识。对于从模型生成的C代码,RFC提供了直接导入模型阶段所作的这些设定,减轻测试工程师的工作量。

结语
Reactive Systems公司提供的Reactis系列产品,作为一个实用的小工具,为测试工程师提供了直观的界面,集成的环境和实用的功能,满足从模型到C代码的功能测试和安全测试的需要。
分享到:
 
反对 0 举报 0 收藏 0 评论 0
沪ICP备11026917号-25