ARM32平台系统crash(系统崩溃) 问题定位的一种解决方法

说明

分享一种crash问题定位的一种解决方法,仅供参考。  

ARM32平台上通过错误使用内存,触发系统异常,系统崩溃。系统异常被挂起后,能在串口中看到异常调用栈打印信息和关键寄存器信息。

如下所示,excType表示异常类型,值为4表示数据终止异常,其它数值可以查看芯片手册。

通过这些信息可以基本定位到异常所在函数和其调用栈的关系,为分析系统死机崩溃提供分析,便于查找问题所在。

crash日志

[11:31:32.475]收←◆--- device idle,timeout=10000,lastT=77601,curT=87612
orbSendcmd: | F5 AE 02 FE 04 00 33 00 00 00 26 FD |
data_abort fault fsr:0x29, far:0x00000001
Abort caused by a read instruction. Domain fault, section
excType = 0x4
taskName = DeviceStaThd
taskID = 29
task stackSize = 24576
system mem addr = 0x40aaecc0
excBuffAddr pc = 0x4004cf34
excBuffAddr lr = 0x400522dc
excBuffAddr sp = 0x400f8340
excBuffAddr fp = 0x41300354
R0         = 0x1
R1         = 0x0
R2         = 0x7fffffff
R3         = 0xffffffff
R4         = 0x7fffffff
R5         = 0x1
R6         = 0x0
R7         = 0x0
R8         = 0x0
R9         = 0x1
R10        = 0x0
R11        = 0x41300354
R12        = 0xffffffff
CPSR       = 0x20000013
*******backtrace begin*******
traceback 0 -- lr = 0x 400537ec    fp = 0x4130065c
traceback 1 -- lr = 0x40054a74    fp = 0x4130075c
traceback 2 -- lr = 0x40055ab8    fp = 0x41300824
traceback 3 -- lr = 0x40471a8c    fp = 0x41300c9c
traceback 4 -- lr = 0x40145f54    fp = 0x41300d24
traceback 5 -- lr = 0x40146384    fp = 0x41300db4
traceback 6 -- lr = 0x40146930    fp = 0x41300dc4
traceback 7 -- lr = 0x40046c58    fp = 0x41300dd4
traceback 8 -- lr = 0x4005044c    fp = 0xb0b0b0b

利用反汇编文件.asm

打开编译后生成的 asm 反汇编文件

搜索PC指针 0x4004cf34 在asm文件中的位置(去掉0x)

PC地址指向发生异常时程序正在执行的指令。在当前执行的二进制文件对应的asm文件中,查找PC值 4004cf34,找到当前CPU正在执行的指令行,得到如下所示结果。

4004cf20 :
4004cf20:	e3100003 	tst	r0, #3
4004cf24:	e6ef1071 	uxtb	r1, r1
4004cf28:	0a000010 	beq	4004cf70 
4004cf2c:	e3520000 	cmp	r2, #0
4004cf30:	0a000035 	beq	4004d00c 

4004cf34:	e5d03000 	ldrb	r3, [r0]

4004cf38:	e1530001 	cmp	r3, r1
4004cf3c:	0a000042 	beq	4004d04c 
4004cf40:	e2803001 	add	r3, r0, #1
4004cf44:	ea000004 	b	4004cf5c 
4004cf48:	e3520000 	cmp	r2, #0
4004cf4c:	0a00002e 	beq	4004d00c 

ldrb指令作用:LDRB指令用于从内存中将一个8位的字节数据读取到指令中的目标寄存器中。并将寄存器的高24位清零。

LDRB R0, [R2,#3]    ;将内存单元(R2+3)中字节数据读取到R0中,R0中高24位设置成0

LDRB R0,[R1]    ;将内存单元(R1)中的字节数据读取到R0中,R0中高24位设置成0

结合指令分析:寄存器r0读到r3,r0为0x1,r3为0xffffffff,r3超出内存范围

cpu执行该命令发生了数据终止异常。可以确认,memchr 被调用拷贝时可能因为长度问题导致异常

根据 LR链接寄存器值 查找调用栈

从异常信息的backtrace begin开始,打印的是调用栈信息。在asm文件中查找backtrace 0 400537ec 对应的LR,如下所示。

400537e4:	e1a00009 	mov	r0, r9
400537e8:	ebfffab4 	bl	400522c0 

400537ec:	e7d93000 	ldrb	r3, [r9, r0]

400537f0:	e0896000 	add	r6, r9, r0
400537f4:	e3530000 	cmp	r3, #0
400537f8:	0affff12 	beq	40053448 
400537fc:	eafffe12 	b	4005304c 
40053800:	e3170b02 	tst	r7, #2048	; 0x800
40053804:	1a000255 	bne	40054160 
40053808:	e51f3c90 	ldr	r3, [pc, #-3216]	; 40052b80 
4005380c:	e2072001 	and	r2, r7, #1
40053810:	e51b12e0 	ldr	r1, [fp, #-736]	; 0xfffffd20
40053814:	e3520000 	cmp	r2, #0
40053818:	e2832005 	add	r2, r3, #5
4005381c:	13a01001 	movne	r1, #1
40053820:	e50b12e0 	str	r1, [fp, #-736]	; 0xfffffd20
40053824:	11a03002 	movne	r3, r2
40053828:	e50b32e4 	str	r3, [fp, #-740]	; 0xfffffd1c

可见,是 strnlen 调用了 memchr。

我们从strnlen中的实现也可以确定此论证

ARM32平台系统crash(系统崩溃) 问题定位的一种解决方法_第1张图片

使用backtrace begin和源码排查

但是此信息还不足以确定问题,我们可以继续从backtrace begin提供的内存地址开始找

从backtrace1中我们确定为printf_core接口导致

traceback 1 -- lr = 40054a74    fp = 0x4130075c
40054a70:	ebfff7a2 	bl	40052900 
40054a74:	e1a05000 	mov	r5, r0
40054a78:	e3580000 	cmp	r8, #0
40054a7c:	0a00000d 	beq	40054ab8 
40054a80:	e5943024 	ldr	r3, [r4, #36]	; 0x24

通过与代码结合,底层接口实现查找,确实如图所示

ARM32平台系统crash(系统崩溃) 问题定位的一种解决方法_第2张图片

继续根据traceback 2 -- lr = 40055ab8    fp = 0x41300824找到printf_core为vprintf调用

traceback 2 -- lr = 40055ab8    fp = 0x41300824
40055ab4:	ebfffbbb 	bl	400549a8 
40055ab8:	e1a05000 	mov	r5, r0
40055abc:	e30a337c 	movw	r3, #41852	; 0xa37c
40055ac0:	e1a00005 	mov	r0, r5
40055ac4:	e3443001 	movt	r3, #16385	; 0x4001
40055ac8:	e5932000 	ldr	r2, [r3]
40055acc:	e51b3020 	ldr	r3, [fp, #-32]	; 0xffffffe0
40055ad0:	e0332002 	eors	r2, r3, r2
40055ad4:	1a00001a 	bne	40055b44 

根据底层代码实现也可验证无问题

ARM32平台系统crash(系统崩溃) 问题定位的一种解决方法_第3张图片

继续往上查找,基本可以确定是打印接口导致

traceback 3 -- lr = 40471a8c    fp = 0x41300c9c
40471a88:	ebef8fe7 	bl	40055a2c 
40471a8c:	e1a00005 	mov	r0, r5
40471a90:	ebef81b7 	bl	40052174 
40471a94:	e2402001 	sub	r2, r0, #1
40471a98:	e7d53002 	ldrb	r3, [r5, r2]
40471a9c:	e353000a 	cmp	r3, #10
40471aa0:	0a000008 	beq	40471ac8 

继续往上查找我们是通过哪个方法的打印接口导致的crash

traceback 4 -- lr = 40145f54    fp = 0x41300d24
40145f50:	eb0caea2 	bl	404719e0 
40145f54:	e3e00000 	mvn	r0, #0
40145f58:	eaffffe3 	b	40145eec <_ZN17CDeviceStaManager25DeviceHealthStatusMonitorEPc+0x2d4>

traceback 5 -- lr = 40146384    fp = 0x41300db4
40146380:	ebfffe24 	bl	40145c18 <_ZN17CDeviceStaManager25DeviceHealthStatusMonitorEPc>
40146384:	e51b3048 	ldr	r3, [fp, #-72]	; 0xffffffb8
40146388:	e1530006 	cmp	r3, r6
4014638c:	1affffa8 	bne	40146234 <_ZN17CDeviceStaManager13DeviceStaProcEv+0x1a0>

traceback 6 -- lr = 40146930    fp = 0x41300dc4
40146904 <_ZN17CDeviceStaManager12DeviceStaThdEPv>:
40146904:	e30e1464 	movw	r1, #58468	; 0xe464
40146908:	e92d4830 	push	{r4, r5, fp, lr}
4014690c:	e1a04000 	mov	r4, r0
40146910:	e3441076 	movt	r1, #16502	; 0x4076
40146914:	e28db00c 	add	fp, sp, #12
40146918:	e3a0000f 	mov	r0, #15
4014691c:	ebfdc7d0 	bl	400b8864 
40146920:	e3540000 	cmp	r4, #0
40146924:	0a000001 	beq	40146930 <_ZN17CDeviceStaManager12DeviceStaThdEPv+0x2c>
40146928:	e1a00004 	mov	r0, r4
4014692c:	ebfffdd8 	bl	40146094 <_ZN17CDeviceStaManager13DeviceStaProcEv>
40146930:	e3a00000 	mov	r0, #0
40146934:	e8bd8830 	pop	{r4, r5, fp, pc}

再根据死机前对信息最后打印可以确定问题所在了,发送完此命令后的一条打印导致的问题

[11:31:31.083]收←◆31 08:01:26.221 [wifi] wifi status check = not connect, retry = 86

[11:31:32.082]收←◆31 08:01:27.221 [wifi] wifi status check = not connect, retry = 87

[11:31:32.475]收←◆--- device idle,timeout=10000,lastT=77601,curT=87612
orbSendcmd: | F5 AE 02 FE 04 00 33 00 00 00 26 FD |
data_abort fault fsr:0x29, far:0x00000001
Abort caused by a read instruction. Domain fault, section

所以到这里,问题可以确定为

DeviceStaThd()->DeviceStaProc()->DeviceHealthStatusMonitor()
->printf("orbSendcmd")->后一条printf导致的crash

总结

1. LR

LR用途有二,一是保存子程序返回地址,当调用BL、BX、BLX等跳转指令时会自动保存返回地址到LR;二是保存异常发生的异常返回地址

2. PC

PC(Program Counter)为程序计数器,用于保存程序的执行地址,在ARM的三级流水线架构中,程序流水线包括取址、译码和执行三个阶段,PC指向的是当前取址的程序地址,所以32位ARM中,译码地址(正在解析还未执行的程序)为PC-4,执行地址(当前正在执行的程序地址)为PC-8。
当突然发生中断的时候,保存的是PC的地址。如果返回的时候返回PC,那么中间就有一个指令没有执行,所以用SUB pc lr-irq #4。

3. SP

每一种异常模式都有其自己独立的r13,它通常指向异常模式所专用的堆栈,也就是说五种异常模式、非异常模式(用户模式和系统模式),都有各自独立的堆栈,用不同的堆栈指针来索引。这样当ARM进入异常模式的时候,程序就可以把一般通用寄存器压入堆栈,返回时再出栈,保证了各种模式下程序的状态的完整性。

你可能感兴趣的:(排查异常,嵌入式,嵌入式硬件,arm)