1.概述
Linux开发过程中,难免遇到死机问题,死机问题主要分层两类死机:
Kernel Oops:内核执行非法操作(如空指针、数组越界),触发软中断,但可能未完全死机 较低 输出 Oops: 日志,含寄存器、栈回溯
Kernel Panic:致命错误(如内核死锁、中断风暴),系统无法继续运行,触发重启 较高 输出 Panic: 日志,含触发原因(如 BUG: soft lockup)
2.测试案例
在实际Linux驱动开发过程中,需要对Linux编译进行配置,开启必要的选项,比如要生成带调试符号的内核(或者ko模块),要开启CONFIG_FRAME_POINTER 如果没有开启,会导致栈回溯时变成 "??" 。并且在前期调试时,要开启early_printk 防止无法捕获到日志。 对于无法正常抓取的,可以尝试使用 kdump进行。
2.1 开启内核调试
#内核debug调试
CONFIG_DEBUG_INFO=y #生成带调试符号的内核(关键!否则无法解析代码行号)
CONFIG_FRAME_POINTER=y #保留函数帧指针,确保栈回溯准确(嵌入式平台必开)
CONFIG_PANIC_ON_OOPS=y #Oops 时触发 Panic(可选,强制死机而非继续运行,便于收集完整日志)
CONFIG_EARLY_PRINTK=y #早期内核启动阶段即可打印日志,避免崩溃时日志无法输出
CONFIG_DEBUG_SLAB=y #检测内存越界、野指针(适合内存相关崩溃)
CONFIG_DEBUG_PAGEALLOC=y #检测非法内存访问(如访问已释放页)
CONFIG_LOCK_DEBUGGING_SUPPORT=y #死锁检测(适合并发场景崩溃)
一定需要注意打开CONFIG_DEBUG_INFO ,生成的vmlinux 需要带调试符号。 对内存越界,野指针操作,需要开启SLAB调试, 对内存越界有可能因为越界小,没有被发现。
2.2 代码增加内存异常操作
在代码中添加内存异常,操作空指针的内容,
static int xiaodong_device_tree_parse_probe(struct platform_device *pdev) {
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node; //first_layer_node
struct device_node *next; //second_layer_node
int numarr[10] = {0};
int *null_ptr = NULL;
printk("drb, start oops!\r\n");
*null_ptr = 10; // 访问空指针,触发 Oops
numarr[10000] = 10;//故意性内存非法操作
//动态分配存储按键信息的内存,这个函数使用后不用手动释放,驱动卸载时会自动释放
_device_node_info = devm_kzalloc(dev, sizeof(device_tree_node_parse_struct), GFP_KERNEL);
if (_device_node_info == NULL) {
printk("[%s]{%d},no memory!\r\n",__FUNCTION__, __LINE__);
return -ENOMEM;
}
}
在Linux上实际运行过程中,出现了死机问题
[ 1.401904] input: keyinput as /devices/virtual/input/input1
[ 1.408635] drb, start oops!
[ 1.411749] 8<--- cut here ---
[ 1.414814] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 1.422934] pgd = (ptrval)
[ 1.425643] [00000000] *pgd=00000000
[ 1.429228] Internal error: Oops: 805 [#1] ARM
[ 1.433658] Modules linked in:
[ 1.436725] CPU: 0 PID: 1 Comm: swapper Not tainted 5.4.99+ #35
[ 1.442625] Hardware name: Allwinner suniv Family
[ 1.447359] PC is at xiaodong_device_tree_parse_probe+0x34/0x280
[ 1.453364] LR is at xiaodong_device_tree_parse_probe+0x2c/0x280
[ 1.459354] pc : [<c04e8764>] lr : [<c04e875c>] psr: 60000013
[ 1.465601] sp : c3839df0 ip : 00000100 fp : 000000bc
[ 1.470812] r10: c0a38580 r9 : c38d5a10 r8 : c0b03228
[ 1.476025] r7 : c0b2b8b8 r6 : c0b2b8b8 r5 : c3f79600 r4 : 00000000
[ 1.482535] r3 : 0000000a r2 : 7966751c r1 : 60000093 r0 : c38d5a10
[ 1.489048] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
[ 1.496164] Control: 0005317f Table: 80004000 DAC: 00000053
[ 1.501896] Process swapper (pid: 1, stack limit = 0x(ptrval))
[ 1.507714] Stack: (0xc3839df0 to 0xc383a000)
[ 1.512073] 9de0: c3b83240 c3b83300 c38d80c0 c0912fe0
[ 1.520242] 9e00: 00000001 7966751c 000000bc 7966751c c38d5a10 00000000 c0b2b8b8 c0b2b8b8
[ 1.528410] 9e20: 00000000 00000000 c0a38580 c044a388 c38d5a10 c0b73ef4 00000000 c0448698
[ 1.536580] 9e40: c38d5a10 c0b2b8b8 c0b2b8b8 c0b03228 00000000 c0a27838 c0a38580 c044893c
[ 1.544748] 9e60: 00000000 c0a27838 c0a38580 c38d5a10 00000000 c0b2b8b8 c0b03228 00000000
[ 1.552916] 9e80: c0a27838 c0a38580 000000bc c0448bf8 00000000 c0b2b8b8 c38d5a10 c0448c80
[ 1.561086] 9ea0: 00000000 c0b2b8b8 c0448c00 c0446a40 c0a27838 c382240c c38d3330 7966751c
[ 1.569255] 9ec0: c0b24448 c0b2b8b8 c3b4ee40 c0b24448 00000000 c0447a1c c0930234 ffffe000
[ 1.577425] 9ee0: c0b2b8b8 c0b2b8b8 c0b03228 ffffe000 c0a19d6c c04494a0 c0b3f000 c0b03228
[ 1.585594] 9f00: ffffe000 c0102cb0 c3f7275a c3f72751 c099c958 00000000 000000bc c013123c
[ 1.593761] 9f20: c099baa8 c092fbd0 00000000 00000006 00000006 00000000 c0b3f000 c3f7275a
[ 1.601928] 9f40: 00000000 7966751c 00000000 000003ac 00000007 7966751c 000003ac 00000007
[ 1.610097] 9f60: c0b3f000 c0a27830 c0b3f000 c0a00eb8 00000006 00000006 00000000 c0a00514
[ 1.618263] 9f80: c0700e34 00000000 c0700e34 00000000 00000000 00000000 00000000 00000000
[ 1.626430] 9fa0: 00000000 c0700e3c 00000000 c01010e0 00000000 00000000 00000000 00000000
[ 1.634596] 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1.642762] 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
[ 1.650967] [<c04e8764>] (xiaodong_device_tree_parse_probe) from [<c044a388>] (platform_drv_probe+0x48/0x98)
[ 1.660802] [<c044a388>] (platform_drv_probe) from [<c0448698>] (really_probe+0x218/0x348)
[ 1.669071] [<c0448698>] (really_probe) from [<c044893c>] (driver_probe_device+0x5c/0x170)
[ 1.677335] [<c044893c>] (driver_probe_device) from [<c0448bf8>] (device_driver_attach+0x58/0x60)
[ 1.686202] [<c0448bf8>] (device_driver_attach) from [<c0448c80>] (__driver_attach+0x80/0xbc)
[ 1.694722] [<c0448c80>] (__driver_attach) from [<c0446a40>] (bus_for_each_dev+0x70/0xb4)
[ 1.702895] [<c0446a40>] (bus_for_each_dev) from [<c0447a1c>] (bus_add_driver+0x150/0x1dc)
[ 1.711157] [<c0447a1c>] (bus_add_driver) from [<c04494a0>] (driver_register+0x74/0x108)
[ 1.719250] [<c04494a0>] (driver_register) from [<c0102cb0>] (do_one_initcall+0x48/0x1c4)
[ 1.727430] [<c0102cb0>] (do_one_initcall) from [<c0a00eb8>] (kernel_init_freeable+0x100/0x1c4)
[ 1.736130] [<c0a00eb8>] (kernel_init_freeable) from [<c0700e3c>] (kernel_init+0x8/0x110)
[ 1.744305] [<c0700e3c>] (kernel_init) from [<c01010e0>] (ret_from_fork+0x14/0x34)
[ 1.751851] Exception stack(0xc3839fb0 to 0xc3839ff8)
[ 1.756897] 9fa0: 00000000 00000000 00000000 00000000
[ 1.765062] 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1.773223] 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 1.779837] Code: e3a04000 eb0852d6 e3a0300a e1a00009 (e5843000)
[ 1.806187] ---[ end trace f472163495642c5c ]---
[ 1.810878] Kernel panic - not syncing: Fatal exception
[ 1.816106] Rebooting in 5 seconds..
[ 7.795339] Reboot failed -- System halted
上面的死机内容可以清楚看到:
[ 1.442625] Hardware name: Allwinner suniv Family
[ 1.447359] PC is at xiaodong_device_tree_parse_probe+0x34/0x280
[ 1.453364] LR is at xiaodong_device_tree_parse_probe+0x2c/0x280
[ 1.459354] pc : [<c04e8764>] lr : [<c04e875c>] psr: 60000013
死机是PC 指针地址 0Xc0448764 , 关键字 “PC is at” ,重点分析这个地址,需要使用的是交叉编译工具 addr2line, 和之前带有符号的vmlinux。 如果是具体的ko驱动模块,则是带有符号的ko文件。
2.3 死机内容分析
从上面的内容中,可以看到PC指针地址, 需要的工具,addr2line, 交叉编译时生成ARM版本的,这里面 arm-xiaodong-linux-uclibcgnueabi-addr2line 工具是buildroot中 配置生成的(名称被客制化成 xiaodong)。
dong@ubuntu:/dong/f1c200-pro/f1c200s-linux-5.4.99$ arm-xiaodong-linux-uclibcgnueabi-addr2line -e vmlinux c04e8764 -f -C
xiaodong_device_tree_parse_probe
/dong/f1c200-pro/f1c200s-linux-5.4.99/drivers/input/keyboard/xiaodong_device_tree_study.c:76
3.总结
OOPS 和 panic 的异常分析差不多,对于开启CONFIG_PAINIC_ON_OOPS=y , 可以将OOPS问题变成panic,进行分析异常位置发生位置。
您还没有登录,请您登录后发表评论。