Linux驱动开发(3):调试技术
2015-01-11 11:45
316 查看
主要的调试有3种:打印调试(printk),查询调试(/proc),监视调试(strace)。
即使采取了以上三种调试技术有时候驱动程序依然会出错,这样驱动程序在执行时候就会产生系统故障,这些错误通常会产生一个oops消息。
最后就是使用相关的调试器(gdb和kdb)跟踪代码,查看变量和计算机寄存器的值。
综上所述总共有5种调试技术:打印,查询,监视,系统故障,调试器。
这里我测试了打印调试和调试器gdb调试。
1.打印调试
printk是内核级别的打印命令,通过不同日志级别(loglevel),或者说“消息优先级”,可以让printk根据这些级别所表示的严重程度对消息进行分类。
在linux2.6内核头文件<linux/kernel.h>中定义了8种可用的日志级别字符串,消息优先级由高到低依次为:
#define KERN_EMERG "<0>" 紧急事件,一般在系统崩溃之前
#define KERN_ALERT "<1>" 立即采取行动
#define KERN_CRIT "<2>" 临界状态,严重的硬件或软件操作失败
#define KERN_ERR "<3>" 报告错误状态,一般用于硬件错误
#define KERN_WARNING "<4>" 警告
#define KERN_NOTICE "<5>" 提示
#define KERN_INFO "<6>" 提示性信息
#define KERN_DEBUG "<7>" 调试信息
例如:printk(KERN_ALERT "Here I am:%s:%i \n",_ _FILE_ _,_ _LINE_ _);
只有高于控制台日志级别的消息才能打印到控制台,而且值得注意的是,消息只能打印到控制台,linux系统有6个控制台tty1~tty6,Ctrl+Alt+F1~F6即可进入相
应的控制台,进入输入用户名lyj和linux密码(用户名是通过终端输入who查到的)。在其中编译make后装载和卸载hello模块时候可以看到打印信息,由于控制
台不能截图,所以网上对于终端不能打印出消息的原因介绍的含糊不清,其实原因就在于内核的消息是无法打印在终端的,只能通过文本控制台打印。
如果想要所有的信息都显示在控制台上可以执行:
# echo 8 > /proc/sys/kernel/printk
原因在于我们可以通过对文本文件/proc/sys/kernel/printk的访问来读取和修改控制台日志级别,里面包含的4个整数值分别为:当前日志级别,未明确指定日志级
别时的默认消息级别,最小允许日志级别,引导时默认日志级别,一般为 4 4 1 7。
2.gdb调试
模块会被划分为许多代码段,一个典型的模块可能包含十多个或者更多的代码段,但对于调试会话来讲,相关的代码段只有下面3个:
.text 模块的可执行代码
.bss
.data 这两个代码段保存模块的变量
获取模块的以上代码段的基地址的方法:
# grep 0 /sys/module/scull/sections/.text
# grep 0 /sys/module/scull/sections/.bss
# grep 0 /sys/module/scull/sections/.data
接下来打开gdb调试:
# gdb /usr/src/linux-source-3.2.0/vmlinux /proc/kcore
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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 "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /usr/src/linux-source-3.2.0/vmlinux...(no debugging symbols found)...done.
[New process 1]
Core was generated by `BOOT_IMAGE=/vmlinuz-3.2.0-29-generic root=UUID=ff29c6ce-02ea-4394-884e-2eb5d083c'.
#0 0x0000000000000000 in ?? ()
(gdb)
然后就可以通过gdb检查可装载模块的变量:
(gdb) add-symbol-file /home/lyj/scull/scull.ko 0xffffffffa047a000 -s .bss 0xffffffffa047d840 -s .data 0xffffffffa047d000
add symbol table from file "/home/lyj/scull/scull.ko" at
.text_addr = 0xffffffffa047a000
.bss_addr = 0xffffffffa047d840
.data_addr = 0xffffffffa047d000
(y or n) y
Reading symbols from /home/lyj/scull/scull.ko...done. (这里我修改了scull的Makefile文件中的DEBUD else中-O2后加-g)
(gdb) p scull_devices[0]
$1 = {data = 0x0, quantum = 4000, qset = 1000, size = 0, access_key = 0,
sem = {lock = {raw_lock = {{head_tail = 0, tickets = {head = 0,
tail = 0}}}}, count = 1, wait_list = {next = 0xffff8801232cc028,
prev = 0xffff8801232cc028}}, cdev = {kobj = {name = 0x0, entry = {
next = 0xffff8801232cc040, prev = 0xffff8801232cc040}, parent = 0x0,
kset = 0x0, ktype = 0xffffffff81c41f20, sd = 0x0, kref = {refcount = {
counter = 1}}, state_initialized = 1, state_in_sysfs = 0,
state_add_uevent_sent = 0, state_remove_uevent_sent = 0,
uevent_suppress = 0}, owner = 0xffffffffa047d5e0,
ops = 0xffffffffa047d000, list = {next = 0xffff8801232cc088,
prev = 0xffff8801232cc088}, dev = 261095424, count = 1}}
由此我们可以查看模块中各种感兴趣的数据。
即使采取了以上三种调试技术有时候驱动程序依然会出错,这样驱动程序在执行时候就会产生系统故障,这些错误通常会产生一个oops消息。
最后就是使用相关的调试器(gdb和kdb)跟踪代码,查看变量和计算机寄存器的值。
综上所述总共有5种调试技术:打印,查询,监视,系统故障,调试器。
这里我测试了打印调试和调试器gdb调试。
1.打印调试
printk是内核级别的打印命令,通过不同日志级别(loglevel),或者说“消息优先级”,可以让printk根据这些级别所表示的严重程度对消息进行分类。
在linux2.6内核头文件<linux/kernel.h>中定义了8种可用的日志级别字符串,消息优先级由高到低依次为:
#define KERN_EMERG "<0>" 紧急事件,一般在系统崩溃之前
#define KERN_ALERT "<1>" 立即采取行动
#define KERN_CRIT "<2>" 临界状态,严重的硬件或软件操作失败
#define KERN_ERR "<3>" 报告错误状态,一般用于硬件错误
#define KERN_WARNING "<4>" 警告
#define KERN_NOTICE "<5>" 提示
#define KERN_INFO "<6>" 提示性信息
#define KERN_DEBUG "<7>" 调试信息
例如:printk(KERN_ALERT "Here I am:%s:%i \n",_ _FILE_ _,_ _LINE_ _);
只有高于控制台日志级别的消息才能打印到控制台,而且值得注意的是,消息只能打印到控制台,linux系统有6个控制台tty1~tty6,Ctrl+Alt+F1~F6即可进入相
应的控制台,进入输入用户名lyj和linux密码(用户名是通过终端输入who查到的)。在其中编译make后装载和卸载hello模块时候可以看到打印信息,由于控制
台不能截图,所以网上对于终端不能打印出消息的原因介绍的含糊不清,其实原因就在于内核的消息是无法打印在终端的,只能通过文本控制台打印。
如果想要所有的信息都显示在控制台上可以执行:
# echo 8 > /proc/sys/kernel/printk
原因在于我们可以通过对文本文件/proc/sys/kernel/printk的访问来读取和修改控制台日志级别,里面包含的4个整数值分别为:当前日志级别,未明确指定日志级
别时的默认消息级别,最小允许日志级别,引导时默认日志级别,一般为 4 4 1 7。
2.gdb调试
模块会被划分为许多代码段,一个典型的模块可能包含十多个或者更多的代码段,但对于调试会话来讲,相关的代码段只有下面3个:
.text 模块的可执行代码
.bss
.data 这两个代码段保存模块的变量
获取模块的以上代码段的基地址的方法:
# grep 0 /sys/module/scull/sections/.text
# grep 0 /sys/module/scull/sections/.bss
# grep 0 /sys/module/scull/sections/.data
接下来打开gdb调试:
# gdb /usr/src/linux-source-3.2.0/vmlinux /proc/kcore
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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 "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /usr/src/linux-source-3.2.0/vmlinux...(no debugging symbols found)...done.
[New process 1]
Core was generated by `BOOT_IMAGE=/vmlinuz-3.2.0-29-generic root=UUID=ff29c6ce-02ea-4394-884e-2eb5d083c'.
#0 0x0000000000000000 in ?? ()
(gdb)
然后就可以通过gdb检查可装载模块的变量:
(gdb) add-symbol-file /home/lyj/scull/scull.ko 0xffffffffa047a000 -s .bss 0xffffffffa047d840 -s .data 0xffffffffa047d000
add symbol table from file "/home/lyj/scull/scull.ko" at
.text_addr = 0xffffffffa047a000
.bss_addr = 0xffffffffa047d840
.data_addr = 0xffffffffa047d000
(y or n) y
Reading symbols from /home/lyj/scull/scull.ko...done. (这里我修改了scull的Makefile文件中的DEBUD else中-O2后加-g)
(gdb) p scull_devices[0]
$1 = {data = 0x0, quantum = 4000, qset = 1000, size = 0, access_key = 0,
sem = {lock = {raw_lock = {{head_tail = 0, tickets = {head = 0,
tail = 0}}}}, count = 1, wait_list = {next = 0xffff8801232cc028,
prev = 0xffff8801232cc028}}, cdev = {kobj = {name = 0x0, entry = {
next = 0xffff8801232cc040, prev = 0xffff8801232cc040}, parent = 0x0,
kset = 0x0, ktype = 0xffffffff81c41f20, sd = 0x0, kref = {refcount = {
counter = 1}}, state_initialized = 1, state_in_sysfs = 0,
state_add_uevent_sent = 0, state_remove_uevent_sent = 0,
uevent_suppress = 0}, owner = 0xffffffffa047d5e0,
ops = 0xffffffffa047d000, list = {next = 0xffff8801232cc088,
prev = 0xffff8801232cc088}, dev = 261095424, count = 1}}
由此我们可以查看模块中各种感兴趣的数据。
相关文章推荐
- 【Linux 驱动】第四章 调试技术
- Linux设备驱动调试技术 1
- Linux驱动开发常用调试工具-------之devmem
- Android/Linux驱动开发之使用dev_dbg调试设备驱动
- Linux驱动开发-字符设备控制技术笔记 3
- Linux设备驱动调试技术
- Linux驱动开发常用调试工具---之devmem
- Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)
- LINUX驱动开发核心技术
- Linux驱动开发常用调试工具---之内存读写工具devmem和devkmem
- Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)
- linux 驱动开发调试问题
- Linux设备驱动调试技术 3
- Linux驱动开发常用调试工具---之内存读写工具devmem和devkmem
- Linux调试技术介绍(编程开发)
- Linux驱动开发常用调试工具-------之devmem
- Linux驱动开发常用调试工具-------之devmem
- Linux驱动开发常用调试工具-------之devmem
- arm+linux下usb驱动开发,移植Libusb以及开发应用调试过程
- Linux驱动开发常用内存调试工具 memtool and devmem