通过静态代码分析和动态测试提升软件的质量
扫描二维码
随时随地手机看文章
召回活动增加,交付延误,难以按时履行承诺的功能:软件质量不明显。只有通过一致的行动、遵守标准和使用成熟的测试和质量保证工具,才能开发出好的软件。糟糕的软件会导致金钱损失和公司形象的恶化.嵌入式软件甚至更为关键,因为它主要用于对安全至关重要的应用程序。在这方面,软件错误可能危及人的生命,因此必须不惜一切代价避免。因此,诸如ISO26262、ICC61508或DO178-C等标准对软件的开发和测试质量有严格的要求。
为了确保质量,静态代码分析程序和动态分析过程中可执行软件的测试(包括单元测试)都是必要的。由于这两种方法中的每一种都只揭示了现有缺陷的一部分,所以这两种互补方法都是必要的。
在开发早期使用静态分析工具
虽然动态分析要求执行代码,但静态分析不需要。因此,静态分析工具可以在开发过程的早期阶段在实施阶段使用。由于这个原因,静态代码分析为项目的成功做出了巨大贡献--早期的错误被发现,而修复的成本效益更高。
没有编写测试用例,静态代码分析工具检查代码的语法、语义、控制流和数据流异常、并发性问题以及编程规则。发现了许多错误和安全漏洞。
建议从开发一开始就定期对代码进行静态分析--最好是由单个开发人员在检查代码之前进行分析。当静态代码分析不再显示任何错误时,只将代码提交到进一步的验证步骤是有意义的,例如代码审查、单元测试或集成测试。有了这个程序,在交付前的最后检查期间,错误消息的数量可以大大减少。
静态分析工具在开发嵌入式系统中特别有用,因为在这些系统中使用的语言有C和C++。这些语言给了开发人员很大的自由--不幸的是,这些语言也给了开发人员编写错误的源代码的自由。诸如无点异常、缓冲溢出或全局变量问题是常见的。这种错误可以通过静态代码分析来避免.
动态测试也是必要的
一旦软件可以执行,静态分析应该补充动态测试。
动态测试主要用于证明系统的功能正确性.通常,一旦第一个代码组件是可执行的,就会执行它们。这些测试的一个重要部分是代码覆盖分析,它确保代码的所有(重要)部分都将被测试。
在源代码的所有相关点(代码的仪器)上,测试代码部分是否在测试运行期间执行过,例如来自VERFY软件技术的代码覆盖工具。由于嵌入式系统通常仅有有限的内存空间,因此重要的是,这种仪器的开销仍然很小。此外,代码覆盖工具对性能的影响应该最小,以避免在时间紧迫的系统中出现故障。覆盖工具通常集成到开发环境中--代码检测是自动的。测试运行后,覆盖分析器生成报告,允许详细查看哪些函数执行了,哪些函数没有执行。
对于安全性至关重要的软件代码覆盖是强制性的。标准DO-178C(航空)、ISO26262(汽车)、IN50128(铁路)和通用标准IC61508(铁路)规定了高编码覆盖率,直至修改的条件决定覆盖率(MC/DC),以展示软件中所有条件或决定的测试。
需要静态分析和动态测试,以实现良好的质量
为了保证高质量,必须结合静态分析,在执行与代码覆盖相关的软件(动态测试)期间进行充分的测试。我们现实生活中的家用电器项目的例子显示了为什么需要使用这两种方法,为什么只有一种测试或分析技术可能导致致命的后果。
制造商为洗衣机生产线的控制单元开发了一个软件.它是用C写的,应该是在单片机上运行的。这一相同的控制单元和相同的软件打算用于这一产品系列的所有机器;因此,特定的功能将分别与机器类型打开或关闭。例如,更昂贵的机器配备了传感器,测量洗衣的"染色度",并使程序能够定制主清洗周期的持续时间。更简单的没有传感器的机器的清洗时间保持不变.
在MC/DC代码覆盖率100%的规定下,开发了确保充分软件质量的测试案例。制造商认为静态分析是可消耗的。
我们需要查看源代码(图1)来描述由此产生的问题。
size_t durationMainWashCycle(size_t prog, size_t load, size_t staining) {
if (((prog == 3) || (prog == 5) || (prog == 7)) && (load < 5)) {
return staining * 5;
}
else if (((prog == 4) || (prog == 6)) && (load < 5)) {
return staining * 8;
}
else if (((prog == 4) || (prog == 6)) && (load < 3)) {
return staining * 7;
}
else {
return staining * 9;
}
}
图1:用于计算主洗涤周期持续时间的功能.
图1中的函数根据所选的清洗程序以及洗衣的负载大小和"染色度"来计算清洗周期的持续时间。与此功能相关的是,图2中列出的测试用例是在模块测试期间执行的,其中"染色度"是测试期间评估的一个因素。"产品版本"对这个结果的影响程度将在后面讨论。
测试案例编号 |
产品_版本 |
行骗者 |
装载量 |
染色 |
结果 |
预期成果 |
1 |
11 |
3 |
4 |
3 |
15 |
15 |
2 |
11 |
5 |
4 |
3 |
15 |
15 |
3 |
11 |
7 |
4 |
3 |
15 |
15 |
4 |
11 |
3 |
6 |
3 |
27 |
27 |
5 |
12 |
4 |
2 |
1 |
8 |
7 |
6 |
12 |
6 |
2 |
1 |
8 |
7 |
7 |
13 |
4 |
4 |
1 |
8 |
8 |
8 |
13 |
6 |
4 |
1 |
8 |
8 |
图2:与功能有关的已执行测试用例 durationMainWashCycle() .
然而,所考虑的功能的测试覆盖率仅为71%。关于测试覆盖率的报告(图3)和测试结果立即揭示了一个问题:在第31行开头具有IF-条件的程序路径
else if (((prog == 4) || (prog == 6)) && (load < 3)) {
return staining * 7;
虽然相关测试案例(第5号和第6号)被执行,但没有通过。此外,这些测试案例的实际测试结果与预期结果不同。测试覆盖率报告说明28行的if-状态是通过的。
在代码中交换这两个其他条件消除了错误。现在,一个新的测试运行使测试覆盖率达到95%,与实际和预期的测试结果相匹配。
测试覆盖率报告显示,需要增加一个测试用例,以达到100%的测试覆盖率:
测试案例编号 |
产品_版本 |
行骗者 |
装载量 |
染色 |
结果 |
预期成果 |
9 |
14 |
6 |
6 |
1 |
9 |
9 |
由于缺少测试用例,达到了100%的规定测试覆盖率。
在成功完成集成测试并解决了一些小问题之后,洗衣机投入生产并交付。过了一段时间,一些不高兴的顾客抱怨起来.有些机器的清洗效果不佳,这是因为清洗周期提前终止。还有报告说,机器将主清洗周期延长几个小时。复制这种行为证明是不可能的。
最初,有人怀疑染色传感器出现故障。因此,它们是在保证范围内交换的。然而,很快就清楚,这并没有解决这个问题。
在更仔细地检查了投诉案件之后,显然这些案件只限于生产线中的一种特定的机器类型。因此,考虑了软件出错的可能性,并委托外部服务提供者运行静态代码分析。
第12型的所有模型对染色传感器和其他所有模型在恒定染色值下工作的规定执行不当。如图4所示,模型12的变量"Y"在函数"获得级别"()仍然没有初始化,通过一个未定义的染色度值。
size_t getStainingLevel(size_t product_version) {
size_t y;
if (product_version < 12) { //products w/o staining sensor
y = 3;
}
else if (product_version > 12) { //products with staining sensor
y = readStainingSensor();
}
return y;
}
图4:视产品版本而定的染色度的测定。
由于该值在模块测试中不明显,所以这个错误仍未被发现。
此外,对简化代码的静态源代码分析结果表明,以前描述的不可访问代码部分的错误很可能已经在实施过程的早期发现。
只有通过静态分析和动态分析才能发现错误.不幸的是,静态代码分析只能追溯执行洗衣机制造商。在开发阶段,错误修正会更经济.及时的静态分析和动态测试将避免昂贵的产品召回和相关的形象损失。该电器制造商目前使用静态分析和动态测试,对其所有软件项目进行测试覆盖。