嵌入式软件bug分析基本要求

news2024/11/17 14:49:06

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

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 = ab

  char 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系统中,需要注意全局函数、全局变量的使用,避免互相竞争影响,对公共函数尽量做到可重入设计,具体实现方案请关注微信公众号 嵌入式系统 的《基于RTOS的软件开发理论》 。

2、中断与任务的调度关系 请关注微信公众号 嵌入式系统 的《基于RTOS的软件开发理论》。

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、心得

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1435580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构高级算法

目录 最小生成树 Kruskal(克鲁斯卡尔)(以边为核心) 9) 不相交集合(并查集合) 基础 Union By Size 图-相关题目 4.2 Greedy Algorithm 1) 贪心例子 Dijkstra Prim Kruskal 最优解(零钱兑换)- 穷举法 Leetcode 322 最优解(零钱兑换)- 贪心法 Leetcode 322 3)…

XUbuntu22.04之两款实用画笔工具(二百一十)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

AR特效自研AI算法技术解决方案

在当今这个高速发展的数字化时代,增强现实(AR)技术已经成为企业创新和市场竞争的重要手段。美摄科技凭借对AI技术的深厚积累,为企业提供了一套创新的AR特效自研AI算法技术解决方案,旨在满足企业在AR领域的多元化需求。…

运行vue3项目出现的问题

Mac 系统运行 vue 启动项目时报错: Permission denied 的解决方式 控制台运行 chmod 777 node_modules/.bin/vue-cli-service 如果 npm run dev 还报这个错 控制台运行 node node_modules/esbuild/install.js

【工具使用】arm-gcc工具链安装

一,简介 本文介绍如何在linux环境安装arm相关工具链。供参考 二,操作步骤 在linux环境下,解压压缩包: tar -jxvf gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2.tar.bz2.tar.bz2解压成功,出现文件夹&a…

CSS:两列布局

两列布局是指一列宽度固定&#xff0c;另一列自适应。效果如下&#xff1a; HTML: <div class"container clearfix"><div class"left"></div><div class"right"></div> </div>公共 CSS&#xff1a; .con…

openstack(T版)公有云--Dashboard服务

公有云上OpenStack Train最小化安装_openstack最小化部署-CSDN博客 我的opensatck(T)是参考上面链接去部署完成的&#xff0c;在部署完Dashboard服务后&#xff0c;将要用浏览器访问的时候出现了404 500 Internal Server Error 等各种各样的问题&#xff0c;以下是我排查问题…

Leetcode—57. 插入区间【中等】

2024每日刷题&#xff08;113&#xff09; Leetcode—57. 插入区间 实现代码 class Solution { public:vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {vector<vector<int>> an…

thinkphp6入门(17)-- 网站开发中session、cache、cookie的区别

Session&#xff08;会话&#xff09;: 定义&#xff1a; Session是一种用于在服务器端存储用户信息的机制&#xff0c;以跟踪用户的状态。 数据存储位置&#xff1a; 存储在服务器端&#xff0c;可以存在于内存、数据库或文件系统中。 生命周期&#xff1a; 存在于用户访问应…

R语言学习case9:ggplot基础画图(Scatter Metrics 矩阵散点图)

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;利用ggplot工具包绘图 开发者们在ggplot2的基础…

机器学习聚类算法

聚类算法是一种无监督学习方法&#xff0c;用于将数据集中的样本划分为多个簇&#xff0c;使得同一簇内的样本相似度较高&#xff0c;而不同簇之间的样本相似度较低。在数据分析中&#xff0c;聚类算法可以帮助我们发现数据的内在结构和规律&#xff0c;从而为进一步的数据分析…

工业以太网交换机引领现代工厂自动化新潮流

随着科技的飞速发展&#xff0c;现代工厂正迎来一场前所未有的自动化变革&#xff0c;而工业以太网交换机的崭新角色正是这场变革的关键组成部分。本文将深入探讨工业以太网交换机与现代工厂自动化的紧密集成&#xff0c;探讨这一集成如何推动工业生产的智能化、效率提升以及未…

【自定义序列化器】⭐️通过继承JsonSerializer和实现WebMvcConfigurer类完成自定义序列化

目录 前言 解决方案 具体实现 一、自定义序列化器 二、两种方式指定作用域 1、注解 JsonSerialize() 2、实现自定义全局配置 WebMvcConfigurer 三、拓展 WebMvcConfigurer接口 章末 前言 小伙伴们大家好&#xff0c;上次做了自定义对象属性拷贝&#x…

【LangChain-04】利用权重和偏差跟踪和检查LangChain代理的提示

利用权重和偏差跟踪和检查LangChain代理的提示 一、说明 考虑到&#xff08;生成&#xff09;人工智能空间&#xff0c;&#xff08;自主&#xff09;代理现在无处不在&#xff01;除了更强大且幸运的是开放的大型语言模型&#xff08;LLM&#xff09;之外&#xff0c;LangCh…

Android中设置Toast.setGravity()了后没有效果

当设置 toast.setGravity()后&#xff0c;弹窗依旧从原来的位置弹出&#xff0c;不按设置方向弹出 类似以下代码&#xff1a; var toast Toast.makeText(this, R.string.ture_toast, Toast.LENGTH_SHORT)toast.setGravity(Gravity.TOP, 0, 0)//设置toast的弹出方向为屏幕顶部…

linux centos安装neofetch

简介 neofetch是一个命令行工具&#xff0c;可以用来显示系统的基本信息和硬件配置。它支持多种操作系统&#xff0c;包括Linux、macOS和Windows等。 安装 增加yum源 curl -o /etc/yum.repos.d/konimex-neofetch-epel-7.repo https://copr.fedorainfracloud.org/coprs/konime…

【学网攻】 第(22)节 -- DHCP中继配置

系列文章目录 目录 系列文章目录 文章目录 前言 一、DHCP中继是什么&#xff1f; 二、实验 1.引入 实验目的理解DHCP中继的功能&#xff1b; 实验背景 实验步骤新建Packet Tracer拓扑图 实验设备PC 2台&#xff1b;Server-PT(Web服务器) &#xff0c;Switch_2950-24 …

「深度学习」门控循环单元GRU

一、梯度消失问题 梯度消失&#xff1a; 基础的 RNN 模型不善于处理长期依赖关系&#xff0c;有很多局部影响&#xff0c;很难调整自己前面的计算。y^{<i>} 仅仅受自己附近的值影响。 解决方法&#xff1a;GRU 或 LSTM 梯度爆炸&#xff1a; 反向传播时&#xff0c;随着…

C++多线程:this_thread 命名空间

std::this_thread 是 C 标准库中提供的一个命名空间&#xff0c;它包含了与当前线程相关的功能。这个命名空间提供了许多与线程操作相关的工具&#xff0c;使得在多线程环境中更容易进行编程。 源码类似于如下&#xff1a; namespace std{namespace this_thread{//...........…

Sentinel(理论版)

Sentinel 1.什么是Sentinel Sentinel 是一个开源的流量控制组件&#xff0c;它主要用于在分布式系统中实现稳定性与可靠性&#xff0c;如流量控制、熔断降级、系统负载保护等功能。简单来说&#xff0c;Sentinel 就像是一个交通警察&#xff0c;它可以根据系统的实时流量&…