debugfs

在调试linux驱动的时候,可以用debugfs来调试,debugfs类似字符设备驱动一样,甚至更简单,不需要主设备号次设备号等等,只需要实现一个file_operations,然后通过debugfs_create_file就可以在debugfs中建立一个文件结点,就像字符设备驱动那样,只需要对这个文件结点进行open就可以进行read、write、ioctl,等等操作,这些操作对应到我们在驱动里为debugfs准备的file_operations。

让内核支持DEBUGFS,使能宏CONFIG_DEBUG_FS,在内核配置中选中,一般是在Kernel hacking中.在实际的使用中,举个例子来说明,在调试GPIO驱动的时候,我们可以通过debugfs来调试:

首先定义一个file_operations:

static const struct file_operations gpiolib_operations = {
    .open = gpiolib_open,
    .read = gpiolib_read,
    .write = gpiolib_write,
    .llseek = seq_lseek,
    .release = single_release,
};

然后,建立一个debugfs文件结点:

(void)debugfs_create_file("gpio", S_IFREG | S_IRUGO, NULL, NULL, &gpiolib_operations);

在实际的驱动中,建立debugfs文件结点一般在驱动module初始化的时候。

根据我们的调试需要,实现读写操作,一般用得比较多的是read和write操作,所以在gpiolib_read和gpiolib_write里加入我们的调试代码。调用GPIO驱动的时候,我的想法是,给GPIO结点发一个读指令,那么就得传入的gpio号的状态,给GPIO结点发一个写指令,那么就根据传入的参数设置gpio的状态。于是,我只需要实现write函数:

static ssize_t gpiolib_write(struct file *file, const char __user *buf,size_t size, loff_t *ppos)  
{
    charinfo[255];  
    int port=0,value=0;  
    memset(info,0, 255);  
    copy_from_user(info,buf, size);  
    printk("gpio:%s\n",info);  
    if((info[0]>= '0') && (info[0] <= '9')){  
        port= (info[0] - 48)*10;
        if((info[1]>= '0') && (info[1] <= '9')){
            port+= (info[1] - 48);
            if(info[2]== ' '){
                if(info[3] == 'w'){
                    value = (info[4] =='0')?0:1;
                }
            }
        }
    }

    if(info[3]== 'r'){
        gpio_direction_input(port);
        printk("gpio%dstatus = %d\n", port, __gpio_get_value(port));
    } else if(info[3] == 'w'){
        printk("write%d to gpio%d\n", value, port);
        gpio_direction_output(port,value);
        __gpio_set_value(port,value);
    }
    return size;
}

这段代码的意思,根据传入的参数info作相应的操作,info的格式是:

info[0]和info[1]分别代表gpio号的十位和个位
info[2]必须为空格;
info[3]为读写性质,‘w'为写,'r'为读;
info[4]如果为写,那么它表示写的状态。


这样就可以在驱动加载之后,用shell命令echo来进行调试了。

例如gpio号为57的端口控制蜂鸣器,gpio号为37的端口连接按键,那么:

蜂鸣器发声:echo 57 w1 > gpio
蜂鸣器停止:echo 57 w0 > gpio
读取按键状态:echo 37 r > gpio

那么这个gpio文件结点在哪呢?
内核启动后会把debugfs文件系统挂载到/sys/kernel/debug目录下,我们的gpio文件结点就在这里。
如果没有找到,那么可以手动挂载mount-t debugfs none /mnt,这样就挂载到/mnt目录下了。


PS:
更为强大的调试选项: CONFIG_GPIO_SYSFS. 定义此宏后 会在/sys/class/gpio/下面到处gpio的设备文件 可以通过此设备文件对gpio进行控制与读取

------------------------------------------------------------------------------------------------------
DebugFS,顾名思义,是一种用于内核调试的虚拟文件系统,内核开发者通过debugfs和用户空间交换数据。类似的虚拟文件系统还有procfs和sysfs等,这几种虚拟文件系统都并不实际存储在硬盘上,而是Linux内核运行起来后才建立起来。通常情况下,最常用的内核调试手段是printk。但printk并不是所有情况都好用,比如打印的数据可能过多,我们真正关心的数据在大量的输出里不是那么一目了然;或者我们在调试时可能需要修改某些内核变量,这种情况下printk就无能为力,而如果为了修改某个值重新编译内核或者驱动又过于低效,此时就需要一个临时的文件系统可以把我们需要关心的数据映射到用户空间。在过去,procfs可以实现这个目的,到了2.6时代,新引入的sysfs也同样可以实现,但不论是procfs或是sysfs,用它们来实现某些debug的需求,似乎偏离了它们创建的本意。比如procfs,其目的是反映进程的状态信息;而sysfs主要用于Linux设备模型。不论是procfs或是sysfs的接口应该保持相对稳定,因为用户态程序很可能会依赖它们。当然,如果我们只是临时借用procfs或者sysfs来作debug之用,在代码发布之前将相关调试代码删除也无不可。但如果相关的调试借口要在相当长的一段时间内存在于内核之中,就不太适合放在procfs和sysfs里了。故此,debugfs应运而生。
默认情况下,debugfs会被挂载在目录/sys/kernel/debug之下,如果您的发行版里没有自动挂载,可以用如下命令手动完成:

mount -t debugfs none /your/debugfs/dir

Linux内核为debugfs提供了非常简洁的API,本文接下来将以一个实作为例来介绍。这个实作会在debugfs中建立如下的目录结构:

a
b
subdir/c

其中,a对应模块中的一个u8类型的变量,b和subdir下面的c都是对应模块里的一个字符数组,只是它们的实现方式不同。
在module_init里,我们首先要建立根目录mydebug:

my_debugfs_root = debugfs_create_dir("mydebug", NULL);   

第一个参数是目录的名称,第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里。
子目录也是用debugfs_create_dir来实现:

sub_dir = debugfs_create_dir("subdir", my_debugfs_root);

建立文件a的代码非常简单:

debugfs_create_u8("a", 0644, my_debugfs_root, &a);

这表示文件名为“a”,文件属性是0644,父目录是上面建立的“mydebug”,对应的变量是模块中的a。Linux内核还提供了其他一些创建debugfs文件的API,参考附录。
b是一个32-bytes的字符数组,在debugfs里,数组可以用blob wrapper来实现。

char hello[32] = "Hello world!\n";
structdebugfs_blob_wrapper b;
b.data = (void*)hello;
b.size = strlen(hello) + 1;
debugfs_create_blob("b", 0644, my_debugfs_root, &b);

这里需要注意的是,blob wrapper定义的数据只能是只读的。在本例中,虽然我们把文件b的权限设定为0644,但实际这个文件还是只读的,如果试图改写这个文件,系统将提示出错。如果需要对内核数组进行写的动作,blob wrapper就无法满足要求,我们只能通过自己定义文件操作来实现。

c和b在模块里对应着同一块字符数组,不同的是,b是只读的,而c通过自定义的文件操作同时实现了读和写。

static int c_open(struct inode *inode, struct file *filp)   
{
    filp->private_data = inode->i_private;
    return 0;
}
   
static ssize_t c_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
    if(*ppos >= 32)
        return 0;
    if(*ppos + count > 32)
        count = 32 - *ppos;
    if(copy_to_user(buffer, hello + *ppos, count))
        return -EFAULT;
    *ppos += count;
    return count;
}
    
static ssize_t c_write(struct file *filp, const char __user *buffer, size_tcount, loff_t *ppos)
{
    if(*ppos >= 32)
        return 0;
    if(*ppos + count > 32)
        count = 32 - *ppos;
    if(copy_from_user(hello + *ppos, buffer, count))
        return -EFAULT;
    *ppos += count;
    return count;
}   

struct file_operations c_fops = {
    .owner = THIS_MODULE,
    .open = c_open,
    .read = c_read,
    .write = c_write,
};

debugfs_create_file("c", 0644, sub_dir, NULL, &c_fops);

注:代码里,c_open其实并没有任何用处,因为c_read和c_write直接引用了全局变量hello。这里,我们也可以换一种写法,在read/write函数里用filp->private_data来引用字符数组hello。
到这里,三个文件和子目录已经创建完毕。在module_exit中,我们要记得释放创建的数据。

debugfs_remove_recursive(my_debugfs_root);

debugfs_remove_recursive可以帮我们逐步移除每个分配的dentry,如果您想一个一个手动的移除,也可以直接调用debugfs_remove。


附录:

创建和撤销目录及文件

struct dentry *debugfs_create_dir(constchar *name, structdentry *parent);   
struct dentry *debugfs_create_file(constchar *name, mode_t mode, structdentry *parent, void*data, conststruct file_operations *fops);
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);

创建单值文件

struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct dentry *parent, u8 *value);   
struct dentry *debugfs_create_u16(const char *name, mode_t mode,struct dentry *parent, u16 *value);   
struct dentry *debugfs_create_u32(const char *name, mode_t mode,struct dentry *parent, u32 *value);   
struct dentry *debugfs_create_u64(const char *name, mode_t mode, struct dentry *parent, u64 *value);   
    
struct dentry *debugfs_create_x8(const char *name, mode_t mode,struct dentry *parent, u8 *value);   
struct dentry *debugfs_create_x16(const char *name, mode_t mode,struct dentry *parent, u16 *value);   
struct dentry *debugfs_create_x32(const char *name, mode_t mode,struct dentry *parent, u32 *value);   
    
struct dentry *debugfs_create_size_t(const char *name, mode_t mode,struct dentry *parent, size_t*value);   
struct dentry *debugfs_create_bool(const char *name, mode_t mode,struct dentry *parent, u32 *value);   

其中,后缀为x8、x16、x32的这三个函数是指debugfs中的数据用十六进制表示。

创建BLOB文件

struct debugfs_blob_wrapper {
    void*data;
    unsigned long size;
};

struct dentry *debugfs_create_blob(const char *name, mode_t mode, struct dentry *parent, struct debugfs_blob_wrapper *blob);

其它

struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, const char *new_name);
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *target);



https://blog.csdn.net/u013165704/article/details/80777799
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值