0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

集成错误检测技术以发现嵌入式C软件中的更多错误

星星科技指导员 来源:嵌入式计算设计 作者:Parasoft SA ,Mirosł 2022-06-19 09:49 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

诸如基于模式的静态代码分析、运行时内存监控、单元测试和流分析等自动化技术可以一起用于查找嵌入式 C 应用程序中的错误。以下讨论将使用 Parasoft C/C++test 演示这些技术,Parasoft C/C++test 是一种集成解决方案,用于自动化广泛的最佳实践,以提高 C 和 C++ 软件开发团队的生产力和软件质量。

示例传感器应用

可以在 ARM Cortex-M3 板上运行的简单传感器应用程序的上下文中探索推荐的错误发现策略。应用程序已创建并上传到开发板,但在运行时,它不会在 LCD 屏幕上呈现预期的输出。

它不起作用,原因尚不清楚。在目标板上进行调试既费时又乏味,因为需要手动分析调试器结果以尝试确定真正的问题。或者,可以应用某些工具或技术来自动查明错误。

此时,两个选项是使用调试器调试应用程序或应用自动化测试策略从代码中剥离错误。如果应用自动化技术后应用程序仍然无法工作,调试器可以作为最后的手段。

基于模式的静态代码分析

应用了基于模式的静态分析,而不是调试,它快速、易于使用并且几乎可以应用于每次代码 更改。通过执行静态分析确定了一个问题(参见图 1)。

图 1:静态代码分析识别 MISRA 编码标准违规。

pYYBAGKugNCAKz00AAKeNT6_DJg677.png

这违反了 MISRA 规则,即在布尔表达式中使用赋值运算符可能会有风险。目的不是使用赋值运算符,而是使用比较运算符。所以这个问题得到解决,程序重新运行。

由于一些输出显示在 LCD 上,因此有所改进。但是,应用程序因访问冲突而崩溃。再一次,有一个选择:使用调试器或继续应用自动错误检测技术。鉴于自动错误检测在发现此类内存损坏方面非常有效,因此执行运行时内存监控是最佳选择。

整个应用程序的运行时内存监控

可以通过应用适合在目标板上运行的轻量级仪器来执行运行时内存监控。上传并运行检测的应用程序并下载结果后,会报告错误(参见图 2)。

图 2:运行时内存监控报告读取超出范围的数组。

poYBAGKugOyASgfwAALXAj_X3z4239.png

这表明在第 48 行读取了一个超出范围的数组。显然,msgIndex变量的值一定超出了数组的范围。向上堆栈跟踪显示,这个具有超出范围值的打印消息是由于在调用函数printMessage()之前为其设置了不正确的条件而导致的。这可以通过放松if语句中的值范围控制并去掉不必要的条件(value 《= 20)来解决。

void handleSensorValue(int value)

{

initialize();

int index = -1;

if (value 》= 0 && value 《= 10) {

index = VALUE_LOW;

} else if ((value 》 10) && (value 《= 20)) {

index = VALUE_HIGH;

}

printMessage(index, value);

}

现在,当重新运行应用程序时,不会报告内存错误。应用程序上传到板后,它似乎按预期工作。然而,一些担忧仍然存在。

在执行的代码路径中发现了一个内存覆盖实例,但这是否意味着未执行的代码中没有内存覆盖?覆盖分析表明,有些代码根本没有被执行。reportSensorFailure() 函数没有被覆盖,并且调用reportSensorFailure的mainLoop函数内部的一个分支根本没有被执行(再次参见图 2)。测试此代码的一种方法是创建一个单元测试(用于mainLoop函数)和一个用户存根(用于readSensor函数),以模拟在功能测试期间难以重现的条件。

带有运行时内存监控的单元测试

创建一个测试用例骨架,然后用测试代码填充。 此外,为readSensor函数添加了一个存根以模拟读取错误。运行测试用例——只执行这个以前未测试的功能——启用运行时内存监控。结果显示该函数现在已被覆盖,但报告了新的错误(参见图 3)。

图 3:启用运行时内存监控的单元测试会暴露内存错误。

poYBAGKugPiANVkRAALMdClGdXM371.png

测试用例发现了更多与内存相关的错误。调用失败处理程序时,内存初始化(空指针)存在明显问题。进一步分析表明,reportSensorValue()中混合了调用顺序,因此finalize()在printMessage()被调用之前被调用,但finalize()实际上释放了printMessage()使用的内存。

void finalize()

{

if (messages) {

free(messages[0]);

free(messages[1]);

free(messages[2]);

}

free(messages);

}

void printMessage(int msgIndex, int value)

{

const char* msg = messages[msgIndex];

printf(“Value: %d, State: %s\n”, value, msg);

fflush(stdout);

}

void reportSensorFailure()

{

finalize();

printMessage(ERROR_MSG, 0);

}

这个顺序是固定的,测试用例会重新运行一次。

这解决了报告的错误之一。下一步是解决报告的第二个问题:打印消息中的 AccessViolationException。这是因为这些表消息未初始化。为了解决这个问题,在打印消息之前调用initialize()函数。修复后的功能如下:

void reportSensorFailure()

{

initialize();

printMessage(ERROR, 0);

finalize();

}

重新运行测试时,只报告一个任务:一个无效的单元测试用例,这并不是真正的错误。必须验证结果才能将此测试转换为回归测试(参见图 4)。

图 4:必须为回归测试配置测试。

poYBAGKugQmAPZF6AALbv8XkLNU703.png

接下来,再次运行整个应用程序。覆盖率分析显示几乎整个应用程序都被覆盖了,结果表明没有出现内存错误问题。

即使运行了整个应用程序并为未覆盖的函数创建了单元测试,但仍有一些路径未被覆盖。可以继续使用单元测试创建,但需要一些时间才能覆盖应用程序中的所有路径。相反,可以使用流量分析来模拟这些路径。

流量分析

运行流分析以模拟通过系统的不同路径,并检查这些路径中是否存在潜在问题。报告了几个问题(参见图 5)。

图 5:流分析发现路径中的几个问题。

pYYBAGKugRKAFdLPAAKJvZLif3o566.png

有一条潜在的路径——一个未被覆盖的路径——在finalize()函数中可以有一个双重释放。reportSensorValue()函数调用finalize(),然后finalize ()调用free()。此外,在mainLoop()中再次调用finalize () 。这可以通过使finalize()更智能来解决:

void finalize()

{

if (messages) {

free(messages[0]);

free(messages[1]);

free(messages[2]);

free(messages);

messages = 0;

}

}

然后再运行一次流量分析。仅报告了两个问题(参见图 6)。

图 6:流量分析检测到两个剩余问题。

poYBAGKugRyAeJDWAAKcpBYS3ko035.png

此处可能正在访问索引为-1的表。这是因为积分索引最初设置为 -1,并且在调用printMessage()之前,可能存在一条通过if语句未将此积分设置为正确值的路径。运行时分析并没有导致这条路径,并且这条路径可能永远不会在现实生活中被采用。与实际运行时内存监控相比,这是流分析的主要弱点。流分析显示潜在路径,不一定是在实际应用程序执行期间将采用的路径。通过删除不必要的条件(value 》= 0)可以轻松修复此潜在错误。

void handleSensorValue(int value)

{

initialize();

int index = -1;

if (value 《= 10) {

index = VALUE_LOW;

} else {

index = VALUE_HIGH;

}

printMessage(index, value);

}

报告的最终错误以类似的方式修复。现在,重新运行流分析时,不会报告任何问题。

回归测试

为确保一切正常,重新运行整个分析。首先,应用程序在运行时内存监控下运行,一切似乎都很好。然后使用内存监控运行单元测试并报告一个任务(参见图 7)。

图 7:单元测试发现回归失败。

pYYBAGKugSqAMO23AAJ8hf7fSs4642.png

单元测试检测到reportSensorFailure()函数的行为发生了变化。这是由finalize()中的修改引起的,这是为了纠正先前报告的问题之一而进行的更改。此任务引起对更改的注意,并指示必须审查测试用例。然后要么更正代码,要么更新测试用例,以表明这个新行为实际上是预期的行为。看了下代码,很明显后者为真,并且更新了断言的条件。

void sensor_tests_test_reportSensorFailure()

{

{

messages = 0 ;

}

{

reportSensorFailure();

CPPTEST_ASSERT(0 == ( messages ));

}

}

作为最后的健全性检查,整个应用程序独立运行,在集成开发环境中构建它,无需任何运行时内存监控。结果证实它按预期工作。

补充工具

所有应用的测试方法——基于模式的静态代码分析、内存分析、单元测试、流分析和回归测试——不相互竞争,而是相互补充。一起使用,它们提供了一个非常强大的工具,可以为嵌入式 C 软件提供无与伦比的自动错误检测水平。

作者:Parasoft SA ,Mirosław Zieli nski

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 传感器
    +关注

    关注

    2574

    文章

    54389

    浏览量

    786166
  • 嵌入式
    +关注

    关注

    5186

    文章

    20153

    浏览量

    328936
  • C++
    C++
    +关注

    关注

    22

    文章

    2122

    浏览量

    76714
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    ARM嵌入式这样学

    工程师的的终点只有一个,就是真正的技术专家。 嵌入式技术大体上可分为编程语言,内核技术,操作系统,总线接口,系统集成。其实细心的可以
    发表于 12-04 07:48

    CW32嵌入式软件开发的必备知识

    设计的原则和方法,能够设计出高效、可维护的软件系统。 了解嵌入式系统的实时性要求,能够设计出满足实时性要求的软件系统。 8、 测试与验证 掌握单元测试、集成测试和系统测试的方法和
    发表于 11-28 07:48

    C语言在嵌入式开发的应用

    C 语言在汽车电子控制系统开发的主导地位。 2、设备驱动程序 设备驱动程序是嵌入式系统连接硬件和软件的桥梁,它负责实现
    发表于 11-21 08:09

    嵌入式与FPGA的区别

    \"嵌入式开发门槛低、就业广,适合转行;FPGA技术深、薪资高,但要求学历和数学功底。选哪个?看你的基础和职业目标。\" ⭕我们先来明白下两者区别在哪? ✅1、嵌入式:分两部分
    发表于 11-20 07:12

    嵌入式软件测试与专业测试工具的必要性深度解析

    技术。 ‌环境适应性挑战‌:温度、湿度、电磁干扰等环境因素会显著影响硬件性能,导致系统不稳定或故障,这类问题在测试往往超出预期范围。 专业测试工具的核心价值专业测试工具针对嵌入式系统的特殊性提供了
    发表于 09-28 17:42

    电商API常见错误排查指南:避免集成陷阱

      在电商平台开发,API集成是连接系统、实现数据交换的核心环节。然而,许多开发者在集成过程中常遇到错误,导致项目延迟、数据丢失或用户体验下降。本文将逐步介绍常见
    的头像 发表于 07-11 14:21 1799次阅读
    电商API常见<b class='flag-5'>错误</b>排查指南:避免<b class='flag-5'>集成</b>陷阱

    如何成为一名嵌入式软件工程师?

    如何成为一名嵌入式软件工程师? 01明确岗位的角色与定位 嵌入式软件工程师主要负责开发运行在特定硬件平台上的软件,这些
    发表于 04-15 14:37

    嵌入式软件单元测试的必要性、核心方法及工具深度解析

    一、为什么嵌入式软件必须重视单元测试? ‌嵌入式系统的特殊性‌ 在汽车 ECU、医疗设备控制器等场景软件直接操控硬件,‌单比特
    的头像 发表于 03-21 14:53 970次阅读

    Python在嵌入式系统的应用场景

    你想把你的职业生涯提升到一个新的水平?Python在嵌入式系统中正在成为一股不可缺少的新力量。尽管传统上嵌入式开发更多地依赖于CC++语言
    的头像 发表于 03-19 14:10 1198次阅读

    嵌入式系统的代码优化与压缩技术

    ,以便容纳更多功能模块或降低硬件存储成本。智能手表为例,其内部存储容量有限,通过代码压缩技术,能在有限空间内存储更多应用程序和数据,丰富手表的功能。 四、常见代码压缩
    发表于 02-26 15:00

    嵌入式机器学习的应用特性与软件开发环境

    作者:DigiKey Editor 在许多嵌入式系统,必须采用嵌入式机器学习(Embedded Machine Learning)技术,这是指将机器学习模型部署在资源受限的设备(如微
    的头像 发表于 01-25 17:05 1214次阅读
    <b class='flag-5'>嵌入式</b>机器学习的应用特性与<b class='flag-5'>软件</b>开发环境

    如何提高嵌入式代码质量?

    技术 现代的嵌入式开发工具和测试技术可以极大地提升代码质量和开发效率: 1. 静态分析工具:如Coverity、Lint等,能够帮助发现潜在的代码缺陷和安全漏洞。 2. 单元测试和
    发表于 01-15 10:48

    GPIO错误排查与解决

    嵌入式系统和微控制器编程,通用输入输出(GPIO)是最常见的接口之一。然而,在使用GPIO时,我们可能会遇到各种错误。 1. 理解GPIO GPIO是微控制器上的一组引脚,可以被配置为输入或输出
    的头像 发表于 01-09 09:46 3718次阅读

    RTOS错误检查机制

    嵌入式应用,有可能发生各种各样的错误,系统必须能够检测到这些错误并作出适当的响应。RTOS通常内置了一些
    的头像 发表于 01-03 14:44 1022次阅读

    嵌入式系统开发的测试方法 嵌入式系统开发与AI结合应用

    嵌入式系统开发的测试方法 嵌入式系统开发是一个复杂的过程,涉及到硬件和软件的紧密结合。测试是确保系统可靠性和性能的关键步骤。以下是一些常用的测试方法: 单元测试 : 单元测试是针对
    的头像 发表于 12-09 10:22 2048次阅读