如何调试内核
通过打印日志调试
使用pr_info/pr_warn/pr_err/printk
等函数打印信息。
使用BUG()
和BUG_ON(x)
引发bug并打印栈回溯信息、cpu寄存器值等信息。
使用WARN()
和WARN_ON(x)
打印栈回溯等信息。
使用dump_stack()
打印栈回溯等信息。
使用dmesg
可以查看内核打印的消息。使用dmesg -w
命令持续查看。使用dmesg -c
命令清空日志。
由于内核消息缓冲区是一个环形队列,队列满后会删除数据,可以使用dmesg -w > kernel_message.log
重定向到磁盘。
printk
(以及pr_info
等)的效率非常低,大量使用会使得系统变的非常慢。
gdb调试内核
可以查看网上的博客或者课件[gdb-tutorial-handout.pdf]熟悉gdb的基础命令和用法。
- 在客户机中编译安装内核,注意要开启下列选项。
CONFIG_DEBUG_INFO=y
CONFIG_GDB_SCRIPTS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO_DWARF5=y
编译结果连同代码应该也有相同的一份位于宿主机上(通过共享文件夹)或者将编译产生的vmlinux以及代码拷贝到宿主机上。
- 修改客户机
/etc/default/grub
中的GRUB_CMDLINE_LINUX
选项,追加nokaslr
选项,然后执行grub2-mkconfig -o /boot/grub2/grub.cfg
。 - 重启客户机,确认启动了新编译安装的内核(即我们要调试的内核)。
- 修改客户机的qemu启动命令,增加
-s -S
选项,启动客户机,-S
选项会使客户机启动后处在卡住的状态。 - 在宿主机上执行
gdb $kernel_path/vmlinux
。然后可以使用gdb的调试命令进行调试。
建立GDB远程调试连接
target remote :1234
为函数start_kernel创建一个硬件断点
hbreak start_kernel
继续运行直到遇到断点
c
打印调用栈
bt
为指定代码位置创建断点
hbreak init/main.c:896
继续运行直到遇到断点
c
打印变量
p command_line
- 上面hbreak是设置硬件断点,普通的break是设置软件断点。如果编译客户机内核时没有设置选项
CONFIG_STRICT_KERNEL_RWX=n
,有些代码区域要用硬件断点才能正常调试,但x86最多支持4个硬件断点。
在vscode中调试
如上步骤使用qemu启动客户机(第4步)后。
编辑vscode中的.vscode/launch.json
如下:
{
"version": "0.2.0",
"configurations": [
{
"name": "kernel-debug",
"type": "cppdbg",
"request": "launch",
"miDebuggerServerAddress": "127.0.0.1:1234",
"program": "${workspaceFolder}/vmlinux",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"MIMode": "gdb",
//如果使用软件断点,去掉下面的选项:
"hardwareBreakpoints": {
"require": true,
"limit": 4
}
}
]
}
按F5
开始调试。
gdb调试内核模块
在前文调试内核的基础上,以kvm模块为例。
- 去掉前文qemu启动客户机时使用的
-S
选项,启动客户机。 - 重新编译安装kvm,确保加载的是最新的kvm模块。
rmmod kvm-intel kvm
cd $kernel_path
make M=arch/x86/kvm -j3
make M=arch/x86/kvm modules_install
sleep 1
modprobe kvm-intel
- 在guest os上执行
grep -e "^kvm" /proc/modules
查看kvm
和kvm_intel
地址。
[root@localhost cxl]# grep -e "^kvm" /proc/modules
kvm_intel 303104 0 - Live 0xffffffffc0391000 (O)
kvm 3338240 1 kvm_intel, Live 0xffffffffc0006000 (O)
- 在宿主机上执行
gdb $kernel_path/vmlinux
。然后可以使用gdb的调试命令进行调试。
target remote :1234
add-symbol-file $kernel_path/arch/x86/kvm/kvm.ko 0xffffffffc0006000
add-symbol-file $kernel_path/arch/x86/kvm/kvm-intel.ko 0xffffffffc0391000
break __kvm_read_guest_page
c
在guest上使用qemu启动一个虚机,触发断点。
bt
在vscode中调试
在.vscode/launch.json
中增加下面的配置。
{
"version": "0.2.0",
"configurations": [
{
"name": "kvm-debug",
"type": "cppdbg",
"request": "launch",
"miDebuggerServerAddress": "127.0.0.1:1234",
"program": "${workspaceFolder}/vmlinux",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"MIMode": "gdb",
}
]
}
打好断点后,按F5开始调试,调试开始时,会首先卡住。然后在vscode的DEBUG CONSOLE
终端输入
-exec add-symbol-file ./arch/x86/kvm/kvm.ko 0xffffffffc0006000
-exec add-symbol-file ./arch/x86/kvm/kvm-intel.ko 0xffffffffc0391000
点击继续,然后使用qemu启动虚机,触发调试断点。