linux驱动调试方法

本文详细介绍了驱动程序的调试方法,包括打印输出、内核与硬件驱动的交互,以及如何利用dmesg命令获取printk缓冲区内的信息。同时,文章通过实例分析了如何根据内核打印的段错误信息来定位问题,特别是针对作为模块或编入内核的情况。此外,文章还介绍了如何使用自制工具和修改内核来定位系统僵死问题,并提供了修改后的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

驱动程序的调试
一. 打印: prink, 自制proc文件
UBOOT传入console=ttySAC0 console=tty1
1. 内核处理UBOOT传入的参数
console_setup
add_preferred_console // 我想用名为"ttySAC0"的控制台,先记录下来


2. 硬件驱动的入口函数里:
drivers/serial/s3c2410.c
register_console(&s3c24xx_serial_console);


3. printk
vprintk
/* Emit the output into the temporary buffer */
// 先把输出信息放入临时BUFFER**************处理前的buffer
vscnprintf

// Copy the output into log_buf.
// 把临时BUFFER里的数据稍作处理,再写入log_buf***********处理后的buffer
// 比如printk("abc")会得到"<4>abc", 再写入log_buf
// 可以用dmesg命令把log_buf里的数据打印出来重现内核的输出信息


// 调用硬件的write函数输出
release_console_sem();
call_console_drivers(_con_start, _log_end);
// 从log_buf得到数据,算出打印级别
_call_console_drivers(start_print, cur_index, msg_level);
// 如果可以级别够格打印
if ((msg_log_level < console_loglevel
__call_console_drivers
con->write(con, &LOG_BUF(start), end - start);


dmesg如何将printk缓冲区内的数据拿出来呢:肯定是打开了某个文件

ll  /proc/kmsg     proc是一个虚拟的文件系统

在系统启动的时候会执行/etc/init.d/rcS中的一句:mount -a 

mount -a 会根据文件/etc/fstab中的内容来挂在文件系统


可以用cat  /proc/mounts看挂在了那些文件系统

dmesg 命令取出/proc/kmsg中的内容   等同于:cat  /proc/kmsg中也会显示跟dmesg一样的内容



写一个自己的命令和缓冲区来模仿dmesg的执行过程       //见     附录一

linux/fs/proc目录下放着proc的各种操作函数

proc_misc.c的入口函数

ifdef  CONFIG_PRINTK

entry=create_proc_entry("kmsg",S_IRUSER ,&proc_root);    //在proc'目录下创建一个文件

entry->proc_fops= 此处的read write 都是操作一个环形缓冲区

二. 根据内核打印的段错误信息分析
a. 作为模块:
1. 根据pc值确定该指令属于内核还是外加的模块
pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序?
先判断是否属于内核的地址: 看System.map确定内核的函数的地址范围:c0004000~c03265a4


如果不属于System.map里的范围,则它属于insmod加载的驱动程序


2. 假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序?
先看看加载的驱动程序的函数的地址范围
cat /proc/kallsyms  (内核函数、加载的函数的地址)
从这些信息里找到一个相近的地址, 这个地址<=0xbf000018
比如找到了:
bf000000 t first_drv_open [first_drv]


3. 找到了first_drv.ko
在PC上反汇编它: arm-linux-objdump -D first_drv.ko > frist_drv.dis
在dis文件里找到first_drv_open


    first_drv.dis文件里              insmod后
00000000 <first_drv_open>:       bf000000 t first_drv_open[first_drv]
00000018                         pc = bf000018
                                 






./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
内核使用56000050来访问时发生了错误


pgd = c3eb0000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #1)
PC is at first_drv_open+0x18(该指令的偏移)/0x3c(该函数的总大小) [first_drv]
PC就是发生错误的指令的地址
大多时候,PC值只会给出一个地址,不到指示说是在哪个函数里


LR is at chrdev_open+0x14c/0x164
LR寄存器的值


pc = 0xbf000018


pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3c7be88  ip : c3c7be98  fp : c3c7be94
r10: 00000000  r9 : c3c7a000  r8 : c049abc0
r7 : 00000000  r6 : 00000000  r5 : c3e740c0  r4 : c06d41e0
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
执行这条导致错误的指令时各个寄存器的值


Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33eb0000  DAC: 00000015
Process firstdrvtest (pid: 777, stack limit = 0xc3c7a258)
发生错误时当前进程的名称是firstdrvtest



Stack: (0xc3c7be88 to 0xc3c7c000)
be80:                   c3c7bebc c3c7be98 c008d888 bf000010 00000000 c049abc0 
bea0: c3e740c0 c008d73c c0474e20 c3e766a8 c3c7bee4 c3c7bec0 c0089e48 c008d74c 
bec0: c049abc0 c3c7bf04 00000003 ffffff9c c002c044 c3d10000 c3c7befc c3c7bee8 
bee0: c0089f64 c0089d58 00000000 00000002 c3c7bf68 c3c7bf00 c0089fb8 c0089f40 
bf00: c3c7bf04 c3e766a8 c0474e20 00000000 00000000 c3eb1000 00000101 00000001 
bf20: 00000000 c3c7a000 c04a7468 c04a7460 ffffffe8 c3d10000 c3c7bf68 c3c7bf48 
bf40: c008a16c c009fc70 00000003 00000000 c049abc0 00000002 bec1fee0 c3c7bf94 
bf60: c3c7bf6c c008a2f4 c0089f88 00008520 bec1fed4 0000860c 00008670 00000005 
bf80: c002c044 4013365c c3c7bfa4 c3c7bf98 c008a3a8 c008a2b0 00000000 c3c7bfa8 
bfa0: c002bea0 c008a394 bec1fed4 0000860c 00008720 00000002 bec1fee0 00000001 
bfc0: bec1fed4 0000860c 00008670 00000002 00008520 00000000 4013365c bec1fea8 
bfe0: 00000000 bec1fe84 0000266c 400c98e0 60000010 00008720 00000000 00000000 


Backtrace: (回溯)
[<bf000000>] (first_drv_open+0x0/0x3c [first_drv]) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3e766a8 r7:c0474e20 r6:c008d73c r5:c3e740c0 r4:c049abc0
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:bec1fee0 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000) 
Segmentation fault



b. 编入内核
Modules linked in:
CPU: 0    Not tainted  (2.6.22.6 #2)
PC is at first_drv_open+0x18/0x3c
LR is at chrdev_open+0x14c/0x164
pc : [<c014e6c0>]    lr : [<c008638c>]    psr: a0000013
sp : c3a03e88  ip : c3a03e98  fp : c3a03e94
r10: 00000000  r9 : c3a02000  r8 : c03f3c60
r7 : 00000000  r6 : 00000000  r5 : c38a0c50  r4 : c3c1e780
r3 : c014e6a8  r2 : 56000050  r1 : c031a47c  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 339f0000  DAC: 00000015
Process firstdrvtest (pid: 750, stack limit = 0xc3a02258)


1. 根据pc值确定该指令属于内核还是外加的模块
pc=c014e6c0 属于内核(看System.map)


2. 反汇编内核: arm-linux-objdump -D vmlinux > vmlinux.dis
在dis文件里搜c014e6c0
c014e6a8 <first_drv_open>:
c014e6a8:       e1a0c00d        mov     ip, sp
c014e6ac:       e92dd800        stmdb   sp!, {fp, ip, lr, pc}
c014e6b0:       e24cb004        sub     fp, ip, #4      ; 0x4
c014e6b4:       e59f1024        ldr     r1, [pc, #36]   ; c014e6e0 <.text+0x1276e0>
c014e6b8:       e3a00000        mov     r0, #0  ; 0x0
c014e6bc:       e5912000        ldr     r2, [r1]
c014e6c0:       e5923000        ldr     r3, [r2] // 在此出错 r2=56000050






3. 根据栈信息分析函数调用过程
# ./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3e78000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #48)
PC is at first_drv_open+0x18/0x3c [first_drv]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>]    lr : [<c008c888>]    psr: a0000013
3.1 根据PC确定出错位置
bf000018 属于 insmod的模块
bf000000 t first_drv_open       [first_drv]


3.2 确定它属于哪个函数
反汇编first_drv.ko












sp : c3e69e88  ip : c3e69e98  fp : c3e69e94
r10: 00000000  r9 : c3e68000  r8 : c0490620
r7 : 00000000  r6 : 00000000  r5 : c3e320a0  r4 : c06a8300
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33e78000  DAC: 00000015
Process firstdrvtest (pid: 752, stack limit = 0xc3e68258)
Stack: (0xc3e69e88 to 0xc3e6a000)
9e80:                   c3e69ebc c3e69e98 c008c888 bf000010 00000000 c0490620 
                        first_drv_open'sp    lr             chrdev_open'sp


9ea0: c3e320a0 c008c73c c0465e20 c3e36cb4 c3e69ee4 c3e69ec0 c0088e48 c008c74c 
                                                            lr    
                                                                                 
9ec0: c0490620 c3e69f04 00000003 ffffff9c c002b044 c06e0000 c3e69efc c3e69ee8 
      __dentry_open'sp


9ee0: c0088f64 c0088d58 00000000 00000002 c3e69f68 c3e69f00 c0088fb8 c0088f40 
      lr                nameidata_to_filp'sp                lr
      
9f00: c3e69f04 c3e36cb4 c0465e20 00000000 00000000 c3e79000 00000101 00000001 
      do_filp_open'sp


9f20: 00000000 c3e68000 c04c1468 c04c1460 ffffffe8 c06e0000 c3e69f68 c3e69f48 
9f40: c008916c c009ec70 00000003 00000000 c0490620 00000002 be94eee0 c3e69f94 
9f60: c3e69f6c c00892f4 c0088f88 00008520 be94eed4 0000860c 00008670 00000005 
               lr                do_sys_open'sp


9f80: c002b044 4013365c c3e69fa4 c3e69f98 c00893a8 c00892b0 00000000 c3e69fa8 
                                          lr                sys_open'sp


9fa0: c002aea0 c0089394 be94eed4 0000860c 00008720 00000002 be94eee0 00000001 
      lr                ret_fast_syscall'sp
                        
9fc0: be94eed4 0000860c 00008670 00000002 00008520 00000000 4013365c be94eea8 
9fe0: 00000000 be94ee84 0000266c 400c98e0 60000010 00008720 00000000 00000000 




三. 自制工具
寄存器编辑器




四. 修改内核来定位系统僵死问题
./firstdrvtest on 
asm_do_IRQ => s3c2410_timer_interrupt : pid = 752, task name = firstdrvtest
pc = bf000084
asm_do_IRQ => s3c2410_timer_interrupt : pid = 752, task name = firstdrvtest
pc = bf000084   // 对于中断, pc-4才是发生中断瞬间的地址


看/proc/kallsyms                                
first_drv.dis
00000000 <first_drv_open>:                     bf000000 t first_drv_open[first_drv]         
0000003c <first_drv_write>:
  3c: e1a0c00d mov ip, sp
  40: e92dd800 stmdb sp!, {fp, ip, lr, pc}
  44: e24cb004 sub fp, ip, #4; 0x4
  48: e24dd004 sub sp, sp, #4; 0x4
  4c: e3cd3d7f bic r3, sp, #8128; 0x1fc0
  50: e3c3303f bic r3, r3, #63; 0x3f
  54: e5933008 ldr r3, [r3, #8]
  58: e0910002 adds r0, r1, r2
  5c: 30d00003 sbcccs r0, r0, r3
  60: 33a03000 movcc r3, #0; 0x0
  64: e3530000 cmp r3, #0; 0x0
  68: e24b0010 sub r0, fp, #16; 0x10
  6c: 1a00001c bne e4 <init_module+0x5c>
  70: ebfffffe bl 70 <first_drv_write+0x34>
  74: ea00001f b f8 <init_module+0x70>
  78: e3520000 cmp r2, #0; 0x0
  7c: 11a01002 movne r1, r2
  80: 1bfffffe blne 80 <first_drv_write+0x44>       // 卡死的地方
  84: ea00001f b 108 <init_module+0x80>




附录一

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/proc_fs.h>


#define MYLOG_BUF_LEN 1024


struct proc_dir_entry *myentry;


static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r = 0;                         //保存重新cat /proc/mymsg 时的位置,重新cat时把它赋值给mylog_r_for_read
static int mylog_r_for_read = 0;      //  cat /proc/mymsg 时读的位置
static int mylog_w = 0;                      


static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);


static int is_mylog_empty(void)
{
return (mylog_r == mylog_w);
}


static int is_mylog_empty_for_read(void)
{
return (mylog_r_for_read == mylog_w);
}


static int is_mylog_full(void)
{
return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
}


static void mylog_putc(char c)
{
if (is_mylog_full())
{
/* 丢弃一个数据 */
mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;


if ((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)
{
mylog_r_for_read = mylog_r;
}
}


mylog_buf[mylog_w] = c;
mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;


/* 唤醒等待数据的进程 */
    wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */
}


static int mylog_getc(char *p)
{
if (is_mylog_empty())
{
return 0;
}
*p = mylog_buf[mylog_r];
mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
return 1;
}


static int mylog_getc_for_read(char *p)
{
if (is_mylog_empty_for_read())
{
return 0;
}
*p = mylog_buf[mylog_r_for_read];
mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN;
return 1;
}




int myprintk(const char *fmt, ...)
{
va_list args;
int i;
int j;


va_start(args, fmt);
i = vsnprintf(tmp_buf, INT_MAX, fmt, args);
va_end(args);

for (j = 0; j < i; j++)
mylog_putc(tmp_buf[j]);

return i;
}


static ssize_t mymsg_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int error = 0;
int i = 0;
char c;


/* 把mylog_buf的数据copy_to_user, return */
if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read())
return -EAGAIN;


//printk("%s %d\n", __FUNCTION__, __LINE__);
//printk("count = %d\n", count);
//printk("mylog_r = %d\n", mylog_r);
//printk("mylog_w = %d\n", mylog_w);


error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());


//printk("%s %d\n", __FUNCTION__, __LINE__);
//printk("count = %d\n", count);
//printk("mylog_r = %d\n", mylog_r);
//printk("mylog_w = %d\n", mylog_w);


/* copy_to_user */
while (!error && (mylog_getc_for_read(&c)) && i < count) {
error = __put_user(c, buf);
buf++;
i++;
}

if (!error)
error = i;

return error;
}


static int mymsg_open(struct inode *inode, struct file *file)
{
mylog_r_for_read = mylog_r;
return 0;
}


const struct file_operations proc_mymsg_operations = {
.open = mymsg_open,
.read = mymsg_read,
};


static int mymsg_init(void)
{
myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
if (myentry)
myentry->proc_fops = &proc_mymsg_operations;
return 0;
}


static void mymsg_exit(void)
{
remove_proc_entry("mymsg", &proc_root);
}


module_init(mymsg_init);
module_exit(mymsg_exit);


EXPORT_SYMBOL(myprintk);


MODULE_LICENSE("GPL");



//debugfs-----

/drivers/video/omap2/dss/core.c中:    

 #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)

……

int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
struct dentry *d;


d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,write, &dss_debug_fops);

                                   第一个参数是目录的名称,第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里

                          mount -t debugfs none /sys/kernel/debug


if (IS_ERR(d))
return PTR_ERR(d);


return 0;
}

……

#else

……

int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
return 0;
}

……

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值