《Linux Device Drivers》第四章 调试技术——note

1.本章知识点

  • 内核中和调试相关的选项

    • CONFIG_DEBUG_KERNEL
    • CONFIG_DEBUG_SLAB
    • CONFIG_DEBUG_PAGEALLOC
    • CONFIG_DEBUG_SPINLOCK
    • CONFIG_DEBUG_SPINLOCK_SLEEP
    • CONFIG_INIT_DEBUG
    • CONFIG_DEBUG_INFO
    • CONFIG_MAGIC_SYSRQ
    • CONFIG_DEBUG_STACKOVERFLOW
    • CONFIG_DEBUG_STACK_USAGE
    • CONFIG_KALLSYMS
    • CONFIG_IKCONFIG
    • CONFIG_IKCONFIG_PROC
    • CONFIG_ACPI_DEBUG
    • CONFIG_DEBUG_DRIVER
    • CONFIG_SCSI_CONSTANTS
    • CONFIG_INPUT_EVBUG
    • CONFIG_PROFILING
  • 通过打印调试

    • printk

      • 通过附加不同日志级别,可让printk对消息进行分类
      • <linux/kernel.h>
        • KERN_EMERG
        • KERN_ALERT
        • KERN_CRIT
        • KERN_ERR
        • KERN_WARNING
        • KERN_NOTICE
        • KERN_INFO
        • KERN_DEBUG
      • 默认级别是DEFAULT_MESSAGE_LOGLEVEL
        • 2.6.10内核中,默认级别是KERN_WARNING
      • /proc/sys/kernel/printk
        • 4个整数值
        • 当前的日志级别、未明确指定日志级别时的默认消息级别、最小允许的日志级别以及引导时的默认日志级别
    • 重定向控制台消息
    • 消息如何被记录
      • printk函数将消息写到一个长度为__LOG_BUF_LEN字节的循环缓冲区中
      • 唤醒正在等待消息的进程,或者正在读取/proc/kmsg的进程
      • 可以在任何地方调用printk,甚至在中断处理函数里也可以调用,而且对数据量的大小没有限制
      • klogd
      • syslogd
      • /etc/syslog.conf
    • 开启及关闭消息
      • 定义一个宏,在需要时,这个宏展开为一个printk(printf)调用

        • 可以通过在宏名字中删除或增加一个字母来启用或禁用每一条打印语句
        • 在编译前修改CFLAGS变量,则可以一次禁用所有消息
        • 同样的打印语句可以在内核代码中也可以在用户级代码使用
    • 速度限制
      • /proc/sys/kernel/printk_ratelimit
      • /proc/sys/kernel/printk_ratelimit_burst
    • 打印设备编号
      • <linux/kdev_t.h>

        • int print_dev_t(char *buffer, dev_t dev);
        • char *format_dev_t(char *buffer, dev_t dev);
  • 通过查询调试
    • syslogd

      • 试图把每件事情都记录到磁盘上,以在系统万一崩溃时最后的记录信息能反应崩溃前的状况
    • 可以使用如下方法对系统进行查询
      • 在/proc文件系统中创建文件、使用驱动程序的ioctl方法,以及通过sysfs导出属性等
    • 使用/proc文件系统
      • /proc文件系统是一种特殊的、由软件创建的文件系统,内核使用它向外界导出信息
      • /proc下面的每个文件都绑定于一个内核函数,用户其中的文件时,该函数动态地生成文件的“内容”
      • 在/proc中实现文件
        • <linux/proc_fs.h>
        • 实现一个函数创建一个只读的/proc文件
          • int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);
          • 当没有数据可返回时,必须设置eof这个参数
          • 返回少量数据的简单read_proc方法可忽略start参数,复杂的read_proc方法会将*start设置为页面,并将所请求偏移量处的数据放到内存页中
          • seq_file
      • 创建自己的/proc文件
        • 需要经与一个/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);
        • remove_proc_entry(const char *name, struct proc_dir_entry *base);
        • 有个经常约定,要求把设备驱动程序对应的/proc入口项转移到子目录driver/中
        • 在卸载模块时,/proc中的入口项也应被删除
        • 不鼓励使用/proc文件
    • seq_file接口
      • 创建一个简单的迭代器对象
      • <linux/seq_file.h>
      • 建立四个迭代器对象:start、next、stop、show
        • void *start(struct seq_file *sfile, loff_t *pos);
        • void *next(struct seq_file *sfile, void *v, loff_t *pos);
        • void stop(struct seq_file *sfile, void *v);
        • int show(struct seq_file *sfile, void *v);
          • int seq_printf(struct seqfile *sfile, const char *fmt, …);
          • int seq_putc(struct seqfile *sfile, char c);
          • int seq_puts(struct seqfile *sfile, const char *s);
          • int seq_escape(struct seqfile *m, const char *s, const char *esc);
          • int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry *dentry, char *esc);
      • static struct seq_operations seq_ops ={.start=start, .next=next, .stop=stop, .show=show};
      • static int proc_open(struct inode *inode, struct file *file){return seq_open(file, &seq_ops);}
      • static struct file_operations proc_ops = {.owner=THIS_MODULE, .open=proc_open, .read=seq_read, .llseek=seq_lseek, .release=seq_release};
      • entry = create_proc_entry(“seq”, 0, NULL);
      • entry->proc_fops = &proc_ops;
      • struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);
    • ioctl方法
      • 接收一个“命令”号以及另一个(可选的)参数
  • 通过监视调试
    • strace命令是一个功能非常强大的工具,它可以显示由用户空间程序所发出的所有系统调用

      • -t: 用来显示调用发生的时间
      • -T: 显示调用所花费的时间
      • -e: 限定被跟踪的调用类型
      • -o: 将输出重定向到一个文件中
      • 默认情况下,strace将跟踪信息打印到stderr上
      • strace也可以跟踪一个正在运行的进程
  • 调试系统故障
    • oops消息

      • 因为对NULL指针取值或使用了其他不正确的指针值
    • 系统挂起
      • SysRq魔法键

        • ALT+RsyRq
        • /proc/sys/kernel/sysrq
        • /proc/sysrq-trigger
  • 调试器和相关工具
    • 使用gdb

      • gdb /usr/src/linux/vmlinux /proc/kcore

        • 第一个参数是未经压缩的内核ELF可执行文件的名字
        • 第二个参数是core文件的名字
      • 为了让gdb使用内核的符号信息,我们必须在打开CONFIG_DEBUG_INFO选项的情况下编译内核
      • 对于调试会话来说,模块相关的代码段只有下面三个
        • .text: 包含了模块的可执行代码
        • .bss, .data: 这两个代码段保存模块的变量,编译时未初始化的变量保存在.bss段,经过初始化的保存在.data段
      • 代码段的地址:/sys/module/<module name>/sections/.*
      • (gdb) add-symbol-file hello.ko 0xe0a27000 -s .bss 0xe0a28d00 -s .data 0xe0a279e0
    • kdb内核调试器
      • 打补丁,oss.sgi.com下载
      • 在控制台上按下Pause(或Bread)键将启动调试
    • kgdb补丁
      • 将运行调试内核的系统和运行调试器的系统隔离开来而工具,而这两个系统之间通过串吕线连接
      • 两个独立的补丁
        • 第一个kgdb补丁可以-mm内核树中找到
        • 还可以使用http://kgdb.sf.net/上的补丁
    • 用户模式的Linux虚拟机
    • Linux跟踪工具包
      • LLT(Linux Trace Toolkit)是一个内核补丁,包含了一组可以用于内核事件跟踪的相关工具集
      • http://www.opersys.com/LTT
    • 动态探测
      • DProbes(Dynamic Probes)是IBM为基于IA-32架构的Linux发布的一种调试工具
      • http://dprobes.sourceforge.net/

2.常用反汇编技术

2.1oops错误定位

(1)首先在编译生成内核的时候同时生成了一个vmlinux,使用gdb。

在内核配置时,make menuconfig 要打开complie with debug info选项。

注意这行: PC is at skb_release_data+0x74/0xc4

这告诉我们,skb_release_data函数有0xc4这么大,而Oops发生在0x74处。 那么我们先看一下skb_release_data从哪里开始:

#grep skb_release_data ./System.map

c0282af4 t skb_release_data

于是我们知道在系统出现错误时程序指针在 c0282af4+0x74=c0282b68

(2)然后用gdb查看,gdb ./vmlinux (在linux目录下执行),进入调试模式。

gdb$ b *0xc0282b68

Breakpoint 1 at 0xc0282b68: file net/core/skbuff.c ,line312

这就是告诉我们在哪个文件,在哪一行。如此知道了错误的位置,具体的原因带解决。

(2)反汇编

gdb$ disassemble 0xc0282b68

2.2反编译某一行

先readelf -a vmlinux | grep setup_arch得到相关信息:

12707: c0351020 543 FUNC GLOBAL DEFAULT 9 setup_arch

其中,c0351020 为起始地址,543为长度(10进制),

然后使用objdump -d --start-address=0xc0351020 --stop-address=0xc035123f vmlinux

2.3反汇编某个文件

在kernel目录下执行命令

$make help | grep lst

会有

dir/file.lst    - Build specified mixed source/assembly target only

(requires a recent binutils and recent build (System.map))

即make xxx.lst 即可反汇编某个文件

时间: 2024-11-07 11:38:46

《Linux Device Drivers》第四章 调试技术——note的相关文章

《Linux Device Drivers》 第九章 与硬件通信

概述,本章正式接触真正的硬件. 将介绍对设备提供I/O访问的方法和函数,用于从设备读取数据或将数据写入到设备 并详细说明I/O端口和I/O内存 I/O端口和I/O内存 每种外设都通过读写寄存器进行控制 在硬件层,内存区域和I/O区域没有概念上的区别:它们都通过向地址总线和控制总线发送电平信号进行访问,再通过数据总线读写数据 I/O寄存器和常规内存 I/O寄存器和RAM的最主要区别就是I/O操作具有边际效应,而内存操作则没有 内存写操作的唯一结果就是在指定位置存储一个数值 内存读操作则仅仅返回指定

《Linux Device Drivers》第八章 分配内存——note

本章主要介绍Linux内核的内存管理. kmalloc函数的内幕 不正确所获取的内存空间清零 分配的区域在物理内存中也是连续的 flags參数 <linux/slab.h> <linux/gfp.h> GFP_KERNEL 在空暇内存较少时把当前进程转入休眠以等待一个页面 分配内存的函数必须是可重入的 GFP_ATOMIC 用于在中断处理例程或其它执行于进程上下文之外的代码中分配内存,不会休眠 GFP_USER 用于为用户空间页分配内存.可能会休眠 GFP_HIGHUSER 类似于

linux device drivers - debugging之proc

在书籍"linux device drivers"的第四章,专门介绍驱动开发中的debugging技术. printk只是其中一种技术,这种技术要求printk打印消息,并且会写入到磁盘里的文件中,这会拖慢整个代码的执行速度. 还有其中的debugging技术,并且对代码执行速度的影响比prink小. 其中讲到了,我们可以让驱动程序在proc文件系统里面创建文件,并且将需要的debugging信息写入到这个文件里面. linux内核提供了相应的API,来和proc文件系统交互,例如创建

第四章--调试器及相关工具入门

在写这章之前,我看了一下看雪以往的教程.本来想参考一下,可忽然发现,写这样的一章,是一件非常愚蠢的事情,因为我觉的关于这些工具的使用教程.看雪教程中已经写的够详细的了,我并不认为你会看不懂.所以我不想做浪费时间的人,本章就此搁浅. 推荐看<Crack Tutorial 2001>,推荐看<看雪论坛精华一.二.三.四>,推荐看<加密与解密--软件保护技术及完全解决方案>,推荐看一切与之有关的教程. 本章补遗: 要想上路,你最少应该熟练掌握以下工具: SoftICE:目前公

Linux内核分析第四章 读书笔记

Linux内核分析第四章 读书笔记 第一部分--进程调度 进程调度:操作系统规定下的进程选取模式 面临问题:多任务选择问题 多任务操作系统就是能同时并发地交互执行多个进程的操作系统,在单处理器机器上这会产生多个进程在同时运行的幻觉,在多处理器机器上,这会使多个进程在不同的处理机上真正同时.并行地运行.无论在单处理器或者多处理器机器上,多任务操作系统都能使多个进程处于堵塞或者睡眠状态,也就是说,实际上不被投入执行,直到工作确实就绪. 多任务系统可以划分为两类:非抢占式多任务和抢占式多任务.Linu

《Linux Device Drivers》第十四章 Linux 设备模型

简介 2.6内核的设备模型提供一个对系统结构的一般性抽象描述,用以支持多种不同的任务 电源管理和系统关机 与用户空间通信 热插拔设备 设备类型 对象生命周期 kobject.kset和子系统 kobject是组成设备模型的基本结构 对象的引用计数 sysfs表述 数据结构关联 热插拔事件处理 kobject基础知识 <linux/kobject.h> 嵌入的kobject 内核代码很少去创建一个单独的kobject对象,kobject用于控制对大型域相关对象的访问 kobject的初始化 首先

Linux device drivers,LDD阅读笔记

第一章       惯例,泛泛而谈的第一章.但我注意到一个关键字“linux的模块”. 第二章       令人激动的hello world.这一章要开始实践了,所以先下载虚拟机和linux吧.

《Linux Device Drivers》 第十七章 网络驱动程序——note

简介 网络接口是第三类标准Linux设备,本章将描述网络接口是如何与内核其余的部分交互的 网络接口必须使用特定的内核数据结构注册自身,以备与外界进行数据线包交换时调用 对网络接口的常用文件操作是没有意义的,因此在它们身上无法体现Unix的"一切都是文件"的思想 网络驱动程序异步自外部世界的数据包 网络设备向内核请求把外部获得的数据包发送给内核 Linux内核中的网络子系统被设计成完全与协议无关 在网络世界中使用术语"octet"指一组8个的数据位,它是能为网络设备和

《Linux Device Drivers》第十二章 PCI驱动程序——note

简介 本章给出一个高层总线架构的综述 讨论重点是用于访问Peripheral Component Interconnect(PCI,外围设备互联)外设的内核函数 PCI总线是内核中得到最好支持的总线 本章主要介绍PCI驱动程序如果寻找其硬件和获得对它的访问 本章也会介绍ISA总线 PCI接口 PCI是一组完整的规范,定义了计算机的各个不同部分之间应该如何交互 PCI规范涵盖了与计算机接口相关的大部分问题 PCI架构被设计为ISA标准的替代品,有三个主要目标 获得在计算机和外设之间传输数据时更好的