用gdb调试内核模块

请读者试用 本人开发搜索页面
速谷歌   http://www.sugoogle.com

  本文也即《Linux Device Drivers》,LDD3的第四章Debuging Techniques的读书笔记之六,但我们不限于此内容。这章看得比较慢,最近比较懒,而陷入了文档工作中,我决定这章节不会有之七,在之六打住。

   在用户程序中,有很多编译工具同提供的debug工具,用于设置断点或者单步跟踪,但是在kernel程序中是很困难的。LDD3介绍了gdb对于kernel模块的使用方式,但是需要kernel版本在2.6.7以上。我们需要两个文件vmlinux和/proc/kcore。我注意到moblin是没有看kcore的,而moblin的快速启动和效率是对linux的一个很大的优化,所以不知道以后是否还能通过这个方式。我在fc10进行实验,具体如下:

一、先获得vmlinux。

   这个不是/boot中任何相关的不bzImage或者zImage等经过压缩的文件,而且我们需要打开CONFIG_DEGBU_INFO的选项,因此我打算重新编译kernel来获取。根据我linux的当前版本,在网上下载kernel的source code文件kernel-2.6.27.5-117.fc10.src.rpm。下面的操作都在非root的普通用户下执行。

   1、rpm –ivh kernel-2.6.27.5-117.fc10.src.rpm,在~/rpmbuild/中或有SPECS/kernel.specs;

   2、进入~/rpmbuild/SPECS目录,执行rpmbuild –bp kernel.specs,在~/rpmbuild/BUILD/下有源代码

   3、进入~/rpmbuild/BUILD/kernel-2.6.27/linux-2.6.27.i386目录,需要根据我们的环境生成我们的.config文件,执行perl merge.pl config-i686 config-x86-generic > .config

   4、打开CONFIG_DEBUG_INFO的选项,执行make menuconfig,进入kernel hacking的选项,打开Kernel debugging,选择Compile the kernel with debug info,保存,查看vi .config,确认CONFIG_DEBUG_INFO的选项已经设置为y。

   5、如果我们直接编译,有一个bug,是Toshiba的api,需要打补丁,既然我的机器没有日立的API,可以在.config中将CONFIG_ACPI_TOSHIBA的选项直接关闭解决。我们只需要获得vmlinux,执行 make ARCH=x86 bzImage即可。这个过程花点时间,完成后在当前目录有一个文件vmlinux,这就是我们所需的。

二、进入kernel的gdb模式:

   执行gdb vmlinux /proc/kcore,在moblin中,不直接使用gdb vmlinux即可。我们加载scull模块后,在/sys/module/scull/sections中,使用ls -a的命令,有很多系统文件,用于显示scull模块的内存物理位置,其中需要注意的有.text。显示模块执行executable code的位置,.bss和.data显示模块变量的位置,如果编译时没有初始化位于.bss,如果编译时已经初始化则位于.data。  需查清各自物理内存的位置。cat .data .bss .text

   下面是处理的过程:

sudo gdb vmlinux /proc/kcore 
GNU gdb Fedora (6.8-29.fc10) 
Copyright (C) 2008 Free Software Foundation, Inc. 
License GPLv3+: GNU GPL version 3 or later 

This is free software: you are free to change and redistribute it. 
There is NO WARRANTY, to the extent permitted by law.  Type "show copying" 
and "show warranty" for details. 
This GDB was configured as "i386-redhat-linux-gnu"... 
Core was generated by `ro root=UUID=e57e257e-7340-411c-a6bb-6eb375f5aa8b rhgb quiet'. 
[New process 0] 
#0  0x00000000 in ?? () 

(gdb) add-symbol-file /home/wei/workspace/learning/kernel_module/scull_debug/scull.ko 0xf9df8000 -s .bss 0xf9dfaa00 -s .data 0xf9df9720 
add symbol table from file "/home/wei/workspace/learning/kernel_module/scull_debug/scull.ko" at 
    .text_addr = 0xf9df8000 
    .bss_addr = 0xf9dfaa00 
    .data_addr = 0xf9df9720 

(y or n) y 
Reading symbols from /workspace/wei/learning/kernel_module/scull_debug/scull.ko...done. 
(gdb) p mydev[0] 
$1 = {data = 0x0, quantum = 0, qset = 0, size = 0, access_key = 0, sem = { 
    lock = {raw_lock = {slock = 0}}, count = 1, wait_list = { 
      next = 0xf9dfaa3c, prev = 0xf9dfaa3c}}, cdev = {kobj = {name = 0x0, 
      entry = {next = 0xf9dfaa48, prev = 0xf9dfaa48}, parent = 0x0, 
      kset = 0x0, ktype = 0xc07d97c0, sd = 0x0, kref = {refcount = { 
          counter = 1}}, state_initialized = 1, state_in_sysfs = 0, 
      state_add_uevent_sent = 0, state_remove_uevent_sent = 0}, 
    owner = 0xf9df9880, ops = 0xf9df9740, list = {next = 0xf9dfaa70, 
      prev = 0xf9dfaa70}, dev = 260046848, count = 1}} 

(gdb) p mydev[0] //这里通过用户程序写入信息,在执行一次,发现结果相同,gdb给出的是上次结果的缓存 
$2 = {data = 0x0, quantum = 0, qset = 0, size = 0, access_key = 0, sem = { 
    lock = {raw_lock = {slock = 0}}, count = 1, wait_list = { 
      next = 0xf9dfaa3c, prev = 0xf9dfaa3c}}, cdev = {kobj = {name = 0x0, 
      entry = {next = 0xf9dfaa48, prev = 0xf9dfaa48}, parent = 0x0, 
      kset = 0x0, ktype = 0xc07d97c0, sd = 0x0, kref = {refcount = { 
          counter = 1}}, state_initialized = 1, state_in_sysfs = 0, 
      state_add_uevent_sent = 0, state_remove_uevent_sent = 0}, 
    owner = 0xf9df9880, ops = 0xf9df9740, list = {next = 0xf9dfaa70, 
      prev = 0xf9dfaa70}, dev = 260046848, count = 1}} 

(gdb) core-file /proc/kcore   //更新,flush cache,下次查询,将是实时内容 
Core was generated by `ro root=UUID=e57e257e-7340-411c-a6bb-6eb375f5aa8b rhgb quiet'. 
[New process 0] 
#0  0x00000000 in ?? () 
(gdb) p mydev[0] 
$3 = {data = 0xee327020, quantum = 1024, qset = 64, size = 1500, 
  access_key = 0, sem = {lock = {raw_lock = {slock = 2570}}, count = 1, 
    wait_list = {next = 0xf9dfaa3c, prev = 0xf9dfaa3c}}, cdev = {kobj = { 
      name = 0x0, entry = {next = 0xf9dfaa48, prev = 0xf9dfaa48}, 
      parent = 0x0, kset = 0x0, ktype = 0xc07d97c0, sd = 0x0, kref = { 
        refcount = {counter = 1}}, state_initialized = 1, state_in_sysfs = 0, 
      state_add_uevent_sent = 0, state_remove_uevent_sent = 0}, 
    owner = 0xf9df9880, ops = 0xf9df9740, list = {next = 0xf264df28, 
      prev = 0xf264df28}, dev = 260046848, count = 1}} 
(gdb)

   我们也可以通过print * (address)来查看某一个位置上的数据,例如上例子中,我们知道data的入口地址,我们就可以进一步查询相关内容,但是说实在的,我觉得这个还不很好用,俺们喜欢printk。

其他调测工具

   Kernel中没有内嵌的debugger,可以在oss.sgi.com中下载path来支持kdb。需要将补丁打上,并编译为kernel,运行这个kernel,我似乎没在oss.sgi.com中找到,而且整个处理方式比较麻烦,根据书上见过可以通过Pause/Break按键进入kdb的debug模式,可以设置断点,具体可以在Documentation/kdb中查阅。

   据说在2.6.25的版本已经将kgdb(另一个debugger)加入mainline中。我查看.config,确实有该选项,但是没有找到相关Documentation。这个是个强悍的工具,某种意义上是kernel自带的debugger,我觉得似乎适合于kernel的调测,而对于kernel module了将可能有些大材小用了。另外还有LTT(Linux Trace Toolkit)的patch,跟踪信息中包含时间,并可提供一段时期内处理事件,不仅可以用来debug,而且可以跟踪性能问题。IBM为x86-32提供动态探针(Dynamic Probes或者DProbes)提供强大的功能,可以加在kernel和用户程序中,不需要rebuild kernel或者重启,是个很好的工具,可以在http://oss.software.ibm.com 下载。

   可以使用虚拟机来调测kernel,例如在moblin中,有MIC2工具,或者可以使用KVM。书中介绍了User-Mode Linux的概念,但是这种方式,适合调测kernel,而不太适合做驱动,因为无法获取硬件设备,因此这本书只是提了一下,并没有做进一步的介绍。在一些实验例子中,如果我觉得有可能危及我的系统,一般转到KVM上先看看。

   总结一下第四章:对于我们的开发,如果不是很大规范,只设计到kernel module不涉及到kernel,不需要引入其他的工具,printk就是很好的工具,在这种中我们可以通过makefile来设定调测的等级。还学习了通过/proc文件来和kernel进行交互。

<think>好的,用户想知道如何使用GDB调试内核kdump。首先,我需要回忆一下kdump的基本概念和工作流程。根据引用[4],Kdump是在系统崩溃时启动第二个内核(捕获内核)来收集生产内核的内存转储,生成/proc/vmcore文件。而用户的问题是如何用GDB调试这个过程。 首先,我应该考虑用户可能需要的前提条件,比如是否已经配置好kdump,生成了vmcore文件。然后,调试步骤可能包括加载vmcore到GDB,使用crash工具辅助分析,以及可能的ftrace结合。引用[3]提到crash可以查看ftrace,这可能是一个相关点。 接下来,需要分解步骤。首先是如何安装必要的工具,比如crash和kernel-debuginfo。然后,用GDB加载vmcore,但可能需要注意GDB本身对内核调试的支持有限,所以需要结合crash工具。引用[3]中的crash安装和调试方法可能适用。 另外,用户可能遇到常见问题,比如符号文件缺失,这时候需要安装debuginfo包,如引用[3]提到的。还有可能涉及分析内核崩溃的原因,比如模块问题,需要加载模块的符号。 最后,需要验证步骤是否正确,比如运行GDB命令后能否查看寄存器、堆栈等。同时,提醒用户可能需要使用crash工具的高级功能,如`bt`查看堆栈,`log`查看内核日志,如引用[4]中提到的分析流程。 总结回答结构:先介绍前提条件,然后分步骤说明如何用GDB和crash分析vmcore,接着常见问题处理,最后注意事项。确保引用相关文献,比如引用[3]和[4]中的crash和kdump流程。</think>### 如何使用GDB调试内核kdump? #### 前提条件 1. **已配置kdump并生成vmcore文件** 确保系统已启用kdump机制,并在崩溃后生成内存转储文件`/proc/vmcore`或转储到指定路径(通过`/etc/kdump.conf`配置)[^2][^4]。 2. **安装调试工具和符号文件** - 安装`crash`工具和`gdb`:用于分析内核转储文件。 - 安装对应内核版本的调试符号包(如`kernel-debuginfo`),否则无法解析内核符号[^3]。 --- #### 调试步骤 ##### 1. **使用GDB直接加载vmcore** ```bash gdb /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /path/to/vmcore ``` - **说明**: - `vmlinux`是包含调试符号的内核镜像,需通过`kernel-debuginfo`包获取。 - GDB会加载崩溃时的寄存器状态和内存快照,可通过`bt`查看崩溃时的调用栈[^3]。 ##### 2. **使用crash工具增强分析** ```bash crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /path/to/vmcore ``` - **常用命令**: - `bt`:显示崩溃时的内核堆栈。 - `log`:查看内核日志,定位崩溃原因。 - `ps`:显示崩溃时的进程状态。 - `struct <结构体名> <地址>`:查看内核数据结构内容。 ##### 3. **结合ftrace分析性能问题** - 若崩溃与内核性能或死锁相关,可通过`crash`扩展命令查看ftrace日志: ```bash crash> trace ``` - 需确保生产内核已启用ftrace功能。 --- #### 常见问题处理 1. **符号文件缺失** 若GDB提示`No symbol table is loaded`,需安装对应内核版本的`kernel-debuginfo`包。 2. **模块未加载符号** 若崩溃由内核模块引起,需手动加载模块符号: ```bash crash> mod -s <模块名> /path/to/module.ko ``` --- #### 注意事项 - **vmcore完整性**:确保转储文件未被截断,可通过`file /path/to/vmcore`验证是否为`ELF core`文件。 - **多核分析**:使用`crash`的`set`命令切换CPU上下文,分析多核环境下的崩溃原因。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值