一、使用printk
这是驱动开发中最朴实无华,同时也是最常用和有效的手段。scull驱动的main.c第338行如下,就是使用printk进行调试的例子,这样的例子相信大家在阅读驱动源码时随处可见。
printk(KERN_alert "wakeup by signal in process %dn", current->;pid);
printk的功能与我们经常在应用程序中使用的printf是一样的,不同之处在于printk可以在打印字符串前面加上内核定义的宏,例如上面例子中的KERN_alert(注意:宏与字符串之间没有逗号):
#define KERN_EMERG ""
#define KERN_alert ""
#define KERN_CRIT ""
#define KERN_ERR ""
#define KERN_WARNING ""
#define KERN_NOTICE ""
#define KERN_INFO ""
#define KERN_DEBUG ""
#define DEFAULT_CONSOLE_LOGLEVEL 7
这个宏是用来定义需要打印的字符串的级别,值越小,级别越高。内核中有个参数用来控制是否将printk打印的字符串输出到控制台(屏幕或者/sys/log/syslog日志文件)
# cat /proc/sys/kernel/printk
6 4 1 7
第一个6表示级别高于(小于)6的消息才会被输出到控制台,第二个4表示如果调用printk时没有指定消息级别(宏)则消息的级别为4,第三个1表示接受的最高(最小)级别是1,第四个7表示系统启动时第一个6原来的初值是7。
因此,如果你发现在控制台上看不到你程序中某些printk的输出,请使用 echo 8 > /proc/sys/kernel/printk 来解决。
我们在复杂驱动的开发过程中,为了调试会在源码中加入成百上千的printk语句。而当调试完毕形成最终产品的时候必然会将这些printk语句删除(为什么?想想你自己是驱动的使用者而不是开发者吧。记住:己所不欲,勿施于人),这个工作量是不小的。最要命的是,如果我们将调试用的printk语句删除后,用户又报告我们的驱动有bug,所以我们又不得不手工将这些上千条的printk语句再重新加上。OMG,杀了我吧。所以,我们需要一种能方便地打开和关闭调试信息的手段。哪里能找到这种手段呢?哈哈,远在天边,近在眼前。看看scull驱动或者leds驱动的源代码吧!
#define LEDS_DEBUG
#undef PDEBUG
#ifdef LEDS_DEBUG
#ifdef __KERNEL__
#define PDEBUG(fmt, args…) printk( KERN_EMERG "leds: " fmt, ## args)
#else
#define PDEBUG(fmt, args…) fprintf(stderr, fmt, ## args)
#endif
#else
#define PDEBUG(fmt, args…)
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args…)
这样一来,在开发驱动的过程中,如果想打印调试消息,我们就可以用 PDEBUG("address of i_cdev is %pn", inode->i_cdev); ,如果不想看到该调试消息,就只需要简单的将 PDEBUG 改为 PDEBUGG 即可。而当我们调试完毕形成最终产品时,只需要简单地将第1行注释掉即可。
上边那一段代码中的__KERNEL__是内核中定义的宏,当我们编译内核(包括模块)时,它会被定义。当然如果你不明白代码中的…和##是什么意思的话,就请认真查阅一下gcc关于预处理部分的资料吧!如果你实在太懒不愿意去查阅的话,那就充当VC工程师把上面的代码copy到你的代码中去吧。
二、查看OOP消息
OOP意为惊讶。当你的驱动有问题,内核不惊讶才怪:嘿!小子,你干吗乱来!好吧,让我们来看看内核是如何惊讶的。
根据 faulty.c 编译出faulty.ko,并 insmod faulty.ko。执行 echo yang > /dev/faulty ,结果内核就惊讶了。内核为什么会惊讶呢?因为faulty驱动的write函数执行了*(int *)0=0,向内存0地址写入,这是内核绝对不会容许的。
ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
*(int *)0=0;
return 0;
}
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd=c3894000
[00000000] *pgd=33830031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1] PREEMPT
Modules linked in: faulty scull
CPU: 0 Not tainted (2.6.22.6 #4)
PC is at faulty_write+0×10/0×18 [faulty]
LR is at vfs_write+0xc4/0×148
pc : [] lr : [] psr: a0000013
sp : c3871f44 ip : c3871f54 fp : c3871f50
r10: 4021765c r9 : c3870000 r8 : 00000000
r7 : 00000004 r6 : c3871f78 r5 : 40016000 r4 : c38e5160
r3 : c3871f78 r2 : 00000004 r1 : 40016000 r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 33894000 DAC: 00000015
Process sh (pid: 745, stack limit=0xc3870258)
Stack: (0xc3871f44 to 0xc3872000)
1f40: c3871f74 c3871f54 c0088eb8 bf00608c 00000004 c38e5180 c38e5160
1f60: c3871f78 00000000 c3871fa4 c3871f78 c0088ffc c0088e04 00000000 00000000
1f80: 00000000 00000004 40016000 40215730 00000004 c002c0e4 00000000 c3871fa8
1fa0: c002bf40 c0088fc0 00000004 40016000 00000001 40016000 00000004 00000000
1fc0: 00000004 40016000 40215730 00000004 00000001 00000000 4021765c 00000000
1fe0: 00000000 bea60964 0000266c 401adb40 60000010 00000001 00000000 00000000
Backtrace:
[] (faulty_write+0×0/0×18 [faulty]) from [] (vfs_write+0xc4/0×148)
[] (vfs_write+0×0/0×148) from [] (sys_write+0x4c/0×74)
r7:00000000 r6:c3871f78 r5:c38e5160 r4:c38e5180
[] (sys_write+0×0/0×74) from [] (ret_fast_syscall+0×0/0x2c)
r8:c002c0e4 r7:00000004 r6:40215730 r5:40016000 r4:00000004
Code: e1a0c00d e92dd800 e24cb004 e3a00000 (e5800000)
1行惊讶的原因,也就是报告出错的原因;
2-4行是OOP信息序号;
5行是出错时内核已加载模块;
6行是发生错误的CPU序号;
7-15行是发生错误的位置,以及当时CPU各个寄存器的值,这最有利于我们找出问题所在地;
16行是当前进程的名字及进程ID
17-23行是出错时,栈内的内容
24-29行是栈回溯信息,可看出直到出错时的函数递进调用关系(确保CONFIG_frame_POINTER被定义)
30行是出错指令及其附近指令的机器码,出错指令本身在小括号中
反汇编 faulty.ko:
arm-linux-objdump -D faulty.ko > faulty.dis ;cat faulty.dis
可以看到如下的语句如下:
0000007c :
7c: e1a0c00d mov ip, sp
80: e92dd800 stmdb sp!, {fp, ip, lr, pc}
84: e24cb004 sub fp, ip, #4 ; 0×4
88: e3a00000 mov r0, #0 ; 0×0
8c: e5800000 str r0, [r0]
90: e89da800 ldmia sp, {fp, sp, pc}
定位出错位置以及获取相关信息的过程:
9 pc : [] lr : [] psr: a0000013
25 [] (faulty_write+0×0/0×18 [faulty]) from [] (vfs_write+0xc4/0×148)
26 [] (vfs_write+0×0/0×148) from [] (sys_write+0x4c/0×74)
- 下一篇: 我来教大家科乐麻将摸宝神器【抖音视频】
- 上一篇: 我来教大家樱花之盛有没有挂【抖音视频】