zoukankan      html  css  js  c++  java
  • 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 ", __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
    ", __FUNCTION__, __LINE__);
    111     //printk("count = %d
    ", count);
    112     //printk("mylog_r = %d
    ", mylog_r);
    113     //printk("mylog_w = %d
    ", mylog_w);
    114 
    115     error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
    116 
    117     //printk("%s %d
    ", __FUNCTION__, __LINE__);
    118     //printk("count = %d
    ", count);
    119     //printk("mylog_r = %d
    ", mylog_r);
    120     //printk("mylog_w = %d
    ", 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:
    ");
     35     printf("%s <r8 | r16 | r32> <phy addr> [num]
    ", file);
     36     printf("%s <w8 | w16 | w32> <phy addr> <val>
    ", 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
    ");
     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
    ", 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
    ", 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
    ", 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
    ",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
    ",current->pid,current->comm);
    20             printk("pc = %08x
    ",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>

  • 相关阅读:
    一起谈.NET技术,深入ASP.NET 2.0的提供者模型(2) 狼人:
    一起谈.NET技术,从.NET中委托写法的演变谈开去(上):委托与匿名方法 狼人:
    一起谈.NET技术,将Flash 嵌入WPF 程序 狼人:
    一起谈.NET技术,数组排序方法的性能比较(中):Array.Sort<T> 实现分析 狼人:
    人一生当中最应该珍惜的十种人
    《程序员的第一年》复习一下C#的【封装 多态 继承 简单计算器源码实例】
    myeclipse 修改模板
    三星将在百思买零售店内开设1400家体验店
    谷歌Q3推Android本 蚕食自己平板市场
    Spring Setting
  • 原文地址:https://www.cnblogs.com/Lwd-linux/p/6358448.html
Copyright © 2011-2022 走看看