驱动篇:设备驱动的调试(三)(摘录)
监视工具
在 Linux 系统中,strace 是一种相当有效的跟踪工具,它的主要特点是可以被用来监视系统调用。我们不仅可以用 strace 调试一个新开始的程序,也可以调试一个已经在运行的程序(这意味着把 strace 绑定到一个已有的 PID 上) 。对于第 6 章的globalmem 字符设备文件,以 strace 方式运行如代码清单 22.9 所示的用户空间应用程序 globalmem_test
输出的每一行对应一次 Linux 系统调用,其格式为“左边=右边”,等号左边是系统调用的函数名及其参数,右边是该调用的返回值
使用 strace 虽然无法直接追踪到设备驱动中的函数,但是足够可以帮助工程师推演,如从“open("/dev/globalmem", O_RDWR) = 3”的返回结果知道/dev/globalmem 的fd 为 3,之后对 fd 为 3 的文件进行的 read()、write()和 ioctl()系统调用最终都会引起globalmem 中 file_operations 中的相应函数被调用, 通过系统调用的结果就可以知道驱动中 globalmem_read()、globalmem_write()和globalmem_ioctl()的运行结果。
内核调试器
kcore
GDB 调试器可以把内核作为一个应用程序来调试,在这种方式中,需要给 GDB指定未压缩的内核映像的文件名和“core 文件”的名字。对于一个正在运行的内核,“core 文件”就是运行时的内存映像/proc/kcore(kcore 代表整个内核地址空间,对应于所有的物理内存)。因此,使用 GDB 和 kcore 调试内核的典型命令如下:
gdb /usr/src/linux/vmlinux /proc/kcore
第一个参数是非压缩的 ELF 核心可执行文件的名字,不能是 zImage、bzImage。第二个参数是内核档案的名称,如同其他/proc 中的文件,/proc/kcore 是在被读的时候才产生的。
在“gdb
(gdb) print jiffies
$3 = 153729
(gdb) print jiffies
$4 = 153729
(gdb) print jiffies
$5 = 153729
虽然 jiffies 已经变更了,但每次 print 出来的都是第 1 次的值 153729。为避免此问题,我们可以在需要刷新 GDB 缓存时发出“core-file /proc/kcore”命令,这将导致调试器使用新的“core 文件”并且丢弃旧信息。当执行“core-file /proc/kcore”命令后,再运行“print jiffies”,值会发生变化,如下所示:
(gdb) core-file /proc/kcore
Core was generated by 'ro root=/dev/sda1 hdc=ide-scsi'.
#0 0x00000000 in globalmem_fops ()
(gdb) print jiffies
$6 = 178683
$6 比$3、$4、$5 要大,这说明新的值被 print 出来了。
为了使 Linux 系统中包含/proc/kcore 文件,必须在编译时包含“/proc/kcore support”(如图 22.10 所示),而为了给 GDB 提供 symbol 信息,必须设定 CONFIG_DEBUG_INFO选项来编译内核(如图 22.11 所示)。
在“gdb
值得一提的是,可加载模块的 symbol 并未包含在 vmlinux 中,必须使用一些辅助方法才能调试模块。 Linux 可加载模块是 ELF 格式的可执行映像, 它们被分成几个段,有 3 个典型的与模块调试相关的段。
l.text:这个段包含模块的可执行程序代码。
l.bss/.data:这两个段包含模块的变量,在编译时未初始化的变量在.bss 中,
而被初始化过的变量在.data 段里。
当一个模块被加载后,/sys/module/目录下会新增一个对应于该模块的目录,如“insmod globalmem.ko” 后, 将生成/sys/module/globalmem, 在该目录下又包含一个 sections目录,运行“ls –a”命令可以获得该目录下包含的文件
之后就可以借用 GDB 的 add-symbol-file 来添加模块的符号信息,这样之后便可以查看模块中的变量了,如下所示: