Chinaunix首页 | 论坛 | 博客
  • 博客访问: 384750
  • 博文数量: 57
  • 博客积分: 2299
  • 博客等级: 大尉
  • 技术积分: 1109
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-27 23:12
文章分类
文章存档

2011年(4)

2010年(53)

分类: 嵌入式

2010-02-28 00:39:04

说明:本单元由于回家忘了带usb下载线,无法下载加入调试选项的内核。先看网上大侠们的学习笔记!边学习驱动边实践调试技术。
一、                        内核中的调试支持
在前面已经建议过:学习编写驱动程序要构建安装自己的内核(标准主线内核)。最重要的原因之一是:内核开发者已经建立了多项用于调试的功能。但是由于这些功能会造成额外的输出,并导致能下降,因此发行版厂商通常会禁止发行版内核中的调试功能。为了实现内核调试,我在内核配置上增加了几项:
  Kernel hacking  -à    
        [*] Magic SysRq key
        [*] Kernel debugging
        [*]   Debug slab memory allocations 
        [*]   Spinlock and rw-lock debugging: basic checks
        [*]   Spinlock debugging: sleep-inside-spinlock checking
        [*]   Compile the kernel with debug info 
        [*] Magic SysRq key
Device Drivers  -à 
        Generic Driver Options  -à
          [*] Driver Core verbose debug messages
General setup  -à
       [*] Configure standard kernel features (for small systems)  -à
          [*]   Load all symbols for debugging/ksymoops
书上介绍的还有其他配置,有的我不需要,或是s3c2440不支持,菜单里看不见。
二、                        通过打印调试
1)                                              Printk
首先,printk有8个loglevel,定义在中:
/**
 * upper_32_bits – return bits 32-63 of a number
 * @n: the number we’re accessing
 *
 * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress
 * the “right shift count >= width of type” warning when that quantity is
 * 32-bits.
 */
#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
#define KERN_EMERG      “<0>”   /* system is unusable                   */
紧急事件消息,一般为系统崩溃之前提示的消息
#define KERN_ALERT      “<1>”   /* action must be taken immediately     */
需要立即采取动作的情况
#define KERN_CRIT       “<2>”   /* critical conditions                  */
临界情况,通常涉及严重的硬件或软件操作失败
#define KERN_ERR        “<3>”   /* error conditions                     */
错误报告,驱动程序常用来报告来自硬件的问题
#define KERN_WARNING    “<4>”   /* warning conditions                  */
对可能出现问题的情况进行警告,不会对系统造成严重问题
#define KERN_NOTICE     “<5>”   /* normal but significant condition     */
有必要进行提示的正常情形,许多与安全相关的状况用此级别
#define KERN_INFO       “<6>”   /* informational                        */
提示信息
#define KERN_DEBUG      “<7>”   /* debug-level messages                 */
用于调试信息
两个有用的例子:
printk(KERN_DEBUG “Here I am: %s:%i\n”, __FILE__, __LINE__);
printk(KERN_CRIT “I’m trashed; giving up on %p\n”, ptr);
注:如果打印的消息不以newline字符结尾,则不会输出,此规则在试验中有所体会;如果不以“\n”结尾是不会打印出来的。
未指定优先级的默认级别定义在/kernel/printk.c中:
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
当优先级的值小于console_loglevel这个整数变量的值,信息才能显示出来。而console_loglevel的初始值DEFAULT_CONSOLE_LOGLEVEL也定义在/kernel/printk.c中:
 #define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
 
而在运行是改变console_loglevel的程序(《Linux设备驱动程序(第3版)》提供)如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define __LIBRARY__ /* _syscall3 and friends are only available through this */
#include <linux/unistd.h>
/* define the system call, to override the library function */
_syscall3(int syslog, int type, char *bufp, int len);
int main(int argc, char **argv)
{
         int level;
         if (argc==2) {
                   level = atoi(argv[1]); /* the chosen console */
         } else {
                   fprintf(stderr,%s: need a single arg\n”,argv[0]); exit(1);
         }
         if (syslog(8,NULL,level) < 0) {
                   fprintf(stderr,%s: syslog(setlevel): %s\n”, argv[0],strerror(errno));
                   exit(1);
         }
         exit(0);
}

存在的问题:通过此程序改变的改变控制台输出优先级未能如书上和Tekkaman  Ninja那样成功试验,带解决;
还有通过对/proc/sys/kernel/printk的访问来改变console_loglevel的值:
[root@gfy-S3C2440 /tmp]# cat /proc/sys/kernel/printk
7       4       1       7
[root@gfy-S3C2440 /tmp]# insmod hello.ko
0array[0] = a
1array[1] = b
2array[2] = c
3array[3] = d
Test string
whom = world
Hello, world,I am gufeiyang!
[root@gfy-S3C2440 /tmp]# rmmod hello.ko
Goodbye, cruel world,YY will be back!
[root@gfy-S3C2440 /tmp]# echo 1 > /proc/sys/kernel/printk
[root@gfy-S3C2440 /tmp]# cat /proc/sys/kernel/printk
1       4       1       7
[root@gfy-S3C2440 /tmp]# insmod hello.ko
[root@gfy-S3C2440 /tmp]# rmmod hello.ko
[root@gfy-S3C2440 /tmp]#
四个数字的含义:当前的loglevel、默认loglevel、最小允许的loglevel、引导时的默认loglevel
说明:打印方式的调试,主要是比较当前使用printk时的打印优先级参数和当前的loglevel,只有在打印优先级参数小于当前loglevel时并且printk以“\n”结尾时,消息才可以显示在控制台或者串口打印机上。当前的loglevel改方式有如上两种,其中第一种通过sys_syslog系统调用修改时,如果系统启用的klogd需要先杀死klogd,然后再用新的-c选项重新启动,才可以修改当前的loglevel。实践证明,2.6.25内核的loglevel是7,引导时的loglevel尚不清楚,不过可以测试得出。
 
为了方便的打开和关闭调试信息,《Linux设备驱动程序(第3版)》提供以下源码:

/* Macros to help debugging */
#undef PDEBUG /* undef it, just in case */
#ifdef SCULL_DEBUG
# ifdef __KERNEL__
     /* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args…) printk( KERN_DEBUG “scull: “ fmt, ## args)
# else /* This one for user space */
# define PDEBUG(fmt, args…) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args…) /* not debugging: nothing */
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args…) /* nothing: it’s a placeholder */
Makefile中要添加的语句:
# Comment/uncomment the following line to disable/enable debugging
DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O –g –DSCULL_DEBUG #-O” is needed to expand inlines
else
  DEBFLAGS = -O2
endif
CFLAGS += $(DEBFLAGS)

经过本人实验,在2.6.25版本内核中,CFLAGS += $(DEBFLAGS)不能正确编译,需要改成EXTRA_CFLAGS += $(DEBFLAGS),根据错误提示修改即可;还有# define PDEBUG(fmt, args…) printk( KERN_DEBUG “scull: “ fmt, ## args)定义成KERN_DEBUG,实验证明,在2.6.25内核中的消息默认级别是7,即console_loglevel的值为7,KERN_DEBUG正好为7,不经修改时不能显示PDEBUG的信息的,所以需要echo 8 > /proc/sys/kernel/printk以使console_loglevel值为8,那么小于其的KERN_DEBUG PDEBUG级别调试信息才能显示出来。
实验如下:
[root@gfy-S3C2440 /tmp]# insmod scull.ko
Scull_major = 249
Module Initialed!
Chracter Device Driver Start!
[root@gfy-S3C2440 /tmp]# echo 8 > /proc/sys/kernel/printk
[root@gfy-S3C2440 /tmp]# rmmod scull
Module exit!
Chracter Device Driver End!
[root@gfy-S3C2440 /tmp]# insmod scull.ko
scull: Debug!
Scull_major = 249
Module Initialed!
Chracter Device Driver Start!
 
为了避免printk重复输出过快而阻塞系统,比如一不小心打开了上千行的调试信息,内核使用以下函数跳过部分输出:
int printk_ratelimit(void);
典型的应用如下:
if (printk_ratelimit( ))
printk(KERN_NOTICE “The printer is still on fire\n”);
可以通过修改/proc/sys/kernel/printk_ratelimit(重开信息前应等待的秒数)和/proc/sys/kernel/printk_ratelimit_burst(在速度限制前可接受的信息数)来定制printk_ratelimit的行为。
 
Linux还提供了打印设备编号的宏(在中定义):
int print_dev_t(char *buffer, dev_t dev);
char *format_dev_t(char *buffer, dev_t dev);
两个函数的唯一区别是:print_dev_t返回打印字符数,format_dev_t返回缓冲区指针。注意缓冲区char
*buffer的大小应至少有20B。
三、通过查询调试
多数情况中,获取相关信息的最好方法是在需要的时候才去查询系统信息,而不是持续不断地产生数据。
使用/proc文件系统/proc文件系统是一种特殊的、由软件创建的文件系统,内核使用他向外界导出信息。/proc下面的每个文件都绑定于一个内核函数,用户读取其中的文件时,该函数动态的生成文件的内容。如以前用过的:
[root@gfy-S3C2440 /]# cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  6 lp
  7 vcs
 10 misc
 13 input
 21 sg
 29 fb
 81 video4linux
 90 mtd
 99 ppdev
128 ptm
136 pts
使用/proc的模块必须包含,而使用seq_file接口要包含
 
说明:在我的试验中,由于在家得不到Tekkaman  Ninja的源码,自己根据《linux驱动设备程序》上写的程序这个老有问题,不能得出正确的现象,《LDD3》上似乎是建立了多个设备,而在我自己写的初始化函数中可以看出只初始化了一个设备,所以在后面不知道怎么修改了;我觉得对于初学者来说看懂《linux设备驱动程序(第三版)》的最大难点就在于如何根据书上的程序段构造完整可以编译的程序。
 
至于其他的调试方法,如gdb、LTT、SysRq等方法,在其他的书籍,如:《嵌入式Linux系统开发技术详解-基于ARM》、《构建嵌入式Linux系统》等,上讲解的更为详细,以后专门花时间研究。
以上为Tekkaman  Ninja大哥的原话,其实对于像我这样的初学者来说,用打印的方式调试就已经足够了,更深入的学习等学习深入了再看,现在只要知道有这样一种方式就可以了;
       调试技术的学习暂告一段落,深入学习边用边学;
阅读(1609) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~