02.驱动调试

驱动程序的调试
一. 打印: printk, 自制proc文件
UBOOT传入console=ttySAC0(串口) console=tty1(LCD)
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
            vscnprintf
            
            // Copy the output into log_buf.
            // 把临时BUFFER里的数据稍作处理,再写入log_buf
            // 比如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);

4.语句

printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
//将打印出 绝对路径,函数,行数

  cat /proc/sys/kernel/printk       //查看默认的打印级别console_loglevel

  echo “8 4 17”>/proc/sys/kernel/printk            //修改默认的打印级别console_loglevel

  set bootargs loglevel=0                      //不打印任何东西

  set bootargs loglevel=10                      //打印所有东西
  执行dmsg本质是读取/proc/kmsg文件

5.打印到proc虚拟文件

要打印的信息会存放在log_buf中,通过文件/proc/kmsg可以来访问这个buf,然后将信息打印出来。由此我们就想了,我们是否可以构造这样一个mylog_buf,里面存放我们所需要的打印信息,通过一个/proc/kmsg文件可以访问该buf,然后打印出来?答案是肯定的!

5.1 proc机制分析

1 #ifdef CONFIG_PRINTK
2     {
3         struct proc_dir_entry *entry;
4         entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
5         if (entry)
6             entry->proc_fops = &proc_kmsg_operations;
7     }
8 #endif

创建一个proc入口,并设置操作函数

其操作函数如下:

1 const struct file_operations proc_kmsg_operations = {
2     .read        = kmsg_read,
3     .poll        = kmsg_poll,
4     .open        = kmsg_open,
5     .release    = kmsg_release,
6 };

所以我们可以仿照该文件写出我们自己的mymsg

  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <asm/uaccess.h>
  7 #include <asm/irq.h>
  8 #include <asm/io.h>
  9 #include <asm/arch/regs-gpio.h>
 10 #include <asm/hardware.h>
 11 #include <linux/proc_fs.h>
 12
 13 #define MYLOG_BUF_LEN 1024
 14
 15 struct proc_dir_entry *myentry;
 16
 17 static char mylog_buf[MYLOG_BUF_LEN];
 18 static char tmp_buf[MYLOG_BUF_LEN];
 19 static int mylog_r = 0;
 20 static int mylog_r_for_read = 0;
 21 static int mylog_w = 0;
 22
 23 static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);
 24
 25 static int is_mylog_empty(void)
 26 {
 27     return (mylog_r == mylog_w);
 28 }
 29
 30 static int is_mylog_empty_for_read(void)
 31 {
 32     return (mylog_r_for_read == mylog_w);
 33 }
 34
 35 static int is_mylog_full(void)
 36 {
 37     return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
 38 }
 39
 40 static void mylog_putc(char c)
 41 {
 42     if (is_mylog_full())
 43     {
 44         /* 丢弃一个数据 */
 45         mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
 46
 47         if ((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)
 48         {
 49             mylog_r_for_read = mylog_r;
 50         }
 51     }
 52
 53     mylog_buf[mylog_w] = c;
 54     mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;
 55
 56     /* 唤醒等待数据的进程 */
 57     wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */
 58 }
 59
 60 static int mylog_getc(char *p)
 61 {
 62     if (is_mylog_empty())
 63     {
 64         return 0;
 65     }
 66     *p = mylog_buf[mylog_r];
 67     mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
 68     return 1;
 69 }
 70
 71 static int mylog_getc_for_read(char *p)
 72 {
 73     if (is_mylog_empty_for_read())
 74     {
 75         return 0;
 76     }
 77     *p = mylog_buf[mylog_r_for_read];
 78     mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN;
 79     return 1;
 80 }
 81
 82
 83 int myprintk(const char *fmt, ...)
 84 {
 85     va_list args;
 86     int i;
 87     int j;
 88
 89     va_start(args, fmt);
 90     i = vsnprintf(tmp_buf, INT_MAX, fmt, args);
 91     va_end(args);
 92
 93     for (j = 0; j < i; j++)
 94         mylog_putc(tmp_buf[j]);
 95
 96     return i;
 97 }
 98
 99 static ssize_t mymsg_read(struct file *file, char __user *buf,
100              size_t count, loff_t *ppos)
101 {
102     int error = 0;
103     int i = 0;
104     char c;
105
106     /* 把mylog_buf的数据copy_to_user, return */
107     if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read())
108         return -EAGAIN;
109
110     //printk("%s %d\n", __FUNCTION__, __LINE__);
111     //printk("count = %d\n", count);
112     //printk("mylog_r = %d\n", mylog_r);
113     //printk("mylog_w = %d\n", mylog_w);
114
115     error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
116
117     //printk("%s %d\n", __FUNCTION__, __LINE__);
118     //printk("count = %d\n", count);
119     //printk("mylog_r = %d\n", mylog_r);
120     //printk("mylog_w = %d\n", mylog_w);
121
122     /* copy_to_user */
123     while (!error && (mylog_getc_for_read(&c)) && i < count) {
124         error = __put_user(c, buf);
125         buf++;
126         i++;
127     }
128
129     if (!error)
130         error = i;
131
132     return error;
133 }
134
135 static int mymsg_open(struct inode *inode, struct file *file)
136 {
137     mylog_r_for_read = mylog_r;
138     return 0;
139 }
140
141 const struct file_operations proc_mymsg_operations = {
142     .open = mymsg_open,
143     .read = mymsg_read,
144 };
145
146 static int mymsg_init(void)
147 {
148     myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
149     if (myentry)
150         myentry->proc_fops = &proc_mymsg_operations;
151     return 0;
152 }
153
154 static void mymsg_exit(void)
155 {
156     remove_proc_entry("mymsg", &proc_root);
157 }
158
159 module_init(mymsg_init);
160 module_exit(mymsg_exit);
161
162 EXPORT_SYMBOL(myprintk);
163
164 MODULE_LICENSE("GPL");

打印到proc虚拟文件

可以直接使用myprintk,然后用cat /proc/mymsg指令查到我们的打印信息。

小结:在本文件里面我们做了两件事情,一件事情是定义了一个写函数,当我们在用户空间使用命令:cat /proc/mymsg的时候,就会调用到这个读函数,这个读函数会将mylog_buf中的数据拷贝到用户空间,那么mylog_buf里面的数据哪里来的呢?这就是我们做的另外一件事情,我们定义了一个打印函数,这个打印函数会将要打印的数据写入一个临时缓冲区,然后又从临时缓冲区里面取出数据放入mylog_buf中。cat /proc/mymsg的候就会将mylog_buf中的数据拷贝到用户空间,就可以显示出来了!

二. 根据内核打印的段错误信息分析
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

三. 自制工具—寄存器编辑器
当我们调试驱动程序的时候,可能要调整寄存器的设置。按照我们之前的作法就是直接在程序里面修改,然后重新编译程序。但是这种方法比较麻烦,我们可以编写一个工具,可以直接对寄存器进行修改,这就是我们说的寄存器编辑器。

  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <linux/irq.h>
  7 #include <asm/uaccess.h>
  8 #include <asm/irq.h>
  9 #include <asm/io.h>
 10 #include <asm/arch/regs-gpio.h>
 11 #include <asm/hardware.h>
 12 #include <linux/poll.h>
 13 #include <linux/device.h>
 14
 15 #define KER_RW_R8      0
 16 #define KER_RW_R16     1
 17 #define KER_RW_R32     2
 18
 19 #define KER_RW_W8      3
 20 #define KER_RW_W16     4
 21 #define KER_RW_W32     5
 22
 23
 24 static int major;
 25 static struct class *class;
 26 static struct class_device    *ker_dev;
 27
 28
 29 static int ker_rw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 30 {
 31     volatile unsigned char  *p8;
 32     volatile unsigned short *p16;
 33     volatile unsigned int   *p32;
 34     unsigned int val;
 35     unsigned int addr;
 36
 37     unsigned int buf[2];
 38
 39     copy_from_user(buf, (const void __user *)arg, 8);
 40     addr = buf[0];
 41     val  = buf[1];
 42
 43     p8  = (volatile unsigned char *)ioremap(addr, 4);
 44     p16 = p8;
 45     p32 = p8;
 46
 47     switch (cmd)
 48     {
 49         case KER_RW_R8:
 50         {
 51             val = *p8;
 52             copy_to_user((void __user *)(arg+4), &val, 4);
 53             break;
 54         }
 55
 56         case KER_RW_R16:
 57         {
 58             val = *p16;
 59             copy_to_user((void __user *)(arg+4), &val, 4);
 60             break;
 61         }
 62
 63         case KER_RW_R32:
 64         {
 65             val = *p32;
 66             copy_to_user((void __user *)(arg+4), &val, 4);
 67             break;
 68         }
 69
 70         case KER_RW_W8:
 71         {
 72             *p8 = val;
 73             break;
 74         }
 75
 76         case KER_RW_W16:
 77         {
 78             *p16 = val;
 79             break;
 80         }
 81
 82         case KER_RW_W32:
 83         {
 84             *p32 = val;
 85             break;
 86         }
 87     }
 88
 89     iounmap(p8);
 90     return 0;
 91 }
 92
 93 static struct file_operations ker_rw_ops = {
 94     .owner   = THIS_MODULE,
 95     .ioctl   = ker_rw_ioctl,
 96 };
 97
 98 static int ker_rw_init(void)
 99 {
100     major = register_chrdev(0, "ker_rw", &ker_rw_ops);
101
102     class = class_create(THIS_MODULE, "ker_rw");
103
104     /* 为了让mdev根据这些信息来创建设备节点 */
105     ker_dev = class_device_create(class, NULL, MKDEV(major, 0), NULL, "ker_rw"); /* /dev/ker_rw */
106
107     return 0;
108 }
109
110 static void ker_rw_exit(void)
111 {
112     class_device_unregister(ker_dev);
113     class_destroy(class);
114     unregister_chrdev(major, "ker_rw");
115 }
116
117 module_init(ker_rw_init);
118 module_exit(ker_rw_exit);
119
120
121 MODULE_LICENSE("GPL");

自制寄存器修改器

  1 #include <sys/types.h>
  2 #include <sys/stat.h>
  3 #include <fcntl.h>
  4 #include <stdio.h>
  5 #include <poll.h>
  6 #include <signal.h>
  7 #include <sys/types.h>
  8 #include <unistd.h>
  9 #include <fcntl.h>
 10 #include <stdlib.h>
 11 #include <string.h>
 12
 13 #define KER_RW_R8      0
 14 #define KER_RW_R16     1
 15 #define KER_RW_R32     2
 16
 17 #define KER_RW_W8      3
 18 #define KER_RW_W16     4
 19 #define KER_RW_W32     5
 20
 21
 22 /* Usage:
 23  * ./regeditor r8  addr [num]
 24  * ./regeditor r16 addr [num]
 25  * ./regeditor r32 addr [num]
 26  *
 27  * ./regeditor w8  addr val
 28  * ./regeditor w16 addr val
 29  * ./regeditor w32 addr val
 30  */
 31
 32 void print_usage(char *file)
 33 {
 34     printf("Usage:\n");
 35     printf("%s <r8 | r16 | r32> <phy addr> [num]\n", file);
 36     printf("%s <w8 | w16 | w32> <phy addr> <val>\n", file);
 37 }
 38
 39 int main(int argc, char **argv)
 40 {
 41     int fd;
 42     unsigned int buf[2];
 43     unsigned int i;
 44     unsigned int num;
 45
 46     if ((argc != 3) && (argc != 4))
 47     {
 48         print_usage(argv[0]);
 49         return -1;
 50     }
 51
 52     fd = open("/dev/ker_rw", O_RDWR);
 53     if (fd < 0)
 54     {
 55         printf("can‘t open /dev/ker_rw\n");
 56         return -2;
 57     }
 58
 59     /* addr */
 60     buf[0] = strtoul(argv[2], NULL, 0);
 61
 62     if (argc == 4)
 63     {
 64         buf[1] = strtoul(argv[3], NULL, 0);
 65         num    = buf[1];
 66     }
 67     else
 68     {
 69         num = 1;
 70     }
 71
 72     if (strcmp(argv[1], "r8") == 0)
 73     {
 74         for ( i = 0; i < num; i++)
 75         {
 76             ioctl(fd, KER_RW_R8, buf);  /* val = buf[1] */
 77             printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned char)buf[1]);
 78             buf[0] += 1;
 79         }
 80     }
 81     else if (strcmp(argv[1], "r16") == 0)
 82     {
 83         for ( i = 0; i < num; i++)
 84         {
 85             ioctl(fd, KER_RW_R16, buf);  /* val = buf[1] */
 86             printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned short)buf[1]);
 87             buf[0] += 2;
 88         }
 89     }
 90     else if (strcmp(argv[1], "r32") == 0)
 91     {
 92         for ( i = 0; i < num; i++)
 93         {
 94             ioctl(fd, KER_RW_R32, buf);  /* val = buf[1] */
 95             printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned int)buf[1]);
 96             buf[0] += 4;
 97         }
 98     }
 99     else if (strcmp(argv[1], "w8") == 0)
100     {
101         ioctl(fd, KER_RW_W8, buf);  /* val = buf[1] */
102     }
103     else if (strcmp(argv[1], "w16") == 0)
104     {
105         ioctl(fd, KER_RW_W16, buf);  /* val = buf[1] */
106     }
107     else if (strcmp(argv[1], "w32") == 0)
108     {
109         ioctl(fd, KER_RW_W32, buf);  /* val = buf[1] */
110     }
111     else
112     {
113         printf(argv[0]);
114         return -1;
115     }
116
117     return 0;
118
119 }

测试程序

四. 修改内核来定位系统僵死问题

在系统运行的过程中可能出现一种状况:系统僵死。

系统处于僵死状态时,程序将不再运行。但是即便系统僵死,系统时钟中断还是以固定的频率发生我们可以进入时钟中断处理函数把当前僵死的进程

信息打印出来

我们在命令行输入:cat /proc/interrupts

 1  打印出如下信息:
 2            CPU0
 3  30:      85713         s3c  S3C2410 Timer Tick   //这个就是系统时钟中断
 4  33:          0         s3c  s3c-mci
 5  34:          0         s3c  I2SSDI
 6  35:          0         s3c  I2SSDO
 7  37:         12         s3c  s3c-mci
 8  42:          0         s3c  ohci_hcd:usb1
 9  43:          0         s3c  s3c2440-i2c
10  51:       3509     s3c-ext  eth0
11  60:          0     s3c-ext  s3c-mci
12  70:         96   s3c-uart0  s3c2440-uart
13  71:         92   s3c-uart0  s3c2440-uart
14  83:          0           -  s3c2410-wdt
15 Err:          0
16
17  30:      85713         s3c  S3C2410 Timer Tick   :这个就是系统时钟中断,我们可以再内核中查找:S3C2410 Timer Tick,会找到这样一个结构体:
18 static struct irqaction s3c2410_timer_irq = {
19 .name= "S3C2410 Timer Tick",
20 .flags= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
21 .handler= s3c2410_timer_interrupt,
22 };
23
24 其中:s3c2410_timer_interrupt就是中断处理函数!我们在其中加入一些信息:
25
26 s3c2410_timer_interrupt(int irq, void *dev_id)
27 {
28        static pid_t pre_pid;
29        static int cnt=0;
30
31        if(pre_pid==current->pid)
32         {
33             cnt++;
34         }
35        else
36         {
37             cnt=0;
38             pre_pid=current->pid;
39         }
40        //如果本进程十秒钟还没有离开的话,就会打印下面的语句
41        if(cnt==10*HZ)
42         {
43             cnt=0;
44             printk("s3c2410_timer_interrupt : pid = %d, task_name = %s\n",current->pid,current->comm);
45         }
46
47 write_seqlock(&xtime_lock);
48 timer_tick();
49 write_sequnlock(&xtime_lock);
50 return IRQ_HANDLED;
51 }

输出信息

 1 关于我们加入的代码有两点需要说一下:
 2 第一:每一个进程都需要用一个结构体来表示:task_struct,这里面保存着与进程的一些状态信息,而current是一个宏,它代表当前的进程,也是一个task_struct结构体。所以current->pid就代表本进程的id,而current->comm就代表本进程的名字!
 3 第二:HZ是一个宏定义,它表示1秒钟发生多少次中断,10*HZ就代表10秒钟发生多少次中断!
 4
 5 下面我们测试一下:
 6 我们可以某个驱动程序里面放入语句:while(1);这样的话,当程序执行到这里的时候就会僵死掉,在之前没有加入上述信息之前,没有任何打印信息,我们根本不知道是哪一个进程发生了僵死,现在的话,没过10秒就会打印相关信息,告诉我们现在是什么进程正在发生僵死!我的测试打印信息如下:
 7 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
 8 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
 9 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
10
11 不过这样还不够详细,有没有办法知道是在哪里发生了僵死呢?这也是有办法的!
12 先来说一下原理:在应用程序的执行的时候,会一直以固定的频率发生时钟中断,那么发生中断的时候肯定会保存现场吧,那么这个保存现场的时候就要保存发生中断处的PC值,这样才能返回,那么如果把这个PC值打印出来不就知道在哪里发生中断了吗!
13
14 我们之前分析过,发生中断的时候经过一些前期处理之后会调用:asm_do_IRQ这个函数
15 在这个函数里面我们发现了一个结构体:struct pt_regs,这个结构体就用来保存发生中断时的现场,其中PC值就是:ARM_pc
16 我们将上面在:s3c2410_timer_interrupt里面加入的信息都删除,并在:asm_do_IRQ函数里面加入如下信息:

进一步优化

 1  static pid_t pre_pid;
 2        static int cnt=0;
 3         //时钟中断的中断号是30
 4         if(irq==30)
 5         {
 6         if(pre_pid==current->pid)
 7         {
 8             cnt++;
 9         }
10         else
11         {
12             cnt=0;
13             pre_pid=current->pid;
14         }
15
16         if(cnt==10*HZ)
17         {
18             cnt=0;
19             printk("s3c2410_timer_interrupt : pid = %d, task_name = %s\n",current->pid,current->comm);
20             printk("pc = %08x\n",regs->ARM_pc);//打印pc值
21         }
22         }
23
24 我们在次测试的话,会打印出如下信息:
25 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
26 pc = bf000084
27 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
28 pc = bf000084
29 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
30 pc = bf000084
31 s3c2410_timer_interrupt : pid = 752, task_name = firstdrvtest
32 pc = bf000084

输出信息和测试

./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>

时间: 2024-11-08 09:43:48

02.驱动调试的相关文章

Windows7驱动调试小Tips

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);}/* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colb

Linux驱动设计—— 驱动调试技术

参考博客与书籍: <Linux设备驱动开发详解> <Linux设备驱动程序> http://blog.chinaunix.net/uid-24219701-id-2884942.html 对于驱动程序设计来说,核心问题之一就是如何完成调试.当前常用的驱动调试技术可分为: 1. 打印调试 printk 重定向控制台消息 消息记录 开启和关闭消息速度限制 打印设备编号 2. 调试器调试 gdb kdb内核调试器 kgdb补丁 3. 查询调试 使用/proc文件系统 ioctl方法 4.

设备驱动调试和移植的一般方法

做linux底层软件工作也有两年了,算上研究生时期对底层软件的研究,加起来也快四年了.慢慢地发现有必要总结一些一般性的方法了.因为一般性的方法有宏观上的指导意义,以后调试和移植驱动时,经常性地回味这些一般性的方法可以防止自己犯同样的错误,进而少走弯路,以最高的效率完成工作. 当谈到底层软件,我们一般都会想到bootloader.BSP.device driver.linux kernel等等.这篇文章将会着重介绍linux device driver调试的一般性方法.另外,关于设备驱动移植的方法

linux设备驱动第四篇:驱动调试方法

linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法linux设备驱动第四篇:驱动调试方法 http://v.17173.com/playlist_18716517.htmlhttp://v.17173.com/playlist_18716521.

展讯7731平台驱动调试总结-驱动配置部分

转载至:http://blog.csdn.net/bmw7bmw7/article/details/46126223 展讯7731平台驱动调试总结-驱动配置部分 1. 关键配置文件路径 1). 项目板级配置:idh.code/device/sprd/scx35_sp7731geaplus_pad_qhd/文件夹内各文件 ⑴.BoardConfig.mk---板级宏配置文件.包括设置该板(项目)所使用的uboot/kerenl全局配置宏文件,摄像头接口类型.分辨率等参数,所使用的重力加速度.光线传

linux驱动调试技术

 对于驱动程序设计来说,核心问题之一就是如何完成调试.当前常用的驱动调试技术可分为: ? 打印调试(printk) ? 调试器调试(kgdb) ? 查询调试 1.合理使用printk #ifdef PDEBUG #define PLOG(fmt,args...) printk(KERN_DEBUG "scull:"fmt,##args) #else #define PLOG(fmt,args...) /*do nothing */ #endif Makefile作如下修改: DEB

linux驱动调试--段错误之oops信息分析

linux驱动调试--段错误之oops信息分析 http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id=4923447 1. 分析coredump打印信息 2. 确定发生错误的函数 看发生错误的地方的函数和pc PC is at segment_test_open+0x14/0x1c [segdrv] 看pc值: pc : [<7f000014>]    lr : [<800d958c>

[windows驱动]windows8.1驱动调试前戏

人们都说在干正事之前,得先做足前戏才会爽,我一直很认同这个观点,下面我来总结下进行windows8.1的WDK调试所要做的准备工作. 软件安装: 1.VS2013. 2.WDK8.1 3.Windows Kits安装目录下的8.1\Romote\x86(x64)\wdk Test Target Setup x86-x86_en-us.exe(远程调试使用) 调试环境设置: 安装WDK8.1之后,驱动开发.部署.调试等工具都集成到VS2013 IDE中了,在进行调试之前,我们需要对开发环境做必要的

i.mx6 lvds接口的DE模式液晶屏驱动调试

我这篇文章主要讲述i.mx6 平台下 采用DE模式的lvds液晶屏的驱动调试, 阅读该文章之前请先阅读如下两篇我转载的文章,这两篇文章是理解我这篇文章的基础知识. 1.        lcd fb参数如何计算: http://blog.csdn.net/liuhuahan/article/details/43447657 2.        camera_lcd之DE和HV模式区别 http://blog.csdn.net/liuhuahan/article/details/43489269 详