赞
踩
写过代码的开发人员都明白,软件bug是无法避免的。当代码中出现bug时,可能导致系统出现异常崩溃、内存泄漏等现象。因此软件开发人员需要具备强大的诊断能力将这些问题抓出来,而掌握这些诊断能力便成为软件开发人员成为高手的必经之路。
对于运行Linux系统的物联网设备而言,这个问题的答案简单而美好——“银弹”存在且有很多。诸如linux自身的coredump以及强大的valgrind等等,显然linux的成熟与强大为开发者提供了足够多的银弹。但更多的物联网设备中,尤其轻量级设备是无法运行linux的。本文便专注于讨论在这些设备上如何分析遇到的各种问题。
通常这样的轻量级物联网设备会运行在特定的RTOS上,部分设备甚至直接落跑没有OS。更有甚者,很多开发人员拿到的开发环境中唯一可以依赖的就是打印(没有JTAG无法使用GDB工具)。本文针对这样的艰苦环境,给出足够多的“银弹”,方便开发者分析各种问题。由于篇幅有限,所以每种“银弹”都以技术思路阐述为主,不再具体到讨论代码。
“银弹”的魅力在于,一个bug出现后开发人员依靠它可以不用连仿真器、不用打开gdb单步调试的情况下能快速找到bug原因。我们先看看一些例子:
RTOS领域大部分系统的诊断能力都十分薄弱,需要开发者自己学会并掌握大量的诊断能力。本章节给出这些诊断能力即“银弹”的实现思路,供读者参考。
栈回溯的目的是根据函数栈的内容,分析初函数调用关系。以嵌入式最常见的ARM架构为例,每一级函数调用都会把函数返回地址进行压栈(LP寄存器),开发者要做的就是在出现问题时,通过PC寄存器找到函数,通过SP寄存器找到栈的位置,然后一层层向上找到栈中保存的返回地址。思路如下图:
linux等其他桌面级OS也具备栈回溯能力,但其是通过-mapcs-frame或-funwind-tables编译选项实现的。这些选项对性能和代码体积都有影响,且嵌入式很多bsp代码并不开源,无法加入编译选项重编。所以这里讨论的是不依赖编译选项的栈回溯能力。
这里面给出一些启发性的关键技术点:
类似中断处理,当系统出现异常时会进入特定的向量处理流程。
对于开发者来说,如果使用OS本身已经接管了异常,并在异常发生时进行了必要的打印,那么最重要的就是如何理解这些异常信息了。以ARM的Cortex-M 3/4/7为例,可以参考这篇文档,分析异常原因。
Using Cortex-M3/M4/M7 Fault Exceptions
这里面最核心的思路时,通过异常时的PC找到出问题的指令,通过上述文档提及的下列关键寄存器的值,找到异常的原因。
还有,通常PC地址不足以分析出问题。比如异常出现在free函数中,那是谁调用了free导致问题就变得非常重要。此时可以结合上一节的“栈回溯”实现向上追溯,完成问题分析。
如果开发者使用的OS碰巧异常时打印的信息太少,那就需要自行补充异常处理代码了。从相关vector开始走读代码,并再合适的位置加上打印,这里篇幅有限不再详细展开。
对于嵌入式物联网设备,内存占用总是非常敏感的。当出现问题时,RAM或者Flash已经不足,那么开发者应该怎么入手分析问题呢?本小节先从静态内存统计入手,分析编译时已经确定的Flash与RAM消耗,找到耗内存大户。
静态内存分析方法的核心是利用好链接时产生的map文件,map文件通过连接选项‘-Map=xxx.map’可以产生。如果使用gcc进行链接,需要使用‘-Wl,-Map=output.map’编译选项,将其传递给链接器。
有了Map文件如何查看?map文件中罗列了每个函数、变量、只读数据的大小。对于小型嵌入式设备,函数与只读数据(比如字符串、初始化的data数据)是放入Flash中的,变量则是放入RAM中的。
可以使用python写一个分析map的脚本,实现类似这样的效果:
该脚本从map文件中提取各个函数、变量与只读数据的信息,并按照从大到小进行排序,最终输出到excle中。如上图所示,开发者就可以很方便查看这些超大的数据从何而来,进而实现静态内存优化的目的。
笔者会将该python脚本配套专门的文章发出,请持续关注本社区。
内存统计的核心是替换原有平台提供的malloc与free接口,增加辅助信息进行统计。这里给出一个简单的思路:
由此便可以实现:
另一个思路,便是重新实现OS的malloc与free算法,这里篇幅有限不再详细展开。
想做到内存泄漏分析,首先需要对申请到的内存进行追踪,增加如下的TraceInfo信息。
然后,找到系统内存稳定的时间点。比如一个物联网设备启动之后,做起点A,然后触发一个完整业务,等业务停止后,记作结尾B。接下来对比A与B之间的内存差异,类似如下流程。
上图橙色部分就是需要开发者为内存泄漏分析能力而增加的软件功能。最终找到 LEAK_CHECK Start(即例子中的A)与LEAK_CHECK End(即例子中的B)两个时间点之间,所有已申请未释放的内存。
开发者根据这些内存的栈回溯信息,便能找到代码中内存泄漏的点了。
内存踩踏问题的第一步是找到稳定复现的场景,然后增加诊断信息复现问题,最终捕获踩踏内存的元凶。
这里各处一些大致思路:
当系统出现意外的代码死循环时,可能导致串口没有任何打印,这种问题非常难以定位。即使对于有命令行交互的OS,也可能因为中断被屏蔽,无法执行命令,进而毫无定位问题的手段。问题如下图所示
这里笔者给出一种主动异常触发的机制,通过特殊的串口命令(或者其他中断)触发CPU跑到特殊的中断向量中,该向量主动执行非法指令进而触发系统异常。最终系统在异常处理中打印出完整的系统体检报告。
该技术的一些关键点:
与其让开发人员一步步自己实现这些银弹,能否提供更简便直接使用这些银弹的方法?
答案是有,只要使用阿里云旗下的物联网操作系统AliOS Things,配合配套SmartTrace工具。便直接可以用上上述各种银弹法宝了。
AliOS Things开源在这里:
https://gitee.com/alios-things/AliOS-Things
后续本社区会陆续更新一些SmartTrace相关的诊断维测系列文章,教会开发者使用AliOS Things时如何快速定位各种疑难杂正问题,成为bugfix高手。拥有“银弹”,享受开发的乐趣~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。