1、定位思路参考
各种各样的错误现场,使用的手段不尽相同。
正常问题有较固定的模式去一层层剥开现象,找到本质;
还有一些问题,需要定位者仔细观察发现每一处不合乎逻辑(代码逻辑、内存逻辑等)可疑点,忽略任意一点可疑点,极大概率就会导致错过真相,含有疑点地去下结论,误判的可能性极高。
还有一些复杂问题,看似毫无逻辑性,有时需要跳出固定思维,换个层面或者角度;
总之,大家在定位问题中都会有一些自己的心得,遇到过各种各样的情况。
此处从内核底层软件角度出发,提供一些基本有效的定位手段和分析方法,给大家参考。
1.1、找致命错误或者异常现场
从内核系统角度,在运行中会检测内存的异常状态。
首先要判断系统出现哪种问题,如下面是内核检测最常见的两种出错状态:
发现内存问题,如内存释放时检测内存踩踏,内存申请时,发现空间不足,即会报“!! Fatal Error !!”;
系统出现异常后,内核会进入异常处理流程,串口log会输出“!! Exception !!”。
1.2、内存释放检测致命错误问题
- 看打印,找报错内存
WARNING, memory maybe corrupt!! 0x81a2c378
- 串口可以输入,输入命令查看内存状态
p 0x81a2c378 32 4 // 输入32个4字节大小数据
- 搜 !used !free关键字,其为内核检测出被踩内存
- 观察出问题内存的前一块,或者之前内存块,一般简单情况都是前一块尾部踩后一块内存头:
- 通过汇编或者基本的调用栈,分析前面一块内存使用情况
重点关注前一块内存申请后的操作,是否有memset、memcpy动作,相关代码逻辑是否有合法性判断。从汇编来看是最准确的代码逻辑,不容易像C代码一样,可能存在各种宏开关之类的误导。但是逻辑复杂,看汇编则不合理,需要大家一起检视代码确认。
也会存在内存直接使用越界的情况,比如结构体强转后越界访问,此时可以通过编译、PC-lint告警之类简单排查,也可以直接检视汇编或者C代码。
检视相关后一致认为前面的内存没有异常使用的情况下,这种可能就是二次现场或者飞踩的情况。不同情况因地制宜,参考下面几种推荐方法:
- 优先考虑加watchpoint监控内存;
- p前后的内存值,找规律,尤其是看内存被改写成何值,有时可以关联被谁误改写;
- 看当前的内存头状态,是否存在已经被释放即use-after-free的情况;
- 看任务栈是否溢出:任务栈溢出会导致各种奇怪的问题,看当前可用任务栈是否已经很少;
- 排查业务逻辑自身问题,尤其涉及模块代码规范性问题,异常状况分支处理
- 排查多核多任务资源保护问题;
- 加维测:在合理的位置、合适的判断条件加合适的维测,能达到让问题尽快暴露,输出更多有用信息的效果。
1.3、看任务栈sp是否溢出
内核中有任务栈溢出的检测,如果检测到栈溢出了,则会即使报错。但是由于不可能检测所有栈范围,用户的代码如果访问时,跨过了栈检测的魔术字位置,那系统就不能及时报错,从而导致后续奇怪的现场。