1.软件问题从何而来?1.1需求描述偏差从客户角度描述,经过业务对接和产品经理报告后,最终的软件需求可能与原来的描述有偏差,开发人员根据自身经验理解有偏差,开发过程缺乏有效沟通和监督,导致最终的软件功能与客户的核心需求出现偏差。1.2异常处理机制不完善。嵌入式软件必须运行在特定的硬件设备上,硬件本身或环境问题等特殊干扰,开发人员由于经验不足缺乏风险评估,面对计算机无法完全猜测和模拟各种异常环境下的差异,最终会导致设备在某些场景下运行异常。1.3软件开发能力不足嵌入式系统的复杂性与开发人员的能力相矛盾,导致软件本身的逻辑存在缺陷。2.软件开发与软件问题关于软件BUG的来源,排除不可控的外部因素,与软件开发人员有关,或者开发人员可以减少出现问题的可能性,从软件开发的角度解决方案如下:2.1强调需求分析认为软件开发就是编写程序并试图让它们运行的??想法是错误的。软件结果与客户期望不符,需求问题不完全是软件开发的错。大多数情况下,客户最初的要求不会直接到软件开发,软件开发也没有办法反诉找客户确认。只能通过软件实现形式识别不合理的地方,或者根据客观环境和研发团队的基础进行评估。风险。例如,客户要求设备能够每隔一定时间每秒采集一次温度,精度要求达到0.0001摄氏度;或者数据采集需要24小时连续采集,然后每天12:00通过TCP将数据上报给后台服务器。这需要考虑温度传感器的精度、RTC唤醒、TCP联网时间以及24小时采样数据的存储。如果硬件资源或者客观环境无法实现,一味的答应客户,或者开始coding,最后的结果可想而知。软件开发是个人的工作,但开发前多沟通和确认,风险评估反馈,减少无用的开发工作量也是对开发者的基本要求。2.2积累行业经验嵌入式产品都是针对某个细分行业的。只有见多识广,才能预知可能出现的异常,在开发阶段有针对性地处理,或者提前告知用户,避免出现异常。有时候经验比技术能力更重要。2.3提高开发水平软件开发水平,首先是个人能力,对软件SDK的应用、相关操作系统、设计模式、调试方法等的熟悉程度。在大多数情况下,软件开发能力决定了软件质量和可维护性。这是一个长期的学习和提高的过程。如果一定要提供一个捷径,那就是阅读更多优秀的开源代码。2.4先设计后编码软件开发不能随心所欲,先明确方案和大概的实现流程,有了周密的方案,再开始编码,完善细节。这个理论没有错,只是实际执行起来比较困难。大多数情况下,我们只关心软件的结果,但实际上计划是不合理的,后期修复更费时间。如果系统和时间不允许,个人可以通过在纸上画框架图和结构来弥补,先构思再开发,至少不会走同一条路。2.5编码规范编码规范是软件开发团队合作的标准。嵌入式行业可以参考《华为技术C语言编程规范》,但实际开发过程和前面的设计,然后编码是一样的。压力和开发人员水平和认知的差异导致编码标准无法严格执行。随着软件工程规模的扩大,软件交付、代码同步、重构或移交的风险逐渐增加。现有的编码规则并不能解决问题,只有强制执行才有意义。2.6代码缺陷静态检查和单元测试软件质量是项目成败的关键之一。在开发周期有限、人力资源不足的情况下,利用工具自动扫码分析潜在隐患,可以从源头减少软件bug,如cppCheck、PC-lint等,实现代码自动静态分析,或手动目视检查,有效规避简单软件风险。如果可能,最好的选择是单元测试。单元测试比可交付成果本身更重要。当文档不完整时,单元测试就是设计文档;单元测试定义的API和用法,以及可能的使用风险点,都是最好的一个参考例子;低于100%的覆盖率是疏忽,开发人员应全权负责测试他们构建的产品。靠后期黑盒测试来发现问题,所消耗的人力物力是编写单元测试的数倍,而且单元测试可以自动反复测试。但是,这种情况在发展论中存在较多。可以参考微信公众号嵌入式系统的《代码的保养》第二章。3、减少前期问题软件问题的解决,有些不是靠个人就能解决的,需要协调沟通,或者跟研发团队的整体风格和体制有关。个人能决定的是软件的具体逻辑,这也是体现个人技术能力的重点。3.1C语言基础看优秀代码,学其本领。使用带参数检测的接口,比如优先使用snprintf,少用sprintf,还有strncmp等其他str前缀,但是一定要明白这种接口和memcmp的区别。不同的编译器行为不一致,所以你应该多加注意。在GCC(device)中编译运行:charstr[5];intret=snprintf(str,3,"%s","abcdefg");//ret=7,str=abcharstr[99];intret=snprintf(str,99,"%s","abcdefg");//ret=7,str=abcdefg注意:snprintf的返回值是字符串的长度,写入的字符串后跟'\0'结束符。在VC中编译运行:charstr[5];intret=snprintf(str,3,"%s","abcdefg");//ret=-1,str=abc【末尾不会自动补上\0符号】注意函数的返回类型,避免类型强制导致的调用判断异常。隐式类型转换有些编译器直接报错,因为它确实有风险。合理使用sizeof、struct、union、weak等关键字,增加代码的可读性和可扩展性。在使用数组下标、指针变量等参数之前,首先要判断它们是否合法。浮点数不能直接与==、!=等进行比较,具体内容太多,请参考《C陷阱与缺陷》。什么都可以讲,说的也对,但是实际写代码的时候容易出现各种小问题,主要是态度问题,缺少自检自测步骤,靠测试找bug对驱动进行开发、调试和修复是大忌。3.2动态内存的申请和释放尽量在同一个函数中进行。申请内存后,先判断申请是否成功,再进行其他操作。内存申请和释放之间有一个特殊的返回,所以要注意释放。释放结构体指针前,注意变量内部是否还有指针变量动态申请空间,先释放内部,再释放外部。关于内存的申请和释放,或者使用越界是C语言的劣势。如果设备堆空间足够大,可以在申请的时候申请额外的固定空间,记录申请的函数,长度,并标记起止,释放时检查内存区的起止标记是否被覆盖;或查询哪些函数分配了尚未释放的内存。3.3跨平台问题使用系统API前,首先判断传入参数的有效性和范围是否满足要求。一般系统API都是一个库文件,使用不当更难发现问题。对于不同平台上常用的接口,需要增加适配层的隔离,方便调试和后续移植。比如部分平台中断(SDK提供的中断回调不一定是硬件中断)不支持串口日志。3.4RTOS系统特点多任务竞争。在RTOS系统中,需要注意全局函数和全局变量的使用,避免相互竞争和影响,对公共函数尽量做到可重入设计。具体实施方案请关注微信♂嵌入《基于RTOS的软件开发理论》公式系统。中断与任务的调度关系,请关注微信《基于RTOS的软件开发理论》嵌入式系统《基于RTOS的软件开发理论》。合理分配任务栈空间和消息队列深度,函数内部尽量少用大数组。3.5个人素养软件写完代码,不代表可以编译就完事了。其功能是否符合预期,是开发者自己检查的最有效方式。很多问题都是由于粗心的开发或简单的C基础应用错误引起的。这不是技术问题。但心态。可以多看开源代码,或者《C专家编程》等。4.后期解决问题如果软件问题无法避免,如何解决?一般来说,100%出现的问题都是比较容易解决的。找到相关的代码仔细检查或者添加一些日志来找出问题所在。难处理的是小概率出现的问题。它的稳定复发是成功的一半。4.1问题复现和稳定性问题复现可以快速定位、解决和验证问题。如何提高复发概率?模拟重复条件。该问题仅在特定条件下发生。很难满足依赖于外部输入的条件。考虑程序中的预设直接进入相应的状态,或者软件内部的极限压力测试。增加相关代码的执行频率。只有在执行特定操作时才会出现异常。人工连续操作,或软件频繁执行相应功能,增加问题点的执行频率,加快复现速度。加大测试样本量,个别样机难以出现,条件允许的情况下可同时使用多台设备进行测试。一般来说,试生产就是为了发现这样的问题。4.2故障排除缩小排查范围,确认引入问题的函数或代码片段。打印日志,日志是最直接简单的调试方式,在问题可疑点添加日志输出,从而跟踪程序执行过程和关键变量的取值,观察是否符合预期。版本回滚。在使用版本管理工具时,可以不断回滚版本,验证之前版本的情况,第一时间定位引入问题的版本,排查本版本的变化。二进制注释,“二进制注释”类似于二分查找法,将部分代码注释掉,从而判断问题是否出自被注释掉的部分代码。具体来说,把与问题无关的一半代码注释掉,看看问题是否解决。如果问题没有解决,评论另一半。导致这个问题。硬件辅助,借助示波器和逻辑分析仪分析波形,必要时请硬件辅助分析;把有问题的样机和正常样机的主控对调一下,看看是不是芯片出问题了。尤其是涉及到驱动问题,比如充电、中断、复位、外设通讯调试异常等。模拟调试,在线调试可以起到类似打印LOG的作用,适用于排查程序崩溃bug,当程序陷入异常中断时,可以直接停下来查看调用栈和内核寄存器的值,快速定位问题点,但这需要硬件支持。三招,用的最多的是前三种方法,这三招足以应对大部分业务逻辑问题;偶尔请硬件帮忙解决驱动问题,日常开发中的问题都能迎刃而解。对于个别系统层面的严重问题或架构不合理,要么花时间在coredunmp上,要么联系原FAE寻求帮助。一般由芯片方案商提供技术支持。4.3问题修复与回归测试缩小范围确定问题代码,再检查具体功能修复问题点。有些问题属于架构层面,比如RTOS相关的竞争关系。这种问题无法定位到具体的问题代码点,只能通过经验或操作系统理论在宏观层面解决。解决方案解决后,需要进行回归测试,确认问题是否不再出现,并确认修改后不会引入其他新的问题。4.4一般情况下,重放的原因一般是一些简单的语句,比如数据越界或者循环体被多执行了一次。这些看似很简单的基本用法,因为一个错误可能需要数周的时间才能找到并解决。为什么一开始就写错了,不检查?总结问题的原因和解决方法,以后如何预防,是否值得其他平台学习,举一反三,从失败中吸取教训。5、体验业务指令开发和测试驱动开发的方法论荒谬,体现在部门协作和职责不清。整体效率低下,相互推诿。在这样的环境下开发软件也是很累的。
