【问题标题】:How to read, understand, analyze, and debug a Linux kernel panic?如何阅读、理解、分析和调试 Linux 内核恐慌?
【发布时间】:2021-10-09 06:03:46
【问题描述】:

考虑以下 Linux 内核转储堆栈跟踪;例如,您可以通过调用panic("debugging a Linux kernel panic"); 从内核源代码触发恐慌:

[<001360ac>] (unwind_backtrace+0x0/0xf8) from [<00147b7c>] (warn_slowpath_common+0x50/0x60)
[<00147b7c>] (warn_slowpath_common+0x50/0x60) from [<00147c40>] (warn_slowpath_null+0x1c/0x24)
[<00147c40>] (warn_slowpath_null+0x1c/0x24) from [<0014de44>] (local_bh_enable_ip+0xa0/0xac)
[<0014de44>] (local_bh_enable_ip+0xa0/0xac) from [<0019594c>] (bdi_register+0xec/0x150)
  • unwind_backtrace+0x0/0xf8+0x0/0xf8 代表什么?
  • 如何查看unwind_backtrace+0x0/0xf8的C代码?
  • 如何解读恐慌的内容?

【问题讨论】:

标签: c linux debugging linux-kernel panic


【解决方案1】:

这只是一个普通的回溯,那些函数以相反的顺序调用(第一个被调用的被前一个调用,依此类推):

unwind_backtrace+0x0/0xf8
warn_slowpath_common+0x50/0x60
warn_slowpath_null+0x1c/0x24
ocal_bh_enable_ip+0xa0/0xac
bdi_register+0xec/0x150

bdi_register+0xec/0x150 是符号 + 偏移量/长度,在 Understanding a Kernel Oops 中有更多信息,以及如何调试内核 oops。 Debugging the Kernel上也有这个优秀的教程

注意:正如 Eugene 下面的建议,您可能想先尝试addr2line,但它仍然需要带有调试符号的图像,例如

addr2line -e vmlinux_with_debug_info 0019594c(+offset)

【讨论】:

  • @0x90 我不认为你不能得到确切的行没有调试内核,因为那是一个指令偏移量,最好的您可以使用 oops dump 来了解崩溃的函数。
  • 有时addr2line 可以解析地址并确定适当的源代码行。当然,并不总是可以将指令映射到源代码中的位置,但总比没有好。当然,为此需要内核的调试符号。如果幸运的话,可以在vmlinux 本身(用于定制内核)或单独的包中找到它们。一些发行版提供这样的包,名称可能会有所不同。 addr2line -e vmlinux_with_debug_info 0019594c 可能有助于找到与bdi_register+0xec 对应的源代码行。
  • 也许这可能有用:我最近尝试了addr2lineeu-addr2line(来自elfutils 包的类似工具),发现后者更可靠。我用这两种方法用适当的调试信息解析“e1000”驱动程序中的地址。我将带有调试信息的文件(在我的情况下为 e1000.ko.debug)复制到另一台机器上,并尝试使用addr2line -f -e e1000.ko.debug -j .devinit.text 0x424eu-addr2line 进行分析。只有后者给出了正确的结果。
  • (继续)当我在驱动程序所在的机器上进行分析时,结果与我描述的相同。 addr2line 指向了源代码中的错误位置,eu-addr2line 做对了。我现在不能说为什么会这样,也许addr2line 需要别的东西。同时,我建议安装 elfutils 并使用 eu-addr2line
【解决方案2】:

这里有两个addr2line 的替代方案。假设您拥有正确的目标工具链,您可以执行以下操作之一:

使用objdump

  1. 在内核根目录下找到vmlinux.ko文件,然后反汇编目标文件:

    objdump -dS vmlinux > /tmp/kernel.s
    
  2. 打开生成的程序集文件/tmp/kernel.s。使用文本编辑器,例如 vim。去 unwind_backtrace+0x0/0xf8,即搜索unwind_backtrace+offset的地址。最后,您在源代码中找到了有问题的部分。

使用gdb

IMO,一个更优雅的选择是使用唯一的gdb。假设您的主机上有合适的工具链:

  1. 运行gdb &lt;path-to-vmlinux&gt;
  2. 在 gdb 的提示符下执行:list *(unwind_backtrace+0x10)

有关更多信息,您可以查看以下资源:

  1. Kernel Debugging Tricks
  2. Debugging The Linux Kernel Using Gdb

【讨论】:

  • gdb 对我不起作用,尽管包含完整的调试符号并且我可以在过去进行远程调试(我必须改变一些东西......)。 objdump 不过,产生了一个非常好的清单,包括源代码,它非常精确,向我展示了引起恐慌的要点。
【解决方案3】:

unwind_backtrace+0x0/0xf8 中的+0x0/0xf8 代表什么?

第一个数字 (+0x0) 是 距函数开头的偏移量(在本例中为 unwind_backtrace)。第二个数字 (0xf8) 是函数的总长度。鉴于这两条信息,如果您已经对故障发生的位置有预感,这可能足以证实您的怀疑(您可以(大致)知道您在该功能中走了多远)。

要获得相应指令的确切源代码行(通常比预感更好),请使用addr2line 或其他答案中的其他方法。

【讨论】:

    猜你喜欢
    • 2020-01-28
    • 2022-11-10
    • 2013-12-12
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    相关资源
    最近更新 更多