我们在Kdump-内核崩溃转储服务中,介绍了kdump服务可以在系统崩溃时,生成崩溃转储文件vmcore,便于我们分析内核崩溃原因,那么,下面我们就来看下如何使用crash工具可以帮助我们分析系统崩溃原因。
crash工具,跟gdb很类似,它可以交互的分析运行中的内核,也可以分析由kdump、netdump、diskdump、xendump产生的core dump文件。
本文实验环境:
[root@yglocal ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
[root@yglocal ~]# uname -r
3.10.0-957.el7.x86_64
一、调试环境准备
使用crash工具分析vmcore,需要:
- crash工具
- 崩溃转储文件(vmcore)
- 发生崩溃的内核映像文件(vmlinux),包含调试内核所需调试信息
所以我们需要下载带有完整调试信息的内核映像文件(编译时带-g选项),内核调试信息包kernel-debuginfo有两个:
- kernel-debuginfo
- kernel-debuginfo-common
对于centos系统,可以在http://debuginfo.centos.org/上下载到各发行版本所需的调试包。
对于centos7.x,安装对应内核版本的内核调试包,执行如下即可:
# wget http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-common-x86_64-`uname -r`.rpm
# wget http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-`uname -r`.rpm
注意:如果系统为centos6.x,则将debuginfo.centos.org/后面的7改成6即可。
下载完后,安装内核调试包:
rpm -ivh *.rpm
安装完成后,可以在/lib/debug/lib/modules/3.10.0-957.el7.x86_64/目录下看到vmlinux内核映像文件:
[root@yglocal ~]# ll -th /lib/debug/lib/modules/3.10.0-957.el7.x86_64/
total 419M
drwxr-xr-x. 2 root root 119 Mar 26 13:13 vdso
drwxr-xr-x. 12 root root 128 Mar 26 13:13 kernel
-rwxr-xr-x. 2 root root 419M Nov 9 2018 vmlinux
再来安装crash工具,先查看下是否已安装
[root@yglocal ~]# rpm -q crash
crash-7.2.3-8.el7.x86_64
若没有安装过,则执行以下命令安装:
yum install crash
二、使用crash分析vmcore
分析vmcore文件,执行命令:
crash /lib/debug/lib/modules/3.10.0-957.el7.x86_64/vmlinux /var/crash/127.0.0.1-2020-04-04-14\:10\:45/vmcore
启动crash
其中下面这些信息,就是导致系统崩溃的直接原因及进程相关信息:
PANIC: "BUG: unable to handle kernel NULL pointer dereference at 000000000000000c"
PID: 27005
COMMAND: "pickup"
TASK: ffff997b388ae180 [THREAD_INFO: ffff997b37f64000]
CPU: 1
STATE: TASK_RUNNING (PANIC)
1、bt命令
backtrace打印内核栈回溯信息,bt pid 打印指定进程栈信息。 bt命令最重要的信息:[exception RIP: my_openat+36],指出发生异常的指令信息
RIP: ffffffffc07c5024,可以得知,发生crash的函数是my_openat中,偏移36字节处的指令。
这里,对应x86-64汇编,应用层下来的系统调用对应的6个参数存放的寄存器依次对应:rdi、rsi、rdx、rcx、r8、r9。对于多于6个参数的,仍存储在栈上。
2、log命令
打印vmcore所在的系统内核日志信息。
log命令
可以分析出当时,内核启动的各项配置,内核最后崩溃时异常日志信息。
3、dis命令
dis -l (function+offset) 10 反汇编出指令所在代码开始,10行代码
dis反汇编
幸运的话,反汇编出来会直接对应源码,我们这里只能看出执行mov 0x70(%rdi),%r13时系统崩溃。
4、mod命令
mod 查看当时内核加载的所有内核模块信息
mod命令
重装加载进改内核模块:
crash> mod -s my_test_lkm /mnt/hgfs/test_ko/lkm-test05/my_test_lkm.ko
MODULE NAME SIZE OBJECT FILE
ffffffffc07c7000 my_test_lkm 12740 /mnt/hgfs/test_ko/lkm-test05/my_test_lkm.ko
5、sym命令
sym 转换指定符号为其虚拟地址,显示系统中符号信息
如上面bt打印的RIP: ffffffffc07c5024,使用sym转换查看系统符号信息
crash> sym ffffffffc07c5024
ffffffffc07c5024 (t) my_openat+36 [my_test_lkm] /mnt/hgfs/test_ko/lkm-test05/my_lkm.c: 25
这时就可以看出对应到my_test_lkm模块的源码,/mnt/hgfs/test_ko/lkm-test05/my_lkm.c文件里第25行,查看代码可以发现:
sym命令
原因:这种hook写法在centos8.x上的最新系统调用约定,是内核版本4.17及之后采用的调用约定。而我当前环境是centos7.6,内核版本为3.10。0,其调用约定并不是这样的,所以这样取参数是有问题的。
6、ps命令
ps 打印内核崩溃时,正常的进程信息
ps查看当前进程信息
带 > 标识代表是活跃的进程,ps pid打印某指定进程的状态信息:
crash> ps 27005
PID PPID CPU TASK ST %MEM VSZ RSS COMM
> 27005 7783 1 ffff997b388ae180 RU 0.2 91732 4124 pickup
7、files命令
files pid 打印指定进程所打开的文件信息
files命令
8、vm命令
vm pid 打印某指定进程当时虚拟内存基本信息
vm命令
9、task命令
task 查看当前进程或指定进程task_struct和thread_info的信息
task命令
10、kmem命令
kmen 查看当时系统内存使用信息
kmem [-f|-F|-c|-C|-i|-v|-V|-n|-z|-o|-h] [-p | -m member[,member]]
[[-s|-S] [slab] [-I slab[,slab]]] [-g [flags]] [[-P] address]]
我们是要kmem -i查看:
kmem -i
11、其它命令
可以通过help查看到如下:
help命令
如:可以使用rd memory读取内存内容;
struct命令显示结构体定义及指定地址的结构体内容;
irq查看中断信息;
vtop查看地址页表信息等等。
三、写在最后
如果我们确定是某个内核模块导致的问题,可以反汇编出该模块的源代码:
objdump -S -D my_test_lkm.ko > lkm.S
然后vim查看lkm.S文件,查看对应的源码,我们上面调试的bt信息中[exception RIP: my_openat+36],也即是my_openat中偏移量为0x24的地方,如下图:
如果崩溃处对应有c代码的话,这样排查起来就简单多了。
可以关注我的微信公众号大胖聊编程,一起交流学习。
参考资料:
[1]: Red_Hat_Enterprise_Linux-7-Kernel_Administration_Guide-en-US 7.5. ANALYZING A CORE DUMP
网友评论