当前位置: 首页 > news >正文

嵌入式软件bug从哪里来,到哪里去

摘要:软件从来不是一次就能完美的,需要以包容的眼光看待它的残缺。那问题究竟为何产生,如何去除呢?

1、软件问题从哪来

软件缺陷问题千千万万,主要是需求、实现、和运行环境三方面。

1.1 需求描述偏差

客户角度的描述,在经过业务对接、产品经理的转述,最终呈现的软件需求可能已经偏离了原始的述求,开发人员基于自身经验的理解偏差,开发过程缺乏有效的沟通及监督,导致最终的软件功能与客户的核心诉求存在偏差。

1.2 异常处理机制不完善

嵌入式软件必定是运行在特定的硬件设备,硬件本身或环境问题等特殊干扰,开发人员因经验不足缺乏风险评估,面对电脑是无法全方位猜测、模拟各种异常环境下的差异,最终导致设备在特定场景下运行异常。

1.3 软件开发能力不足

嵌入式系统的复杂度与开发人员的能力矛盾,导致软件本身的逻辑存在缺陷。

2、软件开发与软件问题

关于软件bug的来源,排除不可控的外界因素,与软件开发人员相关,或者开发人员可以减少问题的发生的可能,从软件开发角度解决的方案如下:

2.1 重视需求分析

软件开发就是写程序,并设法使之运行,这是个错误的想法。软件结果与客户期望不一致,需求问题不全是软件开发的锅。大多数情况下客户的原始述求不会直接到软件开发,软件开发没法去反诉找客户确认,只能通过软件的实现形式去甄别不合理的,或者针对客观环境、研发团队的基础去评估风险。

比如客户要求可以设备可以定时1秒采集一次温度,精度要求0.0001摄氏度;或者要求数据采集持续采集24h后,每天12:00准点TCP上报后台服务器。这就需要考虑温度传感器的精度、RTC唤醒以及TCP联网时间、24小时采样数据的存储。如果硬件资源或者客观环境无法实现,盲目承诺客户,或者开始编码,最终结果可想而知。

软件开发是个人的任务,但开发前多沟通确认,进行风险评估反馈,减少开发的无用功,也是对开发人员的基本要求。

2.2 积累行业经验

嵌入式产品都是针对某个细分行业,见多识广,才能预判可能出现的异常,开发阶段有针对性的去处理,或者提前告知使用者去规避。有时候经验比技术能力重要。

2.3 提高开发水平

软件开发水平,首先是个人能力,熟悉软件SDK的应用,相关的操作系统、设计模式、调试方法等。软件开发能力大多数情况下决定软件质量和可维护性,这是个长期学习提高的过程,如果一定要提供捷径,那就是多阅读优秀的开源代码。

2.4 先设计再编码

软件开发不能随心所欲,先明确方案和大概的实现流程,胸有成竹,然后再开始编码,完善细节。这理论没毛病,但真正执行起来却比较难,大多数情况下都只在乎软件出结果,而实际上方案不合理,后期修修补补更浪费时间。如果制度和时间不允许,个人在纸上画画框图和结构,先构思再开发也能弥补,起码不至于南辕北辙。

2.5 编码规范

编码规范是软件开发团队合作的标准,嵌入式行业可以参考“华为技术C语言编程规范”,但实际开发过程,和前面的先设计再编码一样,各种不可控因素,比如项目进度压力和开发者水平与认知的差异,导致有编码规范却无法严格执行。随着软件工程规模的扩大,软件交期、代码同步、重构或交接,其风险也逐渐放大。存在编码规则并不能解决问题,只有强制执行才有意义。

2.6 代码缺陷静态检查与单元测试

软件质量是项目成败的关键点之一,在开发周期有限,人力资源不足的情况下,使用工具实现代码自动扫描,分析出潜在隐患点,可从源头减少软件bug,比如cppCheck、PC-lint等,实现代码自动静态分析,或者人工视检,有效规避简单的软件风险。

如果可能,最佳的选择是单元测试,单元测试比可交付成果本身更重要,文档注释不全时,单元测试就是设计文档;单元测试定义的API和用法,以及可能的使用风险点,就是最佳的参考范例;不足100%的覆盖率就是玩忽职守,开发人员应该全权负责测试自己造出的产品。依靠后期的黑盒测试发现问题,其消耗的人力物力,是编写单元测试的几倍,而且单元测试可以反复的自动测试。不过这种情况更多的是存在于开发理论中。

3、前期减少问题

软件问题的解决,有些不是个人能解决的,需要协调沟通,或者与研发团队的整体风格、制度有关。个人能决定的是软件具体逻辑,这也是体现个人技术能力的重点。

3.1 C语言基础

1、多看优秀代码,学习其技巧。

2、使用带参数检测的接口,比如优先选择snprintf,少用sprintf,其它str前缀的如strncmp也是,但要明白这类接口和memcmp区别。不同的编译器表现不一致,平时也要多关注。

在GCC中编译运行(设备):

char str[5];int ret = snprintf(str, 3, "%s", "abcdefg");//ret = 7 ,str = abchar str[99];int ret = snprintf(str, 99, "%s", "abcdefg");//ret = 7 ,str = abcdefg

注:snprintf的返回值为字符串的长度,且写入的字符串后面带有‘\0’结束符。

在VC中编译运行:

char str[5];int ret = snprintf(str, 3, "%s", "abcdefg");//ret = -1 ,str = abc  [后面不会自动补\0结束符]

3、注意函数返回类型,避免类型强制转换导致调用判断异常,有些编译器对隐示类型转换直接报错,因为它确实存在风险。

4、合理的使用sizeof、struct、union、weak等关键字,增加代码的可读性和可扩展性。

5、参数使用前,如数组小标,指针变量使用前必须先判断是否合法。

6、浮点数不能直接进行==和!=比较,等等,这些细节太多,可以参考《C陷阱与缺陷》。

7、讲的都会,说的都对,但真实际写代码,就容易各种小问题,主要还是态度问题,缺乏自我检查、自测的步骤,依靠测试发现bug去驱动研发调试修复是大忌。
 

3.2 动态内存

1、尽量做到申请与释放在同一个函数,申请内存后,先判断是否申请成功,再进行其它操作。

2、内存申请与释放之间有特殊情况return,要注意释放。

3、释放结构体指针前,注意该变量内部是否还有指针变量动态申请空间,先释放内部,再释放外部。

4、关于内存申请与释放,或使用越界是C语言的劣势,如果设备堆空间足够大,可以在申请时额外多申请固定空间,记录申请函数、长度、并在首尾标记,后续释放时检查内存区首尾标记是否被覆盖;或者查询是哪些函数申请的内存始终没有被释放。

3.3 跨平台问题

1、使用系统API前先判断自身传入参数的有效性和范围等是否符合要求,一般系统API是库文件,使用错误更难发现问题。

2、针对不同的平台常用的接口,务必增加适配层隔离,便于调试和后续移植。比如有的平台中断(SDK提供的中断回调不一定是硬件中断)不支持串口日志。

3.4 RTOS系统特性

1、多任务的竞争,在RTOS系统中,需要注意全局函数、全局变量的使用,避免互相竞争影响,对公共函数尽量做到可重入设计。

2、中断与任务的调度关系。

3、合理分配任务栈空间和消息队列的深度,函数内部尽量少用大数组。

3.5 个人素养

软件编码完成,不是能编译就收工了,其功能是否符合预期,开发人员自己检查是最高效的,很多问题都是开发不仔细,或者很简单的C基础应用错误,这不是技术问题而是心态。可以多看看开源代码,或者《C专家编程》等。

4、后期解决问题

如果软件问题不可避免,该如何去修复解决呢?

一般来说100%出现的问题都比较容易解决,找到相关代码仔细检查或者加点日志就能发现问题。难处理的是小概率出现的问题,稳定复现它就是成功的一半。

4.1 问题复现

稳定复现问题才能快速对问题进行定位、解决以及验证,如何提高复现的概率?

1、模拟复现条件问题只在特定的条件下出现,对于依赖外部输入的条件难以满足,可以考虑程序里预设直接进入对应状态,或者软件内部进行极端的压力测试。

2、提高相关代码执行频率,进行某个操作才可能出现异常,人工持续操作,或者软件频繁执行相应的功能,提高问题点的执行频率,加快复现速度。

3、增大测试样本量 个别样机难出现,如果条件允许,可以使用多个设备同时进行测试。一般情况下试产就是为了发现这类问题。

4.2 问题定位

缩小排查范围,确认引入问题的函数或代码片段。

1、打印日志 日志是最直接、简单的调试方法,在问题的可疑点增加日志输出,以此来追踪程序执行流程以及关键变量的值,观察是否与预期相符。

2、版本回退使用版本管理工具时可以通过不断回退版本,验证前面版本的情况,定位首次引入该问题的版本,针对该版本的改动进行排查。

3、二分注释“二分注释”类似二分查找法的方式注释掉部分代码,以此判断问题是否由注释掉的这部分代码引起。具体为将与问题不相干的部分代码注释掉一半,看问题是否解决,未解决则注释另一半,如果解决则继续将注释范围缩小一半,以此类推逐渐缩小问题的范围,确定是哪一块代码导致这个问题。

4、硬件协助,借助示波器、逻辑分析仪分析波形,必要时也请硬件协助分析;问题样机与正常样机的主控对调,看问题是否随芯片走。尤其是涉及驱动方面的问题,比如充电、中断、复位、外设通信调试异常时。

5、仿真调试 在线调试可以起到和打印LOG类似的作用,适合排查程序崩溃类的BUG,当程序陷入异常中断候可以直接STOP查看call stack以及内核寄存器的值,快速定位问题点,不过这需要硬件支持。

6、三板斧使用最多的是前面三种方法,这三板斧足以应付大部分业务逻辑问题;偶尔请硬件协助解决驱动问题,日常开发中的问题都能解决。个别系统层面或者架构不合理导致的深沉问题,要么花时间死磕coredunmp,要么联系原厂FAE协助,一般芯片方案商都提供技术支持。

4.3 问题修复与回归测试

1、缩小范围确定问题代码,再排查具体的函数,修复问题点

2、有些问题属于架构层面,比如和RTOS相关的竞争关系,这种就无法定位到具体问题代码点,只能在宏观上依靠经验或操作系统理论去解决

3、解决后需要进行回归测试,确认问题是否不再出现,也要确认修改不会引入其他新问题。

4.4 复盘

1、一般情况下最后发现原因都是很简单的几句话,比如数据越界或者循环体多执行一次,看起来都是很简单的基础用法,因为一句错误可能需要几周时间来发现解决,为什么当初写错而且没检查发现呢?

2、总结问题产生的原因及解决方法,今后如何防范,对其他平台否值得借鉴,做到举一反三,从失败中吸取经验。

5、心得

业务指示开发、测试驱动开发,这一荒谬方法论,体现在部门合作与职责不清,整体就是效率低下、互相推诿,在这样的环境下开发软件也很累。

相关文章:

嵌入式软件bug从哪里来,到哪里去

摘要:软件从来不是一次就能完美的,需要以包容的眼光看待它的残缺。那问题究竟为何产生,如何去除呢? 1、软件问题从哪来 软件缺陷问题千千万万,主要是需求、实现、和运行环境三方面。 1.1 需求描述偏差 客户角度的描…...

去掉WordPress网页图片默认链接功能

既然是wordpress自动添加的,那么我们在上传图片到wordpress后台多媒体的时候,就可以手动改变链接指向或者删除掉,问题是每次都要这么做很麻烦,更别说有忘记的时候。一次性解决这个问题有两种方法,一种是No Image Link插…...

UE学习笔记--解决滚轮无法放大蓝图、Panel等

我们发现有时候创建蓝图之后,右上角的缩放是1:1 但是有时候我们可能需要放的更大一点。 发现一直用鼠标滚轮像上滚动,都没有效果。 好像最大只能 1:1. 那是因为 UE 做了限制。如果希望继续放大,我们可以按住 Ctrl 再去…...

GO结构体

1. 结构体 Go语言可以通过自定义的方式形成新的类型,结构体就是这些类型中的一种复合类型,结构体是由零个或多个任意类型的值聚合成的实体,每个值都可以称为结构体的成员。 结构体成员也可以称为“字段”,这些字段有以下特性&am…...

芯科科技为全球首批原生支持Matter-over-Thread的智能锁提供强大助力,推动Matter加速成为主流技术

智能锁领域的先锋企业U-tec和Nuki选择芯科科技解决方案,成为Matter-over-Thread应用的领先者 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)今…...

面试数据库篇(mysql)- 06覆盖索引

原理 覆盖索引是指查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到 。 id name gender createdate 2 Arm...

[伴学笔记]01-操作系统概述 [南京大学2024操作系统]

文章目录 前言jyy:01-操作系统概述 [南京大学2024操作系统]为什么要学操作系统?学习操作系统能得到什么? 什么是操作系统?想要明白什么是操作系统:时间线:1940s1950s-1960s1960-1970s年代. 信息来源: 前言 督促自己,同时分享所得,阅读完本篇大约需要10分钟,希望为朋友的技术…...

c++二叉树

二叉树进阶 1.二叉搜索树(binary search tree) ​ 二叉搜索树天然就适合查找,对于满二叉树或者完全二叉树,最多搜索lgn次(就像是有序数组二分查找,每次搜索都会减少范围),极端情况简化成单链表就要走n次,即要走高度次…...

第19章-IPv6基础

1. IPv4的缺陷 2. IPv6的优势 3. 地址格式 3.1 格式 3.2 长度 4. 地址书写压缩 4.1 段内前导0压缩 4.2 全0段压缩 4.3 例子1 4.4 例子 5. 网段划分 5.1 前缀 5.2 接口标识符 5.3 前缀长度 5.4 地址规模分类 6. 地址分类 6.1 单播地址 6.2 组播地址 6.3 任播地址 6.4 例子 …...

浅谈人才招聘APP开发的解决方案

随着企业竞争加剧,高效、精准地招聘人才成为企业持续发展的关键。人才招聘系统能够简化招聘流程,提高效率,确保企业快速找到合适人才。同时,通过智能匹配和数据分析,提升招聘质量,优化候选人体验。因此&…...

大语言模型LLM推理加速:Hugging Face Transformers优化LLM推理技术(LLM系列12)

文章目录 大语言模型LLM推理加速:Hugging Face Transformers优化LLM推理技术(LLM系列12)引言Hugging Face Transformers库的推理优化基础模型级别的推理加速策略高级推理技术探索硬件加速与基础设施适配案例研究与性能提升效果展示结论与未来展望大语言模型LLM推理加速:Hug…...

JVM 第四部分—垃圾回收相关概念 2

System.gc() 在默认情况下,通过System.gc()或者Runtime.getRuntime().gc()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存 然而System.gc()调用附带一个免责声明,无法保证对垃…...

tritonserver学习之八:redis_caches实践

tritonserver学习之一:triton使用流程 tritonserver学习之二:tritonserver编译 tritonserver学习之三:tritonserver运行流程 tritonserver学习之四:命令行解析 tritonserver学习之五:backend实现机制 tritonserv…...

2024有哪些免费的mac苹果电脑深度清理工具?CleanMyMac X

苹果电脑用户们,你们是否经常感到你们的Mac变得不再像刚拆封时那样迅速、流畅?可能是时候对你的苹果电脑进行一次深度清理了。在这个时刻,拥有一些高效的深度清理工具就显得尤为重要。今天,我将介绍几款优秀的苹果电脑深度清理工具…...

UE5中实现后处理深度描边

后处理深度描边可以通过取得边缘深度变化大的区域进行描边,一方面可以用来做角色的等距内描边,避免了菲尼尔边缘光不整齐的问题,另一方面可以结合场景扫描等特效使用,达到更丰富的效果: 后来解决了开启TAA十字线和锯齿…...

Java面试值之集合

集合 1.HashMap底层?扩容机制?1.7-1.8的升级?2.HashMap的长度为什么是2的幂次方?3.HashMap 插入1.7和1.8的区别?4.什么是红黑树?O(logn)5.HashMap为什么会使用红黑树?6.ArrayList底层?扩容机制?7.LinkedList底层?扩容机制?8.ArrayList可以序列化,但是为什么不直接序…...

React之组件定义和事件处理

一、组件的分类 在react中,组件分为函数组件和class组件,也就是无状态组件和有状态组件。 * 更过时候我们应该区别使用无状态组件,因为如果有状态组件会触发生命周期所对应的一些函数 * 一旦触发他生命周期的函数,它就会影响当前项…...

LeetCode -55 跳跃游戏

LeetCode -55 跳跃游戏 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。…...

Android和Linux的嵌入式开发差异

最近开始投入Android的怀抱。说来惭愧,08年就听说这东西,当时也有同事投入去看,因为恶心Java,始终对这玩意无感,没想到现在不会这个嵌入式都快要没法搞了。为了不中年失业,所以只能回过头又来学。 首先还是…...

关于Node.js异常处理的教程

在Node.js开发中,异常处理是非常重要的一部分。良好的异常处理可以帮助我们及时发现和解决问题,提高系统的稳定性和可靠性。本教程将向您介绍Node.js中异常处理的最佳实践和策略。 1. 使用try-catch捕获同步异常 在Node.js中,可以使用try-c…...

OpenLayers 可视化之热力图

注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...