arm Linux kernel oops panic 调试技巧

本文介绍了一种调试Linux内核Oops错误的方法,特别是针对USB驱动在拔除设备时引发的问题。通过重新编译内核获取调试信息,并利用gdb定位到具体的代码行。

原文地址:Linux kernel oops panic 调试技巧 作者:axis

最近在调试USB驱动的过程中,偶尔会出现拔出USB线缆时kernel会有oops错误,以下是对kernel oops错误调试的简单记录,该方法也适用于panic错误。

oops错误日志信息:

  1. Unable to handle kernel NULL pointer dereference at virtual address 00000020
  2. pgd = 80004000
  3. [00000020] *pgd=00000000
  4. Internal error: Oops: 17 [#1] PREEMPT
  5. last sysfs file: /sys/devices/platform/mxsdhci.2/mmc_host/mmc0/mmc0:0001/boot_bus_config
  6. CPU: 0 Not tainted (2.6.35.3 #10)
  7. PC is at fsg_main_thread+0x144/0x730
  8. LR is at schedule+0x2ac/0x328
  9. pc : [<8025b0b4>] lr : [<802ac778>] psr: 60000013
  10. sp : cfcd3f88 ip : cfcd3f38 fp : cfcd3fc4
  11. r10: cfcd2000 r9 : cf081640 r8 : 00000200
  12. r7 : 80356ac8 r6 : cfcd2000 r5 : cf081678 r4 : cf081600
  13. r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : 00000000
  14. Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
  15. Control: 10c5387d Table: bd2a0019 DAC: 00000017
  16. Process file-storage-ga (pid: 871, stack limit = 0xcfcd22e8)
  17. Stack: (0xcfcd3f88 to 0xcfcd4000)
  18. 3f80: cf08167c cfcd3f98 802ac648 cf0816b0 00000000 cf029ed8
  19. 3fa0: cfcd3fcc 8025af70 cf081600 00000000 00000000 00000000 cfcd3ff4 cfcd3fc8
  20. 3fc0: 8006565c 8025af7c 00000000 00000000 cfcd3fd0 cfcd3fd0 cf029ed8 800655d8
  21. 3fe0: 80051d14 00000013 00000000 cfcd3ff8 80051d14 800655e4 eda8ff35 f7efad76
  22. Backtrace:
  23. [<8025af70>] (fsg_main_thread+0x0/0x730) from [<8006565c>] (kthread+0x84/0x8c)
  24. [<800655d8>] (kthread+0x0/0x8c) from [<80051d14>] (do_exit+0x0/0x65c)
  25. r7:00000013 r6:80051d14 r5:800655d8 r4:cf029ed8
  26. Code: e5953004 e3530001 1afffff8 e5953018 (e5932020)
  27. ---[ end trace 38aa9563884a33ec ]---

遇到了空指针错误,PC指针指向fsg_main_thread+0x144 处,fsg_main_thread()函数位于driver/usb/gadgate/file_storage.c这个文件内,但是0x144的offset是哪一行呢?由于发生这个oops的kernel缺省没有包含debug信息,所以需要重新生成一个带debug info的vmlinux,步骤如下:
运行make menuconfig之后选中,

  1. kernel hacking->Kernel debugging->Compile the kernel with debug info
这样编译出来的vmlinux就带调试符号了。

打开编译好的kernel vmlinux所在目录的符号表文件System.map,搜索fsg_main_thread,找到所在的行,最左边的就是fsg_main_thread的地址了,即8025af70,偏移0x144,最终出错的地址是:

  1. 0x8025af70+0x144=0x8025b0b4

此时用编译kernel的toolchain中的gdb工具打开带调式符号的vmlinux,

  1. toolchain/arm-eabi-4.4.3/bin/arm-eabi-gdb kernel/vmlinux
在gdb中使用b命令设置断点

  1. (gdb) b * 0x8025b0b4
最终得到了出错的代码行号

  1. Breakpoint 1 at  0x8025b0b4 : file drivers/usb/gadget/file_storage.c, line 2750.
拿到了行号就可以继续深入debug了,在该行前后加一些BUG_ON()宏对变量进行测试,最终找到出错的语句。
Linux 内核中,`panic()` 函数是一个用于**致命错误处理**的重要函数。当内核遇到无法恢复的错误(如硬件故障、严重内存错误、断言失败等)时,就会调用 `panic()` 函数,通知系统发生了严重错误,并尝试**停止系统运行**或**重启系统**。 --- ### 函数原型(定义在 `kernel/panic.c`): ```c void panic(const char *fmt, ...) __noreturn; ``` - `__noreturn` 表示该函数不会返回,调用后程序流程不会继续执行。 --- ### 主要功能: 1. **打印错误信息** - 使用 `printk()` 打印传入的错误信息和内核日志。 2. **触发 oops 或崩溃处理流程** - 如果配置了 `CONFIG_DEBUG_BUGVERBOSE`,会打印出错的文件名和行号。 - 如果启用 Kdump,会触发 kexec,加载 crash kernel。 3. **暂停或重启系统** - 默认情况下,内核会进入死循环,停止所有任务。 - 如果设置了 `panic_timeout`(通过内核参数 `panic=N`),则会在 N 秒后自动重启。 --- ### 示例代码: ```c if (some_critical_error) { panic("Critical error: Memory corruption detected!"); } ``` --- ### 调用栈示例(日志输出): ``` Kernel panic - not syncing: Critical error: Memory corruption detected! CPU: 0 PID: 1 Comm: swapper Not tainted 5.10.0 #1 Hardware name: ARM-Virtual [<ffffff8008012340>] dump_backtrace+0x0/0x190 [<ffffff80080124e0>] show_stack+0x14/0x20 [<ffffff80080a1234>] panic+0x124/0x320 [<ffffff80080b5678>] some_critical_function+0x50/0x80 ``` --- ### 相关配置选项: | 配置项 | 说明 | |--------|------| | `CONFIG_PANIC` | 是否启用 panic 功能 | | `CONFIG_PANIC_TIMEOUT` | 设置 panic 后自动重启的秒数 | | `CONFIG_KEXEC` | 支持 kexec,panic 后加载 crash kernel | | `CONFIG_CRASH_DUMP` | 支持生成 crash dump 文件用于调试 | --- ### 设置 panic 后自动重启: 在内核启动参数中添加: ```bash panic=10 ``` 表示内核 panic 后 10 秒自动重启。 --- ### panic 与 BUG、WARN 的区别: | 函数 | 行为 | 是否可恢复 | 用途 | |------|------|------------|------| | `panic()` | 打印信息并停止系统 | 否 | 致命错误 | | `BUG()` | 强制触发内核 oops | 否 | 断言失败 | | `WARN()` | 打印警告信息 | 是 | 非致命错误 | | `WARN_ON()` | 条件为真时警告 | 是 | 调试用途 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值