目录

第一种方法:利用回溯信息

第二种方法:利用PC定位错误的位置

结合(Stack:)栈信息以及反汇编反推算调用关系

 系统僵死的调试办法--使用系统时钟的中断


内核中程序崩溃,会出现oops的错误信息,比如下面

Unable to handle kernel paging request at virtual address 56000050
pgd = c3f34000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #2)
PC is at first_drv_open+0x18/0x3c [first_drv]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3f07e88  ip : c3f07e98  fp : c3f07e94
r10: 00000000  r9 : c3f06000  r8 : c3db7b20
r7 : 00000000  r6 : 00000000  r5 : c3ecd0c0  r4 : c06d3420
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33f34000  DAC: 00000015
Process firstdrvtest (pid: 792, stack limit = 0xc3f06258)
Stack: (0xc3f07e88 to 0xc3f08000)
7e80:                   c3f07ebc c3f07e98 c008d888 bf000010 00000000 c3db7b20
7ea0: c3ecd0c0 c008d73c c04740a0 c3f04344 c3f07ee4 c3f07ec0 c0089e48 c008d74c
7ec0: c3db7b20 c3f07f04 00000003 ffffff9c c002c044 c3f23000 c3f07efc c3f07ee8
7ee0: c0089f64 c0089d58 00000000 00000002 c3f07f68 c3f07f00 c0089fb8 c0089f40
7f00: c3f07f04 c3f04344 c04740a0 00000000 00000000 c3f35000 00000101 00000001
7f20: 00000000 c3f06000 c046de08 c046de00 ffffffe8 c3f23000 c3f07f68 c3f07f48
7f40: c008a16c c009fc70 00000003 00000000 c3db7b20 00000002 bef7cecc c3f07f94
7f60: c3f07f6c c008a2f4 c0089f88 00008520 bef7cec4 0000860c 00008670 00000005
7f80: c002c044 4013365c c3f07fa4 c3f07f98 c008a3a8 c008a2b0 00000000 c3f07fa8
7fa0: c002bea0 c008a394 bef7cec4 0000860c 00008720 00000002 bef7cecc 00000001
7fc0: bef7cec4 0000860c 00008670 00000001 00008520 00000000 4013365c bef7ce98
7fe0: 00000000 bef7ce74 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:c3f04344 r7:c04740a0 r6:c008d73c r5:c3ecd0c0 r4:c3db7b20
[<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:bef7cecc 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

如何使用这段错误信息

第一种方法:利用回溯信息

OrangePi android 驱动开发_反汇编

OrangePi android 驱动开发_linux_02

 sys_open --->do_sys_open--->do_filp_open--->nameidata_to_filp--->__dentry_open--->chrdev_open---->first_drv_open

根据回溯信息,可以知道上面例子的错误发生在first_drv_open

想利用栈回溯信息需要配置内核的帧子帧

make menuconfig配置内核的该项

OrangePi android 驱动开发_系统时钟_03

第二种方法:利用PC定位错误的位置

PC就是发生错误时指定的地址

OrangePi android 驱动开发_系统时钟_04

 如何使用PC值确定错误代码的位置

1.先判断内核里的代码还是用户自己加载的代码

在使用的当前内核源码里使用命令“vi System.map”,获得内核源码的地址范围,如果PC在该地址范围内,则错误位置属于内核函数,否则就是用户加载的函数

OrangePi android 驱动开发_加载_05

2.确定是加载的哪个驱动程序

查看所有加载的驱动的地址,在运行的单板使用命令cat  /proc/kallsyms >保存文件的路径(vi System.map是在内核源码中使用,cat  /proc/kallsyms是在运行的单板中使用)

OrangePi android 驱动开发_加载_06

从cat出来的信息中找到跟PC相近的地址,bf000000跟bf000018很接近,只偏移18,确定在first_drv中

OrangePi android 驱动开发_系统时钟_07

3.反汇编first_drv arm-linux-objdump -D first_drv.ko >first_drv.dis

根据反汇编以及偏移值,确定出错的地方

 4.如果出错的地方位于内核函数而不是用户加载的函数的话,对整个源码进行反汇编。

arm-linux-objdump -D vmlinux > vmlinux.dis

其他步骤跟上步一样,根据反汇编和偏移值找到出错的地方

结合(Stack:)栈信息以及反汇编反推算调用关系

如果没有栈回溯信息,还想知道first_drv_open是如何被调用的话,就需要使用栈信息

OrangePi android 驱动开发_linux_08

1.确定PC是内核模块还是加载的模块 ,方法跟第二种一样,使用cat /proc/kallsyms,然后查找

2.反汇编出错的模块以及内核。

PC值所在的错误段就位于栈底,最开始的调用者就在栈顶

 lr就是调用者的返回地址,使用lr在反汇编中找到调用者,再从调用者函数的反汇编中找到上一个调用函数的返回地址

OrangePi android 驱动开发_linux_09

 

OrangePi android 驱动开发_加载_10

 系统僵死的调试办法--使用系统时钟的中断

 如果加载的驱动没有崩溃而不打印错误信息,但是僵死了无法操作,可使用系统时钟的中断。

系统时钟是由定时器中断产生的,每隔一定时间便会触发一次,哪怕系统僵死,系统时钟也是不会停止的

使用#cat /proc/interrupt,可查到系统时钟中断的中断号

OrangePi android 驱动开发_加载_11

 内核的中断过程是中断源发生,跳转到IRQ的偏移地址,然后执行偏移地址下的中断处理函数,asm_do_IRQ(),asm_do_IRQ()根据中断号再调用具体中断的handler.系统时钟中断会导致asm_do_IRQ()不停运作,哪怕僵死了,系统时钟在运作,asm_do_IRQ()就会被调用。

用户可以在asm_do_IRQ()中添加监测打印函数,如果长时间在某个进程出不去,则打印提示信息

在asm_do_IRQ()中,添加以下带红色的字

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

         struct pt_regs *old_regs = set_irq_regs(regs);

         struct irq_desc *desc = irq_desc + irq;  

   #ifdef 1

    static pid_t pre_pid;                    //进程号  

    static int cnt=0;                          //计数值

    if(irq==30)          //判断irq中断号,是否等于系统时钟

    {  

        if(pre_pid==current->pid)

        {   

            cnt++;

        }

        else

        {

            cnt=0;   

            pre_pid=current->pid;

        }

        if(cnt==10*HZ)   //超时10s

        {

        cnt=0;

        printk("s3c2410_timer_interrupt : pid = %d, task_name = %s\n",current->pid,current->comm);

        printk("pc = %08x\n",regs->ARM_pc);

        }

}     

#endif

         ... ...

}

获得僵死位置的PC值,就可以使用上面的方法定位