当前位置: 首页 » 产品 » 新闻资讯 » 正文

Linux系统驱动开发调试技术指南

放大字体  缩小字体 发布日期: 2024-09-27 07:24   来源:http://www.baidu.com/  作者:无忧资讯  浏览次数:6
核心提示:  一、使用printk  这是驱动开发中最朴实无华,同时也是最常用和有效的手段。scull驱动的main.c第338行如下,就是使用printk

  一、使用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)

 
 
[ 产品搜索 ]  [ 加入收藏 ]  [ 告诉好友 ]  [ 打印本文 ]  [ 违规举报 ]  [ 关闭窗口 ]

 

 
推荐图文
推荐产品
点击排行
    行业协会  备案信息  可信网站