Linux死机分析方法-(上)-OOPS

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,进行分析异常位置发生位置。