结合AliOS Things谈嵌入式系统通用问题定位方法(3):问题定位思路-阿里云开发者社区

开发者社区> 开发者小助手> 正文

结合AliOS Things谈嵌入式系统通用问题定位方法(3):问题定位思路

简介: 各种各样的错误现场,使用的手段不尽相同。
+关注继续查看

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是否溢出

内核中有任务栈溢出的检测,如果检测到栈溢出了,则会即使报错。但是由于不可能检测所有栈范围,用户的代码如果访问时,跨过了栈检测的魔术字位置,那系统就不能及时报错,从而导致后续奇怪的现场。

异常时,内核会自动打印出任务信息,用户也可以在串口输入tasklist来查看

  • 对比当前SP的值,和任务栈范围

如当前 SP 0x4F8097A8

查看对应任务信息:

在任务栈的范围内则正常。

另外可以查看当前任务栈的MinFree,其表示任务栈使用过程中的最小剩余值,如果发现其很小,则基本上栈已经溢出了。

 

1.4、手动解栈

偶尔会遇到各种原因,现场调用栈没有打印出来。有栈解析遇到特殊情况的问题,也有可能pc非法等造成解析不全之类的。这时候需要人工去解调用栈。

其实方法很简单:

找到当前的SP值 => 找到当前任务栈的内容 => 将大于等于SP位置的内存都过滤 =>

找到函数执行轨迹。

 

1.5、Addr2line

如在ELF结构章节中提到的,可以通过addr2line命令快速关联某汇编和对应的.c位置

localhost: arm-none-eabi-addr2line -e ./helloworld@developerkit.elf 0x80021f8

/workspace/aos_gerrit_new/core/osal/aos/rhino.c:815

 

1.6、查看当前任务状态

tasklist

异常现场会自动打印当前任务状态,用户可以直接输入tasklist查看当前任务状态

 

1.7、查看某任务调用栈

taskbt 任务ID

 

1.9、查看任务/信号量/mutex状态

debug

如从上图可以看到,dyn_mem_proc_task任务在等待某个sem,处于pend状态。

 

1.9、查看任务CPU占有率

cpuusage

以此定位是否有任务超负荷占用CPU

 

1.10、查看内存值

关键时候,需要查看内存,通过p命令

下面命令打印32个4字节内存:

 

1.11、Dumpsys导内存

怀疑内存堆有问题,可以通过dumpsys

mm_info命令查看所有内存blk状态,然后再结合p内存命令定位。

 

1.12、强制异常 (fiq)

如果系统停止运行,怀疑死锁之类的问题,可以通过

$#@!

命令通过让CPU进入fiq打断现场,内核会将当前的各种状态快照输出来分子当前的状态,包括:通用寄存器现场、任务状态、内存状态等。

 

1.13、异常定位

非法的内存、指令访问会导致异常。内存被踩,如果没有及时检测魔术字错误,用户访问的时候也会触发异常。

下面以某一实际遇到的异常log为例:

  • 看异常类型,最普遍的区分数据异常还是指令异常

 

  • 看异常位置,核对异常状态寄存器和通用寄存器

异常PC:
 

  1. kernel space exception
  2. ========== Regs info  ==========
  3. PC      0x4060E160

对应汇编:

4060e160:   e5d4900f    ldrb    r9, [r4, #15]

查看当前r4值:

R4      0x4FFFFFF4

查看数据异常寄存器:

DFAR    0x50000003

从上面看出,R4 = 0x4FFFFFF4,导致访问了 0x4FFFFFF4 + 15位置的内存异常。

那问题就变为R4为何不对。

  • 分析导致异常寄存器的前后赋值关系在汇编中(此时C代码也可),查看r4的赋值:

(下面出错位置代码较长,有截取汇编上下文,关注下面加“//”部分)
 

  1. 4060e098 <scanSetRequestChannel>:
  2. 4060e098:   e92d43f0    push    {r4, r5, r6, r7, r8, r9, lr}
  3. 4060e09c:   e24dd04c    sub sp, sp, #76 ; 0x4c
  4. 4060e0a0:   e1a07001    mov r7, r1
  5. 4060e0a4:   e1a04002    mov r4, r2                                              //
  6. ***********省略*****************
  7. 4060e160:   e5d4900f    ldrb    r9, [r4, #15]                                   //
  8. 4060e164:   e3a02010    mov r2, #16
  9. 4060e168:   e0880206    add r0, r8, r6, lsl #4
  10. 4060e16c:   e1a01004    mov r1, r4
  11. 4060e170:   e2800028    add r0, r0, #40 ; 0x28
  12. 4060e174:   e2866001    add r6, r6, #1
  13. 4060e178:   ebfef704    bl  405cbd90 <memcpy>
  14. 4060e17c:   e3a02020    mov r2, #32
  15. 4060e180:   e28d1028    add r1, sp, #40 ; 0x28
  16. 4060e184:   e1a00009    mov r0, r9
  17. 4060e188:   ebffff87    bl  4060dfac <scanSetBit>
  18. 4060e18c:   e2855001    add r5, r5, #1                                            //

指令4060e160位置为异常位置;

4060e0a4位置R4为从R2赋值,R2为scanSetRequestChannel的第三个参数;

  • 看压栈现场

此时怀疑scanSetRequestChannel入参有问题,那么可以看这个函数调用过来的现场,从Call

stack找出上一层位置:

  1. ========== Call stack ==========
  2. backtrace : 0x4060E160
  3. backtrace : 0x405FBFB4                       //
  4. backtrace : 0x405F5558
  5. backtrace : 0x40628B60
  6. backtrace : ^task entry^

 

查看0x405FBFB4汇编
 

  1. 405fbf94:   e2842f6b    add r2, r4, #428    ; 0x1ac                    //
  2. 405fbf98:   e5d43001    ldrb    r3, [r4, #1]
  3. 405fbf9c:   e1a00005    mov r0, r5
  4. 405fbfa0:   e58d7000    str r7, [sp]
  5. 405fbfa4:   e59411a8    ldr r1, [r4, #424]  ; 0x1a8
  6. 405fbfa8:   e2433003    sub r3, r3, #3
  7. 405fbfac:   e16f3f13    clz r3, r3
  8. 405fbfb0:   e1a032a3    lsr r3, r3, #5
  9. 405fbfb4:      eb004837     bl  4060e098 <scanSetRequestChannel>      //
  10. 405fbfb8:      eaffffab     b   405fbe6c <aisFsmSteps+0x794>          //

指令位置405fbf94显示R2由R4 + 0x1ac所得,那么就需要找到R4,而我们知道R4一般会在被调用函数scanSetRequestChannel压栈处理,那么回头看scanSetRequestChannel的汇编开头

  1. 4060e098 <scanSetRequestChannel>:
  2. 4060e098:   e92d43f0    push    {r4, r5, r6, r7, r8, r9, lr}              //

 

那么找到压栈位置,就可以找到R4的值,一般通过被压栈的LR位置来搜索,当前进入scanSetRequestChannel时LR的值是405fbfb8,可以在栈的上下文搜索(或者通过当前SP)405fbfb8,找到下面栈位置,并往前搜索7个单元,找到R4压栈的位置,显示R4=0x4F7FEE98。

  1. stack(0x4F8097E8) : 0x00043753 0x8184D561 0x4F7FEE98 0x4F7FEE98 (R4)     //
  2. stack(0x4F8097F8) : 0x4F769498 0x4F7FEFA0 0x4F80FD88 0x00000000
  3. stack(0x4F809808) : 0x4F78FF8C 0x405FBFB8(LR) 0x4F80FD88 0x4071B904

 

到目前为止,我们找到R4 = 0x4F7FEE98,这个值看着挺正常,而且和出错时R4 0x4FFFFFF4地址相去甚远。

 

这里我们可以推测一个结论,至少R4在传入的时候还是好的,是在scanSetRequestChannel函数内部运行时发生了问题,怀疑大量循环之后或者不合理的数据运算造成了问题。

  • 观测当前LR(这一步可以在前面观察)

当前异常时LR一般是函数调用时候当前PC跳转前的下一条指令,会随着函数调用不断变化,因此从LR我们能定位当前处于哪个函数范围内。

观察到当前LR 0x4060E18C

对应汇编:

从这一点看出,LR已经在发生异常指令0x4060e160之后,推测确实是在进入了scanSetRequestChannel之后,在进行来回的循环之后才导致的异常。

那么此处有理由怀疑scanSetRequestChannel的R2入参正确的情况下,可能其他参数有问题,造成了R2循环累加的溢出。

同样按照分析R4在栈位置的原理,以及参考对应C代码,可以继续发现下面问题:

arChannel 是出问题的指针源头,u4ScanChannelNum是其索引,有极大的嫌疑有问题。

  1. void scanSetRequestChannel(IN struct ADAPTER *prAdapter,
  2.         IN uint32_t u4ScanChannelNum,
  3.         IN struct RF_CHANNEL_INFO arChannel[],
  4.         IN uint8_t fgIsOnlineScan,
  5.         OUT struct MSG_SCN_SCAN_REQ_V2 *prScanReqMsg)

 

再次分析汇编

u4ScanChannelNum作为参数被保存在R7,根据上下文R7没有被修改。
 

  1. 4060e098 <scanSetRequestChannel>:
  2. 4060e098:   e92d43f0    push    {r4, r5, r6, r7, r8, r9, lr}
  3. 4060e09c:   e24dd04c    sub sp, sp, #76 ; 0x4c
  4. 4060e0a0:   e1a07001    mov r7, r1                                    //

再次查看异常寄存器现场,发现R7果然是一个很大的值。

 

最后一公里:

至此只能说找到出问题的点,scanSetRequestChannel的参数u4ScanChannelNum有问题,至于为什么R7有问题,代码是否有合法性判断,还是被其他引起的,此为后话,最后一公里,往往会产生很多变化,需要在实践中继续积累经验,才能最终定位根因,而不能仅停留在表明的错误。

 

2、质量工作

大部分时候,由于项目周期较短等原因,很多时候忽略了基本的代码质量检查。这种做法,对于商用产品来讲基本上可能是致命的,也是极度危险的。

除了少部分由代码逻辑之类引起的,剩余的还有一大部分问题都是由于代码的不规范引起的。解决不规范的代码所消耗的精力,往往比通过实际联调定位解决问题来的高效得多。

而且在某段时间实际运行中能发现的问题总归是有限的,远不如彻底根治不规范代码引起的风险来的有效。

下面列出的质量工作能够极大提高代码和产品质量:

  • 编译告警
  • PC-LINT检查
  • Coverity检查
  • Fortify检查
  • 详细覆盖全面的单元测试
  • 完善的逻辑组合测试
  • 顶层的黑盒测试
  • 其他

 

3、参考文档

《ARM相关架构文档》

《ELF格式解析》

 

4、传送门

HaaS100开发调试系列 之 使用AliOS Things诊断调试组件定位Bug

 

开发者技术支持

如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号

更多技术与解决方案介绍,请访问阿里云AIoT首页https://iot.aliyun.com/

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
iOS11问题: 定位服务在iOS11系统上不能使用?
iOS11问题: 定位服务在iOS11系统上不能使用? Q:我刚刚用iOS11 SDK重新构建了应用程序,发现定位服务现在根本不起作用。 原因:A:因为苹果现在增加了一项新的隐私保护功能 NSLocationAlwaysAndWhenInUseUsageDeion, 并且原有的 NSLocationAlwaysUsageDeion 被降级为 NSLocationWhenInUseUsageDeion。
974 0
iOS Abort问题系统性解决方案
本文将针对iOS客户端的Abort问题,进行根因定位分析,并提出系统性解决方案。
4606 0
阿里云物联网平台服务端订阅AMQP典型问题——Client failed to authenticate using SASL: PLAIN
服务端可以直接订阅产品下多种类型的消息:设备上报消息、设备状态变化通知、设备生命周期变更、网关发现子设备上报、设备拓扑关系变更等。配置服务端订阅后,物联网平台会将产品下所有设备的已订阅类型的消息转发至您的服务器。
1144 0
[裴礼文数学分析中的典型问题与方法习题参考解答]4.5.13
设 $f(x)$ 于任一有限区间 $[0,a]\ (a>0)$ 上正常可积, 于 $[0,\infty)$ 上绝对可积, 则 $$\bex \vlm{n}\int_0^\infty f(x)|\sin nx|\rd x =\frac{2}{\pi}\int_0^\infty f(x)\rd x.
562 0
[裴礼文数学分析中的典型问题与方法习题参考解答]4.5.12
$f(x)$ 在 $[a,+\infty)$ 上连续且 $\dps{\int_a^{+\infty}f(x)\rd x}$ 收敛. 问能否断定: $\exists\ x_n\to\infty$, 使 $\dps{\vlm{x}f(x_n)=0}$? 为什么? (南开大学)   解答: 肯定.
486 0
wamp You don&#39;t have permission to access / on this server等问题的解决.
原文: wamp You don't have permission to access / on this server等问题的解决. 安装完wamp之后,安装网上的教程设置虚拟路径,出现了问题,同样的问题由不同的原因导致。
948 0
[裴礼文数学分析中的典型问题与方法习题参考解答]5.1.5
证明: 若删去调和级数中所有分母含有数字 $9$ 的项, 则新级数收敛, 且和小于 $80$.   证明: 对 $m=1,2,\cdots$, $[10^{m-1},10^m)$ 中的自然数的十进制表示中没有 $9$ 的个数为 $8\cdot 9^{m-1}$ (除首位可能为 $1,2,\cdo...
844 0
HaaS100开发调试系列 之 定位AliOS Things内存及Crash问题
本文主要说开发调试过程中经常遇到的内存问题。
88 0
1445
文章
309
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载