目录
第一种方法:利用回溯信息
第二种方法:利用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
如何使用这段错误信息
第一种方法:利用回溯信息
sys_open --->do_sys_open--->do_filp_open--->nameidata_to_filp--->__dentry_open--->chrdev_open---->first_drv_open
根据回溯信息,可以知道上面例子的错误发生在first_drv_open
想利用栈回溯信息需要配置内核的帧子帧
make menuconfig配置内核的该项
第二种方法:利用PC定位错误的位置
PC就是发生错误时指定的地址
如何使用PC值确定错误代码的位置
1.先判断内核里的代码还是用户自己加载的代码
在使用的当前内核源码里使用命令“vi System.map”,获得内核源码的地址范围,如果PC在该地址范围内,则错误位置属于内核函数,否则就是用户加载的函数
2.确定是加载的哪个驱动程序
查看所有加载的驱动的地址,在运行的单板使用命令cat /proc/kallsyms >保存文件的路径(vi System.map是在内核源码中使用,cat /proc/kallsyms是在运行的单板中使用)
从cat出来的信息中找到跟PC相近的地址,bf000000跟bf000018很接近,只偏移18,确定在first_drv中
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是如何被调用的话,就需要使用栈信息
1.确定PC是内核模块还是加载的模块 ,方法跟第二种一样,使用cat /proc/kallsyms,然后查找
2.反汇编出错的模块以及内核。
PC值所在的错误段就位于栈底,最开始的调用者就在栈顶
lr就是调用者的返回地址,使用lr在反汇编中找到调用者,再从调用者函数的反汇编中找到上一个调用函数的返回地址
系统僵死的调试办法--使用系统时钟的中断
如果加载的驱动没有崩溃而不打印错误信息,但是僵死了无法操作,可使用系统时钟的中断。
系统时钟是由定时器中断产生的,每隔一定时间便会触发一次,哪怕系统僵死,系统时钟也是不会停止的
使用#cat /proc/interrupt,可查到系统时钟中断的中断号
内核的中断过程是中断源发生,跳转到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值,就可以使用上面的方法定位