问题背景:
查一个概率性出现的问题,与运行环境有关且无法coredump和attach,最后请教大神解决了。解决过程颇有演绎推理的感觉,遂记录于此。详细可见 https://blog.csdn.net/l316194152/article/details/108563637
简要描述:
一、修改系统内核,在出错处截获信号进入循环。
二、复现问题,获取指针地址A和进程号B。
三、根据B进程的smaps内存映射表,由A所在地址区间得到出错的库,记录库的起始地址C。
四、A-C=D,再对库进行反汇编,得到偏移量D所属的函数。
五、甩锅,把问题甩出去。
具体步骤:
1、修改系统内核,在出错处截获信号进入循环。
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index a9ee0d9dc740..73b52806f1f1 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -175,9 +175,15 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr,
tsk->comm, sig, addr, fsr);
show_pte(tsk->mm, addr);
show_regs(regs);
+ ret = sig_kernel_coredump(SIGSEGV);
+ printk(KERN_DEBUG "ret is %d\n", ret);
+ while(1);
}
#endif
Q:为什么加在这个函数?
A:看第二步即可知,出错时,kernel有运行到此函数。
2、复现问题,获取指针地址A和进程号B
[ 28.587915] xxx.cgi: unhandled page fault (11) at 0x00000008, code 0x017
[ 28.587934] pgd = 6510e4e1
[ 28.588024] [00000008] *pgd=28844835, *pte=00000000, *ppte=00000000
[ 28.588057] CPU: 1 PID: 897 Comm: xxx.cgi Tainted: G O 4.19.111
[ 28.588066] Hardware name: Generic DT based system
[ 28.588078] PC is at 0xa6774b8c
[ 28.588086] LR is at 0xa5e005e0
[ 28.588094] pc : [<a6774b8c>] lr : [<a5e005e0>] psr: a00e0010
[ 28.588101] sp : a67059d0 ip : a5e005d8 fp : a6705f7c
[ 28.588109] r10: a6ecc60c r9 : a679169c r8 : aedd3986
[ 28.588117] r7 : a6ecc5dc r6 : 00000000 r5 : a6ecc5d8 r4 : a67059dc
[ 28.588124] r3 : a5e00010 r2 : 00000000 r1 : 00000000 r0 : a5e005d8
[ 28.588133] Flags: NzCv IRQs on FIQs on Mode USER_32 ISA ARM Segment user
[ 28.588143] Control: 10c5387d Table: 3496006a DAC: 00000055
[ 28.588154] CPU: 1 PID: 897 Comm: sss.cgi Tainted: G O 4.19.111
[ 28.588161] Hardware name: Generic DT based system
[ 28.588191] [<b010f408>] (unwind_backtrace) from [<b010b96c>] (show_stack+0x10/0x14)
[ 28.588209] [<b010b96c>] (show_stack) from [<b08076e4>] (dump_stack+0x90/0xa4)
[ 28.588229] [<b08076e4>] (dump_stack) from [<b011376c>] (__do_user_fault+0x160/0x174)
[ 28.588246] [<b011376c>] (__do_user_fault) from [<b01139c0>] (do_page_fault+0x240/0x348)
[ 28.588260] [<b01139c0>] (do_page_fault) from [<b0113c58>] (do_DataAbort+0x4c/0xec)
[ 28.588274] [<b0113c58>] (do_DataAbort) from [<b0101d7c>] (__dabt_usr+0x3c/0x40)
[ 28.588283] Exception stack(0xcb9c9fb0 to 0xcb9c9ff8)
[ 28.588294] 9fa0: a5e005d8 00000000 00000000 a5e00010
[ 28.588305] 9fc0: a67059dc a6ecc5d8 00000000 a6ecc5dc aedd3986 a679169c a6ecc60c a6705f7c
[ 28.588316] 9fe0: a5e005d8 a67059d0 a5e005e0 a6774b8c a00e0010 ffffffff
注意其中的
[ 28.588057] CPU: 1 PID: 897 Comm: xxx.cgi Tainted: G O 4.19.111
[ 28.588078] PC is at 0xa6774b8c
0xa6774b8c即为指针地址A,进程号B为897 。
3、根据B进程的smaps内存映射表,由A所在地址区间得到出错的库,记录库的起始地址C。
cd /proc/$B
cat smaps|grep $A
注意: 有时候grep地址A没有结果,这时候可以改为grep前两位,比如地址为a6764000,则grep a67即可。
4、A-C=D,再对库进行反汇编,得到偏移量D所属的函数。
第三步可以看到类似下面样式的信息
a6764000-a6780000 r-xp 00000000 b3:06 1743 /usr/lib/libxxx.so.0.0.0
接下来就是十六进制计算了,hex(0xa6774b8c-a6764000) = 0x10b8c
把运行环境下的这个/usr/lib/libxxx.so.0.0.0库pull出来,或者push一个addr2line进去都行。
arm-linux-gnueabihf-addr2line 0x10b8c -e libxxx.so.0.0.0 -f -C -s
即可得到出错函数名
5、甩锅,把问题甩出去
如果这个函数是你写的,那没得说,自己改吧。
但如果你已经成功得到了函数名,并且不是你写的,而是调用的外部接口,那么恭喜你,可以把这个锅甩出去了。