今天進入《Linux裝置驅動程式(第3版)》第四章調試技術的學習。
一、核心中的調試支援
在前面已經建議過:學習編寫驅動程式要構建安裝自己的核心(標準主線核心)。最重要的原因之一是:核心開發人員已經建立了多項用於調試的功能。但是由於這些功能會造成額外的輸出,並導致能下降,因此發行版廠商通常會禁止發行版核心中的調試功能。
為了實現核心調試,我在核心配置上增加了幾項:
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,定義在中:
#define KERN_EMERG "" /* system is unusable */
#define KERN_ALERT "" /* action must be taken immediately*/
#define KERN_CRIT "" /* critical conditions */
#define KERN_ERR "" /* error conditions */
#define KERN_WARNING "" /* warning conditions */
#define KERN_NOTICE "" /* normal but significant condition */
#define KERN_INFO "" /* informational */
#define KERN_DEBUG "" /* debug-level messages */
未指定優先順序的預設層級定義在/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);
}
最關鍵的“syslog(8,NULL,level)”語句我不理解,沒有找到相關資料。但是通過在ARM9板上的實驗表明:程式是ok的!我用Hello world模組做了實驗,現象和書上的一致。
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./setlevel 1
[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod hello.ko
[Tekkaman2440@SBC2440V4]#rmmod hello
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./setlevel 7
[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod hello.ko
Hello, Tekkaman Ninja !
[Tekkaman2440@SBC2440V4]#rmmod hello
Goodbye, Tekkaman Ninja !
Love Linux !Love ARM ! Love KeKe !
[Tekkaman2440@SBC2440V4]#
還有通過對/proc/sys/kernel/printk的訪問來改變console_loglevel的值:
[Tekkaman2440@SBC2440V4]#echo 1 > /proc/sys/kernel/printk
[Tekkaman2440@SBC2440V4]#cat /proc/sys/kernel/printk
1 4 1 7
[Tekkaman2440@SBC2440V4]#insmod hello.ko
[Tekkaman2440@SBC2440V4]#rmmod hello
[Tekkaman2440@SBC2440V4]#echo 7 > /proc/sys/kernel/printk
[Tekkaman2440@SBC2440V4]#cat /proc/sys/kernel/printk7 4 1 7
[Tekkaman2440@SBC2440V4]#insmod hello.ko
Hello, Tekkaman Ninja !
[Tekkaman2440@SBC2440V4]#rmmod hello
Goodbye, Tekkaman Ninja !
Love Linux !Love ARM ! Love KeKe !
四個數位含義:當前的loglevel、預設loglevel、最小允許的loglevel、引導時的預設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)
為了避免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下面的每個檔案都綁定於一個核心功能,使用者讀取其中的檔案時,該函數動態組建檔案的內容。如以前用過的:
[Tekkaman2440@SBC2440V4]#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
7 vcs
10 misc
13 input
14 sound
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 scull
253 usb_endpoint
254 rtc
Block devices:
1 ramdisk
256 rfd
7 loop
31 mtdblock
93 nftl
96 inftl
179 mmc
使用/proc的模組必須包含,而使用seq_file介面要包含。
具體的應用方法看來源程式、做實驗更有效果。
至於其他的調試方法,如gdb、LTT、SysRq等方法,在其他的書籍,如:《嵌入式Linux系統開發技術詳解-基於ARM》、《構建嵌入式Linux系統》等,上講解的更為詳細,以後專門花時間研究。
四、源碼實驗
模組程式連結:
模組程式
模組測試程式連結:
模組測試程式
實驗現象:
[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod scull_debug.ko scull_nr_devs=1 scull_quantum=6 scull_qset=2
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./scull_test
write code=6
write code=6
write code=6
write code=2
read code=6
read code=6
read code=6
read code=2
[0]=0 [1]=1 [2]=2 [3]=3 [4]=4
[5]=5 [6]=6 [7]=7 [8]=8 [9]=9
[10]=10 [11]=11 [12]=12 [13]=13 [14]=14
[15]=15 [16]=16 [17]=17 [18]=18 [19]=19
[Tekkaman2440@SBC2440V4]#cd /proc/
[Tekkaman2440@SBC2440V4]#ls
1 751 cmdline kallsyms stat
2 769 cpu kmsg swaps
3 77 cpuinfo loadavg sys
4 778 crypto locks sysrq-trigger
5 779 devices meminfo sysvipc
59 78 diskstats misc timer_list
6 781 driver modules tty
60 783 execdomains mounts uptime
63 785 filesystems mtd version
65 79 fs net vmstat
707 80 ide partitions yaffs
708 819 interrupts scullmem zoneinfo
709 asound iomem scullseq
710 buddyinfo ioports self
742 bus irq slabinfo
[Tekkaman2440@SBC2440V4]#cat scullmem
Device 0: qset 2, q 6, sz 20
item at c071ebd4, qset at c071ef7c
item at c071ef14, qset at c071eee0
0: c071eeac
1: c071ee78
[Tekkaman2440@SBC2440V4]#cat scullseq
Device 0: qset 2, q 6, sz 20
item at c071ebd4, qset at c071ef7c
item at c071ef14, qset at c071eee0
0: c071eeac
1: c071ee78
[Tekkaman2440@SBC2440V4]#rmmod scull_debug
[Tekkaman2440@SBC2440V4]#ls
1 742 buddyinfo iomem self
2 751 bus ioports slabinfo
3 769 cmdline irq stat
4 77 cpu kallsyms swaps
5 778 cpuinfo kmsg sys
59 779 crypto loadavg sysrq-trigger
6 78 devices locks sysvipc
60 781 diskstats meminfo timer_list
63 783 driver misc tty
65 785 execdomains modules uptime
707 79 filesystems mounts version
708 80 fs mtd vmstat
709 824 ide net yaffs
710 asound interrupts partitions zoneinfo