驱动程序的调试 一. 打印: 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 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); 二. 根据内核打印的段错误信息分析 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 : 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 : [] lr : [] 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: (回溯) [] (first_drv_open+0x0/0x3c [first_drv]) from [] (chrdev_open+0x14c/0x164) [] (chrdev_open+0x0/0x164) from [] (__dentry_open+0x100/0x1e8) r8:c3e766a8 r7:c0474e20 r6:c008d73c r5:c3e740c0 r4:c049abc0 [] (__dentry_open+0x0/0x1e8) from [] (nameidata_to_filp+0x34/0x48) [] (nameidata_to_filp+0x0/0x48) from [] (do_filp_open+0x40/0x48) r4:00000002 [] (do_filp_open+0x0/0x48) from [] (do_sys_open+0x54/0xe4) r5:bec1fee0 r4:00000002 [] (do_sys_open+0x0/0xe4) from [] (sys_open+0x24/0x28) [] (sys_open+0x0/0x28) from [] (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 : [] lr : [] 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 : 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 : [] lr : [] 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 : bf000000 t first_drv_open [first_drv] 0000003c : 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 70: ebfffffe bl 70 74: ea00001f b f8 78: e3520000 cmp r2, #0 ; 0x0 7c: 11a01002 movne r1, r2 80: 1bfffffe blne 80 // 卡死的地方 84: ea00001f b 108