1 Debugfs简介
Debugfs文件系统目的是为开发人员提供更多内核数据,方便调试内容. 我们知道/proc文件系统关注的是进程信息,/sysfs关注是one-value-per-file策略集,而Debugfs文件系统没有如此多限制,可是任何内核要输出的信息。
2 Debugfs使用
2.1 安装文件系统
Debugfs没有物理设备,其挂载方式:
mount -t debugfs none /sys/kernel/debug
2.2 编程使用
2.2.1 要使用Debugfs的模块需要包含
2.2.2 具体步骤
a 创建目录。所有该模块输出的信息都在该目录下面。
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
b 创建文件。在上面创建的目录下面创建文件。
struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data,
const struct file_operations *fops);
通过fops操作文件的读写等请求,文件一般用来传递字符创等信息。
c 对于数字情况下,就没有必须通过文件来传递信息了。Debugfs提供了针对数字的接口:
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_bool(const char *name, mode_t mode, struct dentry *parent, u32 *value);
struct dentry *debugfs_create_blob(const char *name, mode_t mode, struct dentry *parent,
struct debugfs_blob_wrapper *blob);
d 模块卸载,需要主动删除目录下的文件。
void debugfs_remove_recursive(struct dentry *dentry);
3 Debugfs内核代码分析
3.1 初始化和释放资源
static int __init debugfs_init(void)
{
int retval;
debug_kobj = kobject_create_and_add("debug", kernel_kobj);
if (!debug_kobj)
return -EINVAL;
retval = register_filesystem(&debug_fs_type);
if (retval)
kobject_put(debug_kobj);
else
debugfs_registered = true;
return retval;
}
static void __exit debugfs_exit(void)
{
debugfs_registered = false;
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
unregister_filesystem(&debug_fs_type);
kobject_put(debug_kobj);
}
3.2 关键函数
debugfs_create_file函数是关键函数,其他接口基本都是该接口的wrapper。
3.2.1 变参数宏的定义
#define pr_debug(fmt, ...) do { \
dynamic_pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
dynamic_pr_debug 也是变参数的.
3.2.2 debugfs_create_file
static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
static bool debugfs_registered;
struct debugfs_blob_wrapper {
void *data;
unsigned long size;
};
static struct file_system_type debug_fs_type = {
.owner = THIS_MODULE,
.name = "debugfs",
.get_sb = debug_get_sb,
.kill_sb = kill_litter_super,
};
struct dentry *debugfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops)
{
struct dentry *dentry = NULL;
int error;
pr_debug("debugfs: creating file '%s'\n",name);
error = simple_pin_fs(&debug_fs_type, &debugfs_mount,
&debugfs_mount_count);
if (error)
goto exit;
error = debugfs_create_by_name(name, mode, parent, &dentry);
if (error) {
dentry = NULL;
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
goto exit;
}
if (dentry->d_inode) {
if (data)
dentry->d_inode->i_private = data;
if (fops)
dentry->d_inode->i_fop = fops;
}
exit:
return dentry;
}
int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count)
{
struct vfsmount *mnt = NULL;
spin_lock(&pin_fs_lock);
if (unlikely(!*mount)) {
spin_unlock(&pin_fs_lock);
mnt = vfs_kern_mount(type, 0, type->name, NULL);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
spin_lock(&pin_fs_lock);
if (!*mount)
*mount = mnt;
}
mntget(*mount);
++*count;
spin_unlock(&pin_fs_lock);
mntput(mnt);
return 0;
}
static int debugfs_create_by_name(const char *name, mode_t mode,
struct dentry *parent,
struct dentry **dentry)
{
int error = 0;
if (!parent) {
if (debugfs_mount && debugfs_mount->mnt_sb) {
parent = debugfs_mount->mnt_sb->s_root;
}
}
if (!parent) {
pr_debug("debugfs: Ah! can not find a parent!\n");
return -EFAULT;
}
*dentry = NULL;
mutex_lock(&parent->d_inode->i_mutex);
*dentry = lookup_one_len(name, parent, strlen(name));
if (!IS_ERR(*dentry)) {
switch (mode & S_IFMT) {
case S_IFDIR:
error = debugfs_mkdir(parent->d_inode, *dentry, mode);
break;
case S_IFLNK:
error = debugfs_link(parent->d_inode, *dentry, mode);
break;
default:
error = debugfs_create(parent->d_inode, *dentry, mode);
break;
}
dput(*dentry);
} else
error = PTR_ERR(*dentry);
mutex_unlock(&parent->d_inode->i_mutex);
return error;
}
static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &debugfs_file_operations;
break;
case S_IFLNK:
inode->i_op = &debugfs_link_operations;
break;
case S_IFDIR:
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inc_nlink(inode);
break;
}
}
return inode;
}
const struct file_operations debugfs_file_operatiOns= {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
};
const struct file_operations simple_dir_operatiOns= {
.open = dcache_dir_open,
.release = dcache_dir_close,
.llseek = dcache_dir_lseek,
.read = generic_read_dir,
.readdir = dcache_readdir,
.fsync = simple_sync_file,
};
const struct inode_operations simple_dir_inode_operatiOns= {
.lookup = simple_lookup,
};
3.3 其他函数
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
{
return debugfs_create_file(name,
S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
parent, NULL, NULL);
}
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
struct dentry *parent, u32 *value)
{
return debugfs_create_file(name, mode, parent, value, &fops_bool);
}
由以上可以看出,其他的输出信息都是debugfs_create_file的包装,这里主要是debugfs_create_file函数最后一个参数struct file_operations不同而已。
这里以bool类型为例:
static ssize_t read_file_bool(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[3];
u32 *val = file->private_data;
if (*val)
buf[0] = 'Y';
else
buf[0] = 'N';
buf[1] = '\n';
buf[2] = 0x00;
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t write_file_bool(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[32];
int buf_size;
u32 *val = file->private_data;
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
switch (buf[0]) {
case 'y':
case 'Y':
case '1':
*val = 1;
break;
case 'n':
case 'N':
case '0':
*val = 0;
break;
}
return count;
}
static const struct file_operations fops_bool = {
.read = read_file_bool,
.write = write_file_bool,
.open = default_open,
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之下,如果您的发行版里没有自动挂载,可以用如下命令手动完成:
Linux内核为debugfs提供了非常简洁的API,本文接下来将以一个实作为例来介绍,sample code可以从这里下载。
这个实作会在debugfs中建立如下的目录结构:
其中,a对应模块中的一个u8类型的变量,b和subdir下面的c都是对应模块里的一个字符数组,只是它们的实现方式不同。
在module_init里,我们首先要建立根目录mydebug:
第一个参数是目录的名称,第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里。
子目录也是用debugfs_create_dir来实现:
建立文件a的代码非常简单:
这表示文件名为“a”,文件属性是0644,父目录是上面建立的“mydebug”,对应的变量是模块中的a。
Linux内核还提供了其他一些创建debugfs文件的API,请参考本文的附录。
b是一个32-bytes的字符数组,在debugfs里,数组可以用blob wrapper来实现。
这里需要注意的是,blob wrapper定义的数据只能是只读的。在本例中,虽然我们把文件b的权限设定为0644,但实际这个文件还是只读的,如果试图改写这个文件,系统将提示出错。
如果需要对内核数组进行写的动作,blob wrapper就无法满足要求,我们只能通过自己定义文件操作来实现。在这个实作里,可以参考文件c的实现。c和b在模块里对应着同一块字符数组,不同的是,b是只读的,而c通过自定义的文件操作同时实现了读和写。
注:代码里,c_open其实并没有任何用处,因为c_read和c_write直接引用了全局变量hello。这里,我们也可以换一种写法,在read/write函数里用filp->private_data来引用字符数组hello。
到这里,三个文件和子目录已经创建完毕。在module_exit中,我们要记得释放创建的数据。
debugfs_remove_recursive可以帮我们逐步移除每个分配的dentry,如果您想一个一个手动的移除,也可以直接调用debugfs_remove。
附录:
创建和撤销目录及文件
创建单值文件
其中,后缀为x8、x16、x32的这三个函数是指debugfs中的数据用十六进制表示。
创建BLOB文件
其它