美文网首页
内核panic后,如何快速定位?

内核panic后,如何快速定位?

作者: 迷死她张 | 来源:发表于2019-06-06 17:16 被阅读0次

    内核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)

    通过分析,要快速定位出错的代码行,其实就是快速查找到出错的地址对应的代码?

    首先,在代码编译连接时,每个函数都有起始地址和长度,这个地址是程序运行时的地址,而函数内部,每条指令相对于函数开始地址会有偏移。那么有了地址以后,就可以定位到该地址落在哪个函数的区间内,然后找到该函数,进而通过计算偏移,定位到代码行。

    但是,如果拿到的日志文件所在的系统版本跟当前的代码版本不一致,那么编译后的地址就会有差异。那么简单地直接通过地址就可能找不到原来的位置,这个就可能需要回调里头的函数名信息。先通过函数名定位到所在函数,然后通过偏移定位到代码行。

    相应的工具有addr2line, gdb, objdump等。但是要注意的是,代码的实际运行是不需要符号的,只需要地址就行。所以如果要调试代码,必须确保调试符号已经编译到内核中,不然,回调里头打印的是一堆地址,根本看不到符号,那么对于上面提到的情况二而言,将无法准确定位问题。所以,如果要获取到足够多的调试信息,请根据需要打开如下选项:

    CONFIG_DEBUG_KERNEL=y

    CONFIG_DEBUG_INFO=y

    CONFIG_KALLSYMS=y

    CONFIG_KALLSYMS_ALL=y

    CONFIG_DEBUG_BUGVERBOSE=y

    CONFIG_STACKTRACE=y

    1、addr2line

    如果出错的内核跟当前需要调试的内核一致,而且编译器等都一致,那么可以通过addr2line直接获取到出错的代码行,假设出错地址为0019594c:

    $ addr2line -e vmlinux 0x0019594c

    mm/backing-dev.c:335

    如果是情况二,可以先通过nm获取到当前的vmlinux中bdi_register函数的真实位置。

    $ nm vmlinux | grep bdi_register

    0x00195860 T bdi_register

    然后,加上0xec的偏移,即可算出真实地址:

    $ echo "obase=16;ibase=10;$((0x00195860+0xec))" | bc -l

    19594C

    2、gdb

    这个使用比较普遍,这里就不阐述了。

    3、objdump

    如果是情况一,直接用地址dump出来。咱们回头看一下Backtrace信息:bdi_register+0xec/0x150,这里的0xec是偏移,而0x150是该函数的大小。用objdump默认可以获取整个vmlinux的代码,但是咱们其实只获取一部分,这个可以通过--start-address和--stop-address来指定。另外-d可以反汇编代码,-S则可以并入源代码。

    $ objdump -dS vmlinux_with_debug_info --start-address=0x0019594c --stop-address=$((0x0019594c+0x150))

    如果是情况二,也可以跟addr2line一样先算出真实地址,然后再通过上面的方法导出。

    相关文章

      网友评论

          本文标题:内核panic后,如何快速定位?

          本文链接:https://www.haomeiwen.com/subject/ukmuxctx.html