原文地址:http://blog.csdn.net/ariesjzj/article/details/8244333
一些Linux Kernel的分析调试工作,主要包换qemu,kprobes和trace等,以作备忘。
Qemu源码级调试Kernel
1. Qemu编译与安装
先安装libsdl的开发库
$ ./configure
$ make
# make install
Qemu-1.2.1试过能work,早先的版本编译启动时可能有问题。怕麻烦的可以直接apt-get install qemu。。。但这样装起来的qemu有可能断点不work。
2. Linux kernel编译
如果要在64位机上编译32位内核,要加上ARCH=xxx选项。如
# make ARCH=i386 defconfig
# make ARCH=i386 menuconfig
然后选上:
kernel hacking –> compile the kernel with debug info
kernel hacking –> compile the kernel with frame pointers
另外选上
File system -> The extended 4 (ext4) filesystem
否则对有些img可能会在启动时挂载root失败.
# make ARCH=i386 bzImage
结束后生成Arch/x86/boot/bzImage和vmlinux,再在qemu的官网上下linux-0.2.img.bz2并解压得到linux-0.2.img。运行
$ qemu -kernel bzImage -hda linux-0.2.img -append root=/dev/sda rw -s -S
由于加了-S起来后停住在1234端口上等待debugger连接,于是起gdb client:
$ gdb vmlinux
(gdb) target remote localhost:1234
然后就可以像调app一样调kernel了。如图:
注意Root文件系统镜像除了用qemu提供的test image外,还可以自己用busybox做。嫌麻烦的可以用Buildroot,它会自动下载和编译Linux内核和rootfs等等。
用Qemu调有个好处是还可以用Qemu的Monitor查看系统信息(http://doc.opensuse.org/products/draft/SLES/SLES-kvm_sd_draft/cha.qemu.monitor.html) 。如查看control register,tlb或者memory mapping,在调试时都是非常有用的。
Kprobes, jprobes,dprobes
Xprobes族工具以module的方式在指定地址或函数位置加hook,然后用户可以调用自定义的handler。kprobes用来截地址(当然更多时候是根据symbol定位地址),jprobes用来截函数,用它查看函数参数比较方便,因为它的handler原型和要截的函数是一样的。Dprobes可以动态执行。
大多数时候我们会通过symbol来加hook,kernel的symbol可以通过以下三种方式找到
1. kernel编译生成的System.map文件
2. $ nm vmlinuz
3. /proc/kallsyms
查看当前的kprobes:
# cat/sys/kernel/debug/kprobes/list
关闭
# echo "0"> /sys/kernel/debug/kprobes/enabled
开启
# echo "1"> /sys/kernel/debug/kprobes/enabled
Linux kernel提供了相关文档Documentation/kprobes.txt,并且提供了几个例子,在samples/kprobes下。用以下Makefile单独编译例子:
obj-m +=jprobe_example.o kprobe_example.oKDIR=/lib/modules/$(shell uname -r)/build
all:
$(MAKE)-C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm-rf *.o *.ko *.mod.* .c* .t*
编译完后:
# insmod./kprobes_example.ko
然后用 dmesg命令就可以看到handler的输出,如:
[ 1251.789211]Planted kprobe at c1058e30
[ 1256.716389]pre_handler: p->addr = 0xc1058e30, ip = c1058e31, flags = 0x246
[ 1256.716394]post_handler: p->addr = 0xc1058e30, flags = 0x246
[ 1262.680009]pre_handler: p->addr = 0xc1058e30, ip = c1058e31, flags = 0x246
[ 1262.680024]post_handler: p->addr = 0xc1058e30, flags = 0x246
...
[ 1468.928704]Planted jprobe at c1058e30, handler addr e08ff000
[ 1471.688212]jprobe: clone_flags = 0x1200011, stack_size = 0x0, regs = 0xcef87fb4
[ 1474.153652]jprobe: clone_flags = 0x1200011, stack_size = 0x0, regs = 0xcef87fb4
产生kernel Core dump
Kernel core dump需要内核支持,Linux kernel中的Documentation/kdump.txt对此讲得比较详细。
Ubuntu上可以装linux-crashdump, 其中包含crash,kexec-tools和makedumpfile等工具。装完后重启机子,启动时按shift进grub,按e看到启动选项多了crashkernel=XXX啥的。进入系统后
# service kdump start
开启kdump进程。系统崩溃时就会在/var/crash/下产生dump文件。简便使系统crash的方法是用magic sysrq:
SysrqAlt+SysRq+s // sync
SysrqAlt+SysRq+c // force crash
分析core dump和实时查看系统
crash可用于查看core dump或者实时查看运行状态。假设内核core dump在/var/crash/linux-image-2.6.32-38-generic.0.crash。先从crash文件中解压出vmcore文件:
# apport-unpack /var/crash/linux-image-2.6.32-38-generic.0.crash ./
再用crash查看:
# crash vmlinux ./VmCore
这里的vmlinux是kernel source编译出来的binary。
crash还可以用来实时查看当前运行的系统,直接运行:
# crash vmlinux
其实相当于
# crash vmlinux /proc/kcore,/proc/kcore是系统虚拟出来的core文件。
gdb, ddd也可用于相同功能,比如要实时查看系统:
# ddd vmlinux /proc/kcore
就可以对系统进行有限的gdb调试。
Kernel Trace
Kernel的trace功能依赖于debugfs,如果还没挂载的话要先挂上。trace的主要文件在/sys/kernel/debug/tracing下。其用法在kernel中的documentation/trace下写得比较详细,同时kernel还提供了相关例子sample/trace_events.c。
在/sys/kernel/debug/tracing下几个重要的文件:
trace: tracer的输出
available_tracers: 可用tracer
current_tracer:当前enabled的tracer
tracing_enabled:开关
例:
# echo function > current_tracer
# echo 1> tracing_enabled
# cat trace > trace.txt
# echo 0> tracing_enabled
# cat ./trace.txt
得到如:
...
Xorg-896 [000] 212.518803: do_softirq <-irq_exit
Xorg-896 [000] 212.518806: __do_softirq <-do_softirq
Xorg-896 [000] 212.518813: run_timer_softirq <-__do_softirq
Xorg-896 [000] 212.518816: hrtimer_run_pending <-run_timer_softirq
Xorg-896 [000] 212.518818: _raw_spin_lock_irq <-run_timer_softirq
Xorg-896 [000] 212.518821: rcu_bh_qs <-__do_softirq
Xorg-896 [000] 212.518824: __local_bh_enable <-__do_softirq
Xorg-896 [000] 212.518826: rcu_irq_exit <-irq_exit
Xorg-896 [000] 212.518828: rcu_enter_nohz <-rcu_irq_exit
Xorg-896 [000] 212.518831: idle_cpu <-irq_exit
...
注:上面的工具依赖于一些kernel的feature,如debugfs和sysrq等,要使用则要在内核编译时加上它们。
debugfs使内核的信息以基于内存的文件系统呈现,从而使用户很方便地与之交互。详见kernel的Documentation/debugfs.txt。挂载通过
# mount-t debugfs nodev /sys/kernel/debug
Sysrq提供了一些用于开发调试kernel的热键,详见kernel的Documentation/sysrq.txt, 该feature的开启通过
# echo 1 >/proc/sys/kernel/sysrq
或将/etc/sysctl.conf设置kernel.sysrq = 1。
上面介绍的只是kernel调试工具中的沧海一粟,还有好多神器无法列举,如kgdb, kdb远程调试kernel,kmemcheck,faultinjection等等。