kernel module编程(十一):使用gdb以及其他调测工具

  本文也即《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进行交互。

你可能感兴趣的:(编程,linux,redhat,读书,Access)