




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1第第4 章章 调试技术调试技术内核中的调试支持内核中的调试支持 通过打印调试通过打印调试 通过查询调试通过查询调试 通过监视调试通过监视调试 调试系统故障调试系统故障 调试器和相关工具调试器和相关工具2内核中的调试支持内核中的调试支持l内核开发者建立了许多用于支持调试的功能,以方便调试,内核开发者建立了许多用于支持调试的功能,以方便调试,但这些功能会导致性能下降,发行版自带内核中往往关闭了但这些功能会导致性能下降,发行版自带内核中往往关闭了这些功能这些功能l内核开发者需要这些功能,应该打开这些调试功能内核开发者需要这些功能,应该打开这些调试功能l重新配置、编译、安装内核重新配置、编译、安装内
2、核lKernel hacking菜单中有如下选项菜单中有如下选项CONFIG_DEBUG_KERNEL 调试选项总开关,关时全关,开时并不打开下级子开关调试选项总开关,关时全关,开时并不打开下级子开关CONFIG_DEBUG_SLAB 开启内存分配函数中的类型检查,可以检测内存溢出及开启内存分配函数中的类型检查,可以检测内存溢出及忘记初始化错误忘记初始化错误3内核中的调试支持内核中的调试支持CONFIG_DEBUG_PAGEALLOC 该选项大降速度,但可定位特定内存损坏错误所在位置该选项大降速度,但可定位特定内存损坏错误所在位置CONFIG_DEBUG_SPINLOCK 内核将捕获未初始化自
3、旋锁及两次解开同一锁等错误内核将捕获未初始化自旋锁及两次解开同一锁等错误CONFIG_DEBUG_SPINLOCK_SLEEP 检查拥有自旋锁时的休眠企图检查拥有自旋锁时的休眠企图CONFIG_INIT_DEBUG 用于初始化完成后对用于初始化的内存空间的访问检查用于初始化完成后对用于初始化的内存空间的访问检查CONFIG_DEBUG_INFO 使内核的构造包含完整的调试信息,使内核的构造包含完整的调试信息,gdb需要需要4内核中的调试支持内核中的调试支持CONFIG_MAGIC_SYSRQ 打开打开“SYsRq魔法魔法”按键按键CONFIG_DEBUG_STACKOVERFLOWCONFIG
4、_DEBUG_STACK_USAGE 帮助跟踪内核栈溢出问题,前者在内核中增加明确有溢帮助跟踪内核栈溢出问题,前者在内核中增加明确有溢出检查,后者让内核监视栈的使用,出检查,后者让内核监视栈的使用, 并通过并通过SYsRq按按键输出一些统计信息键输出一些统计信息lCONFIG_KALLSYMS出现在出现在General setup/Standard features菜单中菜单中内核中包含用于调试上下文的符号信息内核中包含用于调试上下文的符号信息5内核中的调试支持内核中的调试支持lCONFIG_IKCONFIGlCONFIG_IKCONFIG_PROC出现在出现在General setup菜单中
5、菜单中让完整的内核配置状态包含到内核中,并可通过让完整的内核配置状态包含到内核中,并可通过/proc访问访问lCONFIG_ACPI_DEBUG出现在出现在Power management/ACPI菜单中菜单中打开打开ACPI(Advanced Configuration and Power Interface)中中的详细调试信息,若疑遇到问题与的详细调试信息,若疑遇到问题与ACPI相关时用相关时用lCONFIG_DEBUG_DRIVER出现在出现在Device drivers菜单中菜单中打开驱动程序核心中的调试信息,可以帮助跟踪底层支持代码中打开驱动程序核心中的调试信息,可以帮助跟踪底层支持
6、代码中的问题的问题6内核中的调试支持内核中的调试支持lCONFIG_SCSI_CONSTANTS出现在出现在Device drivers/SCSI device support菜单中菜单中打开详细的打开详细的SCSI错误消息,开发错误消息,开发SCSI驱动时可用驱动时可用lCONFIG_INPUT_EVBUG出现在出现在Device drivers/Input device support菜单中菜单中打开对输入事件的详细记录,开发输入设备驱动时可用打开对输入事件的详细记录,开发输入设备驱动时可用lCONFIG_PROFILING出现在出现在Profiling support菜单中菜单中用于系统
7、性能的调节,但对跟踪内核挂起及相关问题也有帮助用于系统性能的调节,但对跟踪内核挂起及相关问题也有帮助7第第4 章章 调试技术调试技术内核中的调试支持内核中的调试支持 通过打印调试通过打印调试 通过查询调试通过查询调试 通过监视调试通过监视调试 调试系统故障调试系统故障 调试器和相关工具调试器和相关工具8通过打印调试通过打印调试lPrintk是内核调试中最简单有效的工具,通过在内核是内核调试中最简单有效的工具,通过在内核代码的某些关键地方放置一条代码的某些关键地方放置一条printk语句,打印一些语句,打印一些信息,即可知道运行代码的某些行为,例:信息,即可知道运行代码的某些行为,例: prin
8、tk(KERN_CRIT Im trashed; giving up on %pn, ptr);lPrintk的使用需要包含头文件的使用需要包含头文件l只有在消息优先级高于控制台优先级时,信息才能出只有在消息优先级高于控制台优先级时,信息才能出现在终端上现在终端上l控制台日志级别控制台日志级别console_loglevel的初始值为的初始值为DEFAULT_CONSOLE_LOGLEVE,可通过可通过sys_syslog系统调用修改,也可通过文本文件系统调用修改,也可通过文本文件/proc/sys/kernel/printk修改修改9通过打印调试通过打印调试l例例 如下指令将控制台日志级别修
9、改为如下指令将控制台日志级别修改为8 # echo 8 /proc/sys/kernel/printkl下表为消息日志级别,数字越小,优先级越高下表为消息日志级别,数字越小,优先级越高 日志级别日志级别 描述描述 KERN_EMERG 紧急事件消息紧急事件消息 KERN_ALERT 用于需要立即采取动作的情况用于需要立即采取动作的情况 KERN_CRIT 临界状态,通常涉及严重的硬件或软件操作失败临界状态,通常涉及严重的硬件或软件操作失败 KERN_ERR 用于报告错误状态用于报告错误状态 KERN_WARNING 对可能出现问题的情况进行警告对可能出现问题的情况进行警告 KERN_NOTIC
10、E 有必要进行提示的情形有必要进行提示的情形 KERN_INFO 提示性信息提示性信息 KERN_DEBUG 用于调试信息用于调试信息10通过打印调试通过打印调试l未指定消息优先级的未指定消息优先级的 printk 语句缺省优先级是语句缺省优先级是 DEFAULT_MESSAGE_LOGLEVEL, 在在 kernel/printk.c 里指定作为一个整数里指定作为一个整数. 在在 2.6.10 内核内核中中, 为为 KERN_WARNING。11通过打印调试通过打印调试l消息如何被记录消息如何被记录printk将消息写到一个长度为将消息写到一个长度为_LOG_BUF_LEN(4KB1MB,默
11、认,默认16KB)的循环缓冲区的循环缓冲区中中,然后唤醒在该缓冲区上等待消息的进程然后唤醒在该缓冲区上等待消息的进程 睡眠在睡眠在syslog系统调用上的进程系统调用上的进程(调用调用syslog()-写、写、openlog()-打打开或开或closelog()-关闭的进程关闭的进程) 正在读取文件正在读取文件/proc/kmsg的进程的进程用户空间守护进程用户空间守护进程klogd运行时读取内核消息(通过运行时读取内核消息(通过syslog系统调用或系统调用或/proc/dmsg文件,默认为后者),并传文件,默认为后者),并传给另一个守护进程给另一个守护进程syslogdsyslogd根据根
12、据/etc/syslog.conf设置的处理方法处理收到的设置的处理方法处理收到的消息,默认追加到消息,默认追加到/var/log/message中中klogd也可将消息存入一指定文件中,不给也可将消息存入一指定文件中,不给syslogd12通过打印调试通过打印调试l若若klogd没运行,消息不会送到用户空间,只能查看没运行,消息不会送到用户空间,只能查看/proc/kmsg文件(可通过文件(可通过dmesg命令)命令)13通过打印调试通过打印调试l开启及关闭消息开启及关闭消息printk对调试和测试新代码很有帮助,但在正式发布驱动对调试和测试新代码很有帮助,但在正式发布驱动程序时,就需要删除
13、或禁用程序时,就需要删除或禁用如何有效地启用如何有效地启用/禁用它们?禁用它们?#undef PDEBUG #ifdef SCULL_DEBUG# ifdef _KERNEL_# define PDEBUG(fmt, args.) printk( KERN_DEBUG scull: fmt, # args)# else # define PDEBUG(fmt, args.) fprintf(stderr, fmt, # args)# endif#else# define PDEBUG(fmt, args.) #endif#undef PDEBUGG#define PDEBUGG(fmt, ar
14、gs.)14通过打印调试通过打印调试l打印速度控制打印速度控制Printk有时每秒钟能产生上万条消息充满控制台或使有时每秒钟能产生上万条消息充满控制台或使日志文件溢出,若控制台为串口超级终端,过高的消日志文件溢出,若控制台为串口超级终端,过高的消息输出速度会使系统变慢,甚至无法正常响应。息输出速度会使系统变慢,甚至无法正常响应。内核提供了控制打印速度的函数,原型为:内核提供了控制打印速度的函数,原型为: int printk_ratelimit(void);可通过函数的返回值来控制打印输出,若函数返回一可通过函数的返回值来控制打印输出,若函数返回一个非零值,继续输出消息,否则跳过,例:个非零值
15、,继续输出消息,否则跳过,例: if (printk_ratelimit( ) printk(KERN_NOTICE The printer is still on firen);15通过打印调试通过打印调试printk_ratelimit跟踪发送到控制台的消息数量,若超跟踪发送到控制台的消息数量,若超过某一阈值,则返回过某一阈值,则返回0,否则非,否则非0可通过修改下列文件控制可通过修改下列文件控制printk_ratelimit的行为的行为 /proc/sys/kernel/printk_ratelimit /proc/sys/kernel/printk_ratelimit_burst16
16、通过打印调试通过打印调试l打印设备编号打印设备编号当从一个驱动程序打印消息时,有时会希望打印关联当从一个驱动程序打印消息时,有时会希望打印关联的设备编号,内核提供了两个宏:的设备编号,内核提供了两个宏:int print_dev_t(char *buffer, dev_t dev);char *format_dev_t(char *buffer, dev_t dev);17第第4 章章 调试技术调试技术内核中的调试支持内核中的调试支持 通过打印调试通过打印调试 通过查询调试通过查询调试 通过监视调试通过监视调试 调试系统故障调试系统故障 调试器和相关工具调试器和相关工具18通过查询调试通过查询
17、调试l通过打印调试的缺点通过打印调试的缺点syslogd会一直保持对其输出文件的同步刷新,降低性会一直保持对其输出文件的同步刷新,降低性能。虽可通过在能。虽可通过在/etc/syslog.conf中日志文件的名字前中日志文件的名字前加一减号前缀来避免,但调试完后配置会被保留下来加一减号前缀来避免,但调试完后配置会被保留下来虽可通过降低虽可通过降低console_loglevel来减少控制台输出,但来减少控制台输出,但大量大量printk的使用也降低性能的使用也降低性能l大多数情况下,获取相关信息的最好方法是在需要的大多数情况下,获取相关信息的最好方法是在需要的时候才去查询,而不是持续不断地产生
18、数据,如时候才去查询,而不是持续不断地产生数据,如ps、netstat、vmstat等等l驱动程序开发中可用的查询方法有驱动程序开发中可用的查询方法有在在/proc中创建文件、中创建文件、ioctl方法、方法、sysfs等等19通过查询调试通过查询调试1. /proc文件系统文件系统/proc文件系统是一种读时创建的文件系统是一种读时创建的“内存文件系统内存文件系统”。每个文件都绑定一个内核函数,当文件被读取时,该每个文件都绑定一个内核函数,当文件被读取时,该函数动态地生成文件的函数动态地生成文件的“内容内容”内核使用内核使用/proc文件系统向外界导出信息,如文件系统向外界导出信息,如/pr
19、oc/modules因此可通过因此可通过/proc文件系统将设备信息导出到用户空间文件系统将设备信息导出到用户空间,以方便对设备驱动程序进行调试,以方便对设备驱动程序进行调试l在在/proc中实现文件系统需做两件事中实现文件系统需做两件事创建一个函数,用于在文件被读取时生成文件数据创建一个函数,用于在文件被读取时生成文件数据创建文件节点创建文件节点20通过查询调试通过查询调试l创建函数创建函数当一个进程读取当一个进程读取 /proc 文件时文件时, 内核会分配一页内存,内核会分配一页内存,我们可以把设备数据写入其中,以返回给用户空间。我们可以把设备数据写入其中,以返回给用户空间。 那个将数据写
20、入这个缓存区的函数那个将数据写入这个缓存区的函数, 就是我们要创建的就是我们要创建的函数,称为函数,称为 read_proc 方法,函数原型如下:方法,函数原型如下:int (*read_proc)(char *page, char *start, off_t offset, int count, int *eof, void *data); page:指向用来写入数据的缓冲区,大小为一页指向用来写入数据的缓冲区,大小为一页 start:指向数据写入页中的具体位置指向数据写入页中的具体位置 offset:相对于页边界的偏移量相对于页边界的偏移量 count:写入的字节数写入的字节数 eof:指
21、向一个整型数的指针,可返回一个整型数指向一个整型数的指针,可返回一个整型数 data:提供给驱动程序的专用数据指针:提供给驱动程序的专用数据指针21通过查询调试通过查询调试scull中的实现中的实现int scull_read_procmem(char *buf, char *start, off_t offset, int count, int *eof, void *data) int i, j, len = 0; int limit = count - 80; for (i = 0; i scull_nr_devs & len data; if (down_interruptib
22、le(&d-sem) return -ERESTARTSYS; len += sprintf(buf+len,nDevice %i: qset %i, q %i, sz %lin, i, d-qset, d-quantum, d-size); for (; qs & len next) len += sprintf(buf + len, item at %p, qset at %pn, qs, qs-data); if (qs-data & !qs-next) for (j = 0; j qset; j+) if (qs-dataj) len += sprintf(bu
23、f + len, % 4i: %8pn, j, qs-dataj); up(&scull_devicesi.sem); *eof = 1; return len;22通过查询调试通过查询调试l创建创建/proc文件节点文件节点原型原型struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data); name:要创建的文件名要创建的文件名 mode:该文件的保护掩码,该文
24、件的保护掩码,0为系统默认值(全局可读)为系统默认值(全局可读) base:指定该文件所在的目录,若为:指定该文件所在的目录,若为NULL,则在,则在/proc的根目录的根目录 read_proc:实现该文件的:实现该文件的read_proc方法方法Scull中的实现中的实现create_proc_read_entry(scullmem, 0 , NULL ,scull_read_procmem,NULL );节点删除节点删除remove_proc_entry(scullmem, NULL /* parent dir */);23通过查询调试通过查询调试2. seq_file接口接口在在/pr
25、oc中创建文件的另一种方式。在中创建文件的另一种方式。在/proc下创建的下创建的文件大小通常不超过一页,文件大小通常不超过一页,seq_file接口不受此限制接口不受此限制,且相当灵活。,且相当灵活。seq_file接口假定我们正在创建的虚拟文件要顺序遍接口假定我们正在创建的虚拟文件要顺序遍历一个项目序列,而这些项目正是必须要返回给用户历一个项目序列,而这些项目正是必须要返回给用户空间的空间的为使用为使用seq_file,我们必须创建一个简单的,我们必须创建一个简单的“迭代器迭代器(iterator)”对象,该对象用来表示项目系列中的位对象,该对象用来表示项目系列中的位置,每前进一步,该对象
26、输出序列中的一个项目置,每前进一步,该对象输出序列中的一个项目迭代器对象分别为迭代器对象分别为start、next、stop和和show24通过查询调试通过查询调试Start方法首先被调用方法首先被调用,原型如下:原型如下:void *start(struct seq_file *sfile, loff_t *pos); sfile通常被忽略通常被忽略 pos表示读取位置,为指向下一个项目的指针。表示读取位置,为指向下一个项目的指针。scull 驱驱动将每个设备作为系列中的一项动将每个设备作为系列中的一项, 因此因此pos 可作为可作为 scull_device 数组的索引。于是数组的索引。于
27、是, scull 使用的使用的 start 方方法如下:法如下: static void *scull_seq_start(struct seq_file *s, loff_t *pos) if (*pos = scull_nr_devs) return NULL; return scull_devices + *pos;25通过查询调试通过查询调试next 函数应当将函数应当将iterator移动到下一个位置移动到下一个位置, 如果序如果序列里什么都没有剩下就返回列里什么都没有剩下就返回 NULL,这个方法的原,这个方法的原型是型是:void *next(struct seq_file *s
28、file, void *v, loff_t *pos); v 是先前对是先前对 start 或者或者 next 调用所返回的调用所返回的 iterator, pos 是文件的当前位置。是文件的当前位置。next 应当递增应当递增 pos 指向的值,指向的值, scull 的的next方法实现如下方法实现如下: static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos) (*pos)+; if (*pos = scull_nr_devs) return NULL; return scull_devices + *po
29、s;26通过查询调试通过查询调试当内核处理完当内核处理完 iterator之后之后, 会调用会调用 stop方法通知我们方法通知我们进行清除工作,进行清除工作,stop函数原型如下:函数原型如下:void stop(struct seq_file *sfile, void *v); scull 实现没有清理工作要做实现没有清理工作要做, 所以它的所以它的 stop 方法是空方法是空的的.在上述调用之间在上述调用之间, 内核会调用内核会调用 show 方法来将实际数据方法来将实际数据输出到用户空间输出到用户空间. 这个方法的原型是这个方法的原型是:int show(struct seq_file
30、 *sfile, void *v); 这个方法为这个方法为 iterator v 指向的项目建立输出。但是,它指向的项目建立输出。但是,它不能使用不能使用 printk, 应该使用针对应该使用针对seq_file的一套特殊函数的一套特殊函数27通过查询调试通过查询调试int seq_printf(struct seq_file *sfile, const char *fmt, .);这是这是 seq_file 实现的实现的 printf等价等价 函数函数; 它采用常用的它采用常用的格式串和附加值参数格式串和附加值参数. 你必须将给你必须将给 show 函数的函数的 set_file 结构传递给
31、它结构传递给它Scull中的中的show方法实现如下:方法实现如下:28通过查询调试通过查询调试static int scull_seq_show(struct seq_file *s, void *v) struct scull_dev *dev = (struct scull_dev *) v; struct scull_qset *d; int i; if (down_interruptible(&dev-sem) return -ERESTARTSYS; seq_printf(s, nDevice %i: qset %i, q %i, sz %lin, (int) (dev -
32、 scull_devices), dev-qset, dev-quantum, dev-size); for (d = dev-data; d; d = d-next) seq_printf(s, item at %p, qset at %pn, d, d-data); if (d-data & !d-next) for (i = 0; i qset; i+) if (d-datai) seq_printf(s, % 4i: %8pn, i, d-datai); up(&dev-sem); return 0;29通过查询调试通过查询调试l上面已经定义了完整的迭代器操作函数,上面
33、已经定义了完整的迭代器操作函数,scull必须必须将这些函数打包并和将这些函数打包并和/proc中的某个文件连接起来,中的某个文件连接起来,首先要填冲一个首先要填冲一个seq_operations结构体:结构体:static struct seq_operations scull_seq_ops = .start = scull_seq_start, .next = scull_seq_next, .stop = scull_seq_stop, .show = scull_seq_show;30通过查询调试通过查询调试l首先创建文件的首先创建文件的open方法,该方法将文件连接到方法,该方法将
34、文件连接到seq_file操作操作static int scull_proc_open(struct inode *inode, struct file *fil return seq_open(file, &scull_seq_ops);31通过查询调试通过查询调试l对对seq_open的调用将的调用将file结构和我们上面定义的序结构和我们上面定义的序列操作连接在一起。事实上列操作连接在一起。事实上, open 是我们必须自己是我们必须自己实现的唯一文件操作实现的唯一文件操作, 因此我们现在可以建立我们的因此我们现在可以建立我们的 file_operations 结构结构:stat
35、ic struct file_operations scull_proc_ops = .owner = THIS_MODULE, .open = scull_proc_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release;32通过查询调试通过查询调试l建立建立/proc 文件节点文件节点函数原型函数原型struct proc_dir_entry *create_proc_entry(const char *name,mode_t mode, struct proc_dir_entry *parent); name
36、:要创建的文件名:要创建的文件名 mode:该文件的保护掩码:该文件的保护掩码 parent:父目录:父目录Scull中的实现中的实现entry = create_proc_entry(scullseq, 0, NULL);if (entry) entry-proc_fops = &scull_proc_ops;33通过查询调试通过查询调试lioctl是一个作用于文件描述符上的系统调用是一个作用于文件描述符上的系统调用, 它接收它接收一个命令号以及一个命令号以及(可选地可选地)另一个参数另一个参数, 常常是一个指常常是一个指针。针。 作为作为/proc 文件系统的替代文件系统的替代,
37、你可以实现几个用你可以实现几个用来调试用的来调试用的 ioctl 命令,以从驱动程序拷贝相关的数命令,以从驱动程序拷贝相关的数据到用户空间据到用户空间, 在这里你可以检查它们。在这里你可以检查它们。34第第4 章章 调试技术调试技术内核中的调试支持内核中的调试支持 通过打印调试通过打印调试 通过查询调试通过查询调试 通过监视调试通过监视调试 调试系统故障调试系统故障 调试器和相关工具调试器和相关工具35通过监视调试通过监视调试l通过监视用户空间中应用程序的运行情况,可以捕捉到一些通过监视用户空间中应用程序的运行情况,可以捕捉到一些问题。有几个方法可以用来监视用户空间程序运行,问题。有几个方法可
38、以用来监视用户空间程序运行, 你可以你可以运行一个调试器来单步跟踪它的函数运行一个调试器来单步跟踪它的函数, 插入打印语句或者在插入打印语句或者在 strace 下运行程序等等。本节主要讨论下运行程序等等。本节主要讨论strace技术。技术。lstrace命令功能强,可以显示由用户空间程序所发出的所有命令功能强,可以显示由用户空间程序所发出的所有系统调用,包括调用参数及用符号表示的返回值系统调用,包括调用参数及用符号表示的返回值lstrace 有很多命令行选项:有很多命令行选项:-t :显示每个调用发生的时间:显示每个调用发生的时间-T:显示调用所花费的时间:显示调用所花费的时间 -e:限定被
39、跟踪的调用类型:限定被跟踪的调用类型-o:重定向输出到一个文件中,:重定向输出到一个文件中, 缺省情况下输出到缺省情况下输出到 stderr.lstrace 从内核获取信息,这意味着它可以跟踪一个不带有调从内核获取信息,这意味着它可以跟踪一个不带有调试支持试支持 (对对 gcc是是 -g 选项选项)以及去掉了符号信息的程序。以及去掉了符号信息的程序。36通过监视调试通过监视调试l例例 strace ls /dev /dev/scull0open(/dev, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 3fstat64(3, st_mode=S_
40、IFDIR|0755, st_size=24576, .) = 0fcntl64(3, F_SETFD, FD_CLOEXEC) = 0getdents64(3, /* 141 entries */, 4096) = 4088.getdents64(3, /* 0 entries */, 4096) = 0close(3) = 0.fstat64(1, st_mode=S_IFCHR|0664, st_rdev=makedev(254, 0), .) = 0write(1, MAKEDEVnadmmidi0nadmmidi1nadmmid., 4096) = 4000write(1, bnpt
41、ywcnptywdnptywenptywfnptyx0n., 96) = 96write(1, bnptyxcnptyxdnptyxenptyxfnptyy0n., 4096) = 3904write(1, s17nvcs18nvcs19nvcs2nvcs20nvcs21., 192) = 192write(1, nvcs47nvcs48nvcs49nvcs5nvcs50nvc., 673) = 673close(1) = 0exit_group(0) = ?37通过监视调试通过监视调试l例例 用用wc命令读命令读scull设备设备.open(/dev/scull0, O_RDONLY|O_L
42、ARGEFILE) = 3fstat64(3, st_mode=S_IFCHR|0664, st_rdev=makedev(254, 0), .) = 0read(3, MAKEDEVnadmmidi0nadmmidi1nadmmid., 16384) = 4000read(3, bnptywcnptywdnptywenptywfnptyx0n., 16384) = 4000read(3, s17nvcs18nvcs19nvcs2nvcs20nvcs21., 16384) = 865read(3, , 16384) = 0fstat64(1, st_mode=S_IFCHR|0620, st_
43、rdev=makedev(136, 1), .) = 0write(1, 8865 /dev/scull0n, 17) = 17close(3) = 0exit_group(0) = ?38第第4 章章 调试技术调试技术内核中的调试支持内核中的调试支持 通过打印调试通过打印调试 通过查询调试通过查询调试 通过监视调试通过监视调试 调试系统故障调试系统故障 调试器和相关工具调试器和相关工具39调试系统故障调试系统故障1. oops消息消息oops是内核发生错误时(比如引用了一个空指针)所是内核发生错误时(比如引用了一个空指针)所展现给我们的一系列信息,这些信息包含了当时展现给我们的一系列信息,这
44、些信息包含了当时CPU的状态、进程的状态、内核堆栈的状态以及调用栈的的状态、进程的状态、内核堆栈的状态以及调用栈的记录等记录等大部分系统故障都是引用大部分系统故障都是引用 NULL 指针或者使用其他不指针或者使用其他不正确指针值引起的,这类故障通常会导致一个正确指针值引起的,这类故障通常会导致一个 oops 消息消息.40调试系统故障调试系统故障l例例 faulty模块中的模块中的write调用由于引用空指针导致的调用由于引用空指针导致的oopsssize_t faulty_write (struct file *filp, const char _user *buf, size_t coun
45、t, loff_t *pos) *(int *)0 = 0; /*make a simple fault by dereferencing a NULL pointer */ return 0;41调试系统故障调试系统故障Unable to handle kernel NULL pointer dereference at virtual address 00000000 printing eip:d083a064Oops: 0002 #1SMPCPU: 0EIP: 0060: Not taintedEFLAGS: 00010246 (2.6.6)EIP is at faulty_write+
46、0 x4/0 x10 faultyeax: 00000000 ebx: 00000000 ecx: 00000000 edx: 00000000esi: cf8b2460 edi: cf8b2480 ebp: 00000005 esp: c31c5f74ds: 007b es: 007b ss: 0068Process bash (pid: 2086, threadinfo=c31c4000 task=cfa0a6c0)Stack: c0150558 cf8b2460 080e9408 00000005 cf8b2480 00000000 cf8b2460 cf8b2460 fffffff7
47、080e9408 c31c4000 c0150682 cf8b2460 080e9408 00000005 cf8b2480 00000000 00000001 00000005 c0103f8f 00000001 080e9408 00000005 00000005Call Trace: vfs_write+0 xb8/0 x130 sys_write+0 x42/0 x70 syscall_call+0 x7/0 xbCode: 89 15 00 00 00 00 c3 90 8d 74 26 00 83 ec 0c b8 00 a6 83 d0说明引发内核错误的原因说明内核错误的事发现场
48、说明错误发生时函数调用链42调试系统故障调试系统故障l例例 faulty模块中的模块中的read 调用由于缓冲区溢出引发的调用由于缓冲区溢出引发的oopsssize_t faulty_read(struct file *filp, char _user *buf, size_t count, loff_t *pos) int ret; char stack_buf4; /* Lets try a buffer overflow */ memset(stack_buf, 0 xff, 20); if (count 4) count = 4; /* copy 4 bytes to the user
49、 */ ret = copy_to_user(buf, stack_buf, count); if (!ret) return count; return ret;43调试系统故障调试系统故障EIP: 0010:Unable to handle kernel paging request at virtual address ffffffff printing eip:ffffffffOops: 0000 #5SMPCPU: 0EIP: 0060: Not taintedEFLAGS: 00010296 (2.6.6)EIP is at 0 xffffffffeax: 0000000c ebx
50、: ffffffff ecx: 00000000 edx: bfffda7cesi: cf434f00 edi: ffffffff ebp: 00002000 esp: c27fff78ds: 007b es: 007b ss: 0068Process head (pid: 2331, threadinfo=c27fe000 task=c3226150)Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7 bfffda70 c27fe000 c0150612 cf434f00 bfffda7
51、0 00002000 cf434f20 00000000 00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70Call Trace: sys_read+0 x42/0 x70 syscall_call+0 x7/0 xbCode: Bad EIP value.44调试系统故障调试系统故障2. 系统挂起系统挂起内核代码中的大多数错误通常只会导致一个内核代码中的大多数错误通常只会导致一个oops消息,但有时消息,但有时也会将系统挂起,系统真挂起时不会再响应任何动作,包括也会将系统挂起,系统真挂起时不会再响应任
52、何动作,包括Ctrl-Alt-Del 组合键组合键但有时系统不是真的挂起,只是某种原因使键盘被锁住,此时可但有时系统不是真的挂起,只是某种原因使键盘被锁住,此时可用用“SySRq魔法键(魔法键(magic SysRq key)”工具进行调试工具进行调试“ SySRq魔法键魔法键” 在大部分体系上都可用。在在大部分体系上都可用。在 PC 上,可通过上,可通过Alt 和和SysRq 键组合来激活,在别的平台上可使用其他特殊键键组合来激活,在别的平台上可使用其他特殊键(详见详见 documentation/sysrq.txt)来激活来激活, 在串口控制台上也可用在串口控制台上也可用,由第三个键来决定
53、其动作中,由第三个键来决定其动作中: r :关闭键盘原始模式。用于当某个崩溃的应用程序:关闭键盘原始模式。用于当某个崩溃的应用程序( 例如例如 X 服务服务器器 )让键盘处于一个奇怪状态。让键盘处于一个奇怪状态。 k :调用:调用“安全注意键安全注意键”( SAK ) 功能功能. SAK 将杀掉在当前控制台将杀掉在当前控制台上的所有运行的进程上的所有运行的进程, 给你一个干净的终端给你一个干净的终端. S:对全部磁盘进行紧急同步:对全部磁盘进行紧急同步.45调试系统故障调试系统故障 u:umount, 尝试以只读模式重新加载所有磁盘。尝试以只读模式重新加载所有磁盘。 这个操作常常在这个操作常常
54、在 s 之后马上调用之后马上调用, 它可以在系统处于严重故障时节省很多检查文件它可以在系统处于严重故障时节省很多检查文件系统的时间。系统的时间。 b:boot,立刻重启系统。,立刻重启系统。 确认要先同步和重新加载磁盘。确认要先同步和重新加载磁盘。 p :打印处理器寄存器消息。:打印处理器寄存器消息。 t: 打印当前任务列表。打印当前任务列表。 m :打印内存信息。:打印内存信息。魔法键魔法键SysRq 必须在内核配置中显式使能必须在内核配置中显式使能, 大部分的发布版没有大部分的发布版没有使能它使能它, 因为明显的安全理由因为明显的安全理由. 对于用来开发驱动的系统对于用来开发驱动的系统,
55、使能魔使能魔法键法键SysRq是值得的。是值得的。魔法键魔法键 sysrq 能在运行时关闭能在运行时关闭, 使用如下命令使用如下命令:echo 0 /proc/sys/kernel/sysrq 46第第4 章章 调试技术调试技术内核中的调试支持内核中的调试支持 通过打印调试通过打印调试 通过查询调试通过查询调试 通过监视调试通过监视调试 调试系统故障调试系统故障 调试器和相关工具调试器和相关工具47调试器和相关工具调试器和相关工具lgdb调试内核调试内核gdb /usr/src/linux/vmlinux /proc/kcore Vmlinux:一参,指定待调试的文件为非压缩内核映像文件一参,
56、指定待调试的文件为非压缩内核映像文件 /proc/kcore:二参,指定内核在内存中的核心映像,对于一个运:二参,指定内核在内存中的核心映像,对于一个运行的内核,核心映像就是运行时的内核映像行的内核,核心映像就是运行时的内核映像gdb可以打印结构体中存放的信息、跟踪一个指针、输出一个变可以打印结构体中存放的信息、跟踪一个指针、输出一个变量的值、反汇编一个函数等量的值、反汇编一个函数等 p jiffies disassemble function打印数据时,内核仍在运行,各种数据在不同时间可能会有不同打印数据时,内核仍在运行,各种数据在不同时间可能会有不同的值,但的值,但gdb将第一次读取的数据
57、放在缓存中,第二次读取时得将第一次读取的数据放在缓存中,第二次读取时得到的仍是原数据到的仍是原数据执行命令执行命令core-file /proc/kcore刷新缓冲区后再读刷新缓冲区后再读48调试器和相关工具调试器和相关工具lgdb调试可加载模块调试可加载模块上面的命令只可调试内核,但对于可加载模块却上面的命令只可调试内核,但对于可加载模块却无能为力,因为可加载模块并没有随无能为力,因为可加载模块并没有随vmlinux传递传递给给gdb,所以,所以gdb 对它一无所知对它一无所知 可加载模块是可加载模块是 ELF 格式的可执行映象格式的可执行映象, 它们被分它们被分成几个代码段成几个代码段.
58、一个典型的模块可能包含一打或更一个典型的模块可能包含一打或更多段多段, 以下以下 3 个段与调试相关个段与调试相关: .text:这个段包含了模块的可执行代码:这个段包含了模块的可执行代码. .bss /.data :这:这 2 个段包含了模块的变量个段包含了模块的变量. 未初始化的未初始化的变量在变量在 .bss 中中, 已初始化的在已初始化的在 .data 里里.要想使要想使 gdb 能够处理可加载模块,必须告诉它模能够处理可加载模块,必须告诉它模块的段加载位置。块的段加载位置。49调试器和相关工具调试器和相关工具每个模块在加载后都会在每个模块在加载后都会在 /sys/module 目录下
59、产生一目录下产生一个对应的目录,在该目录下又有一个个对应的目录,在该目录下又有一个sections目录,目录,sections下有下有.text、.bss、.data三个文件,文件内容三个文件,文件内容就是就是3个段的基地址。个段的基地址。 例如例如, 在加载在加载 scull 模块后模块后, /sys/module/scull/sections 会包含一个名为会包含一个名为 .text 的的文件,文件的内容就是代码段的基地址文件,文件的内容就是代码段的基地址.通过通过sysfs文件获取模块的段地址后,就可以使用如下文件获取模块的段地址后,就可以使用如下命令进行调试命令进行调试(gdb) ad
60、d-symbol-file ./scull.ko 0 xd0832000 -s .bss 0 xd0837100 -s .data 0 xd0836be050调试器和相关工具调试器和相关工具例例 scull调试调试(gdb) add-symbol-file scull.ko 0 xd0832000 -s .bss 0 xd0837100 -s .data 0 xd0836be0add symbol table from file scull.ko at .text_addr = 0 xd0832000 .bss_addr = 0 xd0837100 .data_addr = 0 xd0836be0(y or n) yReading symbols from scu
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年消防执业资格考试题库(消防应急救援装备)消防器材维护保养试题
- 2025年古筝演奏技能考核试卷:古筝音色处理与音准控制试题
- 2025年滑雪教练学员滑雪教学实施论文试卷
- 2025年高压电工考试题库:高压电力系统运行优化设备故障排除试题
- 2025年广告设计师专业知识考核试卷:平面设计软件操作与应用试题
- 高压电工考试题库2025年:事故现场急救知识与技能试题
- 2025年育婴师职业技能测评试卷:亲子互动与家庭育儿指导试题
- 2025年护士执业资格考试题库:急危重症护理学专项护理急救设备操作试题
- 2025年小学英语毕业考试模拟卷:英语跨文化交际案例分析与应用
- 2025年建筑工地安全防护措施验收与评价试题库试卷
- PTIO和ABTS自由基清除实验操作指南-李熙灿-曾婧媛
- PCI患者的术后护理课件
- 2024年供应链可持续性培训资料
- 丁丽娟《数值计算方法》五章课后实验题答案(源程序很详细-且运行无误)
- WS-T 10001-2023 疾病预防控制机构实验室仪器设备配置和管理
- 成人住院患者跌倒评估与预防(团体标准)解读
- 通止规设计公差自动计算表
- 静设备安装质量控制过程
- 桥梁亮化施工流程图
- 深基坑巡视记录
- 现代侦察监视技术讲课教案
评论
0/150
提交评论