searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

如何调试内核

2024-07-31 09:49:35
11
0

如何调试内核

通过打印日志调试

使用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的基础命令和用法。

  1. 在客户机中编译安装内核,注意要开启下列选项。
CONFIG_DEBUG_INFO=y
CONFIG_GDB_SCRIPTS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO_DWARF5=y

编译结果连同代码应该也有相同的一份位于宿主机上(通过共享文件夹)或者将编译产生的vmlinux以及代码拷贝到宿主机上。

  1. 修改客户机/etc/default/grub中的GRUB_CMDLINE_LINUX选项,追加nokaslr选项,然后执行grub2-mkconfig -o /boot/grub2/grub.cfg
  2. 重启客户机,确认启动了新编译安装的内核(即我们要调试的内核)。
  3. 修改客户机的qemu启动命令,增加-s -S选项,启动客户机,-S选项会使客户机启动后处在卡住的状态。
  4. 在宿主机上执行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
  1. 上面hbreak是设置硬件断点,普通的break是设置软件断点。如果编译客户机内核时没有设置选项CONFIG_STRICT_KERNEL_RWX=n,有些代码区域要用硬件断点才能正常调试,但x86最多支持4个硬件断点。

162662076811533.png

在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开始调试。

250214664145083.png

gdb调试内核模块

在前文调试内核的基础上,以kvm模块为例。

  1. 去掉前文qemu启动客户机时使用的-S选项,启动客户机。
  2. 重新编译安装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
  1. 在guest os上执行 grep -e "^kvm" /proc/modules 查看kvmkvm_intel地址。
[root@localhost cxl]# grep -e "^kvm" /proc/modules
kvm_intel 303104 0 - Live 0xffffffffc0391000 (O)
kvm 3338240 1 kvm_intel, Live 0xffffffffc0006000 (O)
  1. 在宿主机上执行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

274862139073686.png

在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启动虚机,触发调试断点。

0条评论
0 / 1000
冯****光
3文章数
0粉丝数
冯****光
3 文章 | 0 粉丝
冯****光
3文章数
0粉丝数
冯****光
3 文章 | 0 粉丝
原创

如何调试内核

2024-07-31 09:49:35
11
0

如何调试内核

通过打印日志调试

使用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的基础命令和用法。

  1. 在客户机中编译安装内核,注意要开启下列选项。
CONFIG_DEBUG_INFO=y
CONFIG_GDB_SCRIPTS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO_DWARF5=y

编译结果连同代码应该也有相同的一份位于宿主机上(通过共享文件夹)或者将编译产生的vmlinux以及代码拷贝到宿主机上。

  1. 修改客户机/etc/default/grub中的GRUB_CMDLINE_LINUX选项,追加nokaslr选项,然后执行grub2-mkconfig -o /boot/grub2/grub.cfg
  2. 重启客户机,确认启动了新编译安装的内核(即我们要调试的内核)。
  3. 修改客户机的qemu启动命令,增加-s -S选项,启动客户机,-S选项会使客户机启动后处在卡住的状态。
  4. 在宿主机上执行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
  1. 上面hbreak是设置硬件断点,普通的break是设置软件断点。如果编译客户机内核时没有设置选项CONFIG_STRICT_KERNEL_RWX=n,有些代码区域要用硬件断点才能正常调试,但x86最多支持4个硬件断点。

162662076811533.png

在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开始调试。

250214664145083.png

gdb调试内核模块

在前文调试内核的基础上,以kvm模块为例。

  1. 去掉前文qemu启动客户机时使用的-S选项,启动客户机。
  2. 重新编译安装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
  1. 在guest os上执行 grep -e "^kvm" /proc/modules 查看kvmkvm_intel地址。
[root@localhost cxl]# grep -e "^kvm" /proc/modules
kvm_intel 303104 0 - Live 0xffffffffc0391000 (O)
kvm 3338240 1 kvm_intel, Live 0xffffffffc0006000 (O)
  1. 在宿主机上执行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

274862139073686.png

在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启动虚机,触发调试断点。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0