概述
Debugfs是内核开发人员向用户空间提供信息的一种简单方法。 开发人员可以把他们想要的任何信息放在那里。
Debugfs通常使用如下命令进行挂载:
mount -t debugfs none /sys/kernel/debug
或者在/etc/fstab中增加一行
默认情况下,只有root用户可以访问debufgfs根目录. To change access to the tree the “uid”, “gid” and “mode” mount options can be used.
使用debugfs时需要包含头文件:
#include <linux/debugfs.h>.
还需要在deconfig文件中,设置CONFIG_DEBUG_FS
CONFIG_DEBUG_FS=y
然后,要先创建至少一个目录来存放一组debugfs文件:
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
关于这个接口的说明如下:
- 如果调用成功,将在指定的parent目录下创建一个名为name的目录;
- 如果parent为NULL,则该目录将在debugfs根目录中创建name目录;
- 成功执行时,返回值是一个struct dentry指针,这个指针可用于在name目录中创建文件,并且在最后清理文件时也要用到这个指针
- 执行失败时,返回值为ERR_PTR(-ERROR) 。注意这里的ERROR是errno。如果返回值为ERR_PTR(-ENODEV),这表明编译时没有把debugfs编译到内核
The most general way to create a file within a debugfs directory is with:
在debugfs目录中创建文件的最常用方法是使用下面的接口:
struct dentry *debugfs_create_file(const char *name, umode_t mode,struct dentry *parent, void *data,const struct file_operations *fops);
关于这个接口的说明:
- name是要创建的文件的名称
- mode描述name文件的访问权限
- parent表示存放name文件的目录
- data将被存储在生成的inode结构的i_private字段中
- fops是一组文件操作接口。至少,应该提供read()和/或write()操作;其他可以根据需求决定是否包括在内。
- 返回值将是一个指向创建文件的dentry指针,错误时为ERR_PTR(-ERROR),如果不支持debugfs,则返回为ERR_PTR(-ENODEV)。
创建一个初始大小的文件,可以使用下面的函数:
void debugfs_create_file_size(const char *name, umode_t mode,struct dentry *parent, void *data,const struct file_operations *fops,loff_t file_size);
关于这个接口的说明:
- File_size为文件的初始大小。其他参数与函数debugfs_create_file相同。
在许多情况下,实际上并不需要创建一组文件操作;debugfs代码为简单的情况提供了许多辅助函数。
创建一个文件,文件内写入了一个十进制整数:
void debugfs_create_u8(const char *name, umode_t mode,struct dentry *parent, u8 *value);void debugfs_create_u16(const char *name, umode_t mode,struct dentry *parent, u16 *value);void debugfs_create_u32(const char *name, umode_t mode,struct dentry *parent, u32 *value);void debugfs_create_u64(const char *name, umode_t mode,struct dentry *parent, u64 *value);
关于这个接口的说明:
- value是创建文件时要写入的整数
创建一个文件,文件内写入了一个十六进制整数::
void debugfs_create_x8(const char *name, umode_t mode,struct dentry *parent, u8 *value);void debugfs_create_x16(const char *name, umode_t mode,struct dentry *parent, u16 *value);void debugfs_create_x32(const char *name, umode_t mode,struct dentry *parent, u32 *value);void debugfs_create_x64(const char *name, umode_t mode,struct dentry *parent, u64 *value);
只要开发人员知道要导出的值的大小,可以用下面的函数导出数值。但是,有些类型在不同的体系架构上有不同的宽度,这在一定程度上使情况复杂化,这些函数如下:
下面函数将创建一个debugfs文件来表示size_t类型的变量:
void debugfs_create_size_t(const char *name, umode_t mode,struct dentry *parent, size_t *value);
导出unsigned long变量的接口如下:
struct dentry *debugfs_create_ulong(const char *name, umode_t mode,struct dentry *parent,unsigned long *value);void debugfs_create_xul(const char *name, umode_t mode,struct dentry *parent, unsigned long *value);
- debugfs_create_ulong用于导出十进制unsigned long变量
- debugfs_create_xul用于到处十六进制unsigned long变量
布尔值可以被放置在debugfs中:
void debugfs_create_bool(const char *name, umode_t mode,struct dentry *parent, bool *value);
对文件的读取将产生Y(对于非零值)或N,后跟一个换行符。如果写入,它将接受大写(Y/N)或小写值(y/n),或1或0。任何其他输入都将被静默地忽略。
atomic_t值可以放在debugfs中,对该文件的读取将获得atomic_t值,而对该文件的写入将设置atomic_t值。
void debugfs_create_atomic_t(const char *name, umode_t mode,struct dentry *parent, atomic_t *value)
导出二进制数据块:
struct debugfs_blob_wrapper {void *data;unsigned long size;};struct dentry *debugfs_create_blob(const char *name, umode_t mode,struct dentry *parent,struct debugfs_blob_wrapper *blob);
对该文件的读取将返回debugfs_blob_wrapper结构所指向的数据。可以用于导出一些格式化的数据。注意:debugfs_create_blob创建的文件都是只读的。
导出寄存器块:
struct debugfs_reg32 { //一个寄存器的name和offsetchar *name;unsigned long offset;};struct debugfs_regset32 { //一组寄存器。这里的“set”是“a set of register”的“set”const struct debugfs_reg32 *regs;int nregs;void __iomem *base;struct device *dev; /* Optional device for Runtime PM */};struct dentry *debugfs_create_regset32(const char *name, umode_t mode,struct dentry *parent,struct debugfs_regset32 *regset);void debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,int nregs, void __iomem *base, char *prefix);
debugfs_create_regset32创建寄存器块文件。debugfs_print_regs32将寄存器块打印到seq_file中。
如果你想在debugfs导出一个u32数组,你可以创建文件:
struct debugfs_u32_array {
u32 *array;
u32 n_elements;
};
void debugfs_create_u32_array(const char *name, umode_t mode,
struct dentry *parent,
struct debugfs_u32_array *array);
array 参数包装了一个struct debugfs_u32_array的指针。注意:array一旦创建,其大小就不能更改。
There is a helper function to create device related seq_file:
void debugfs_create_devm_seqfile(struct device *dev,
const char *name,
struct dentry *parent,
int (*read_fn)(struct seq_file *s,
void *data));
dev argument is the device related to this debugfs file, read_fn is a function pointer which to be called to print the seq_file content.
修改文件名和文件路径:
struct dentry *debugfs_rename(struct dentry *old_dir, //原文件所在的目录的struct dentry
struct dentry *old_dentry, //原文件的struct dentry
struct dentry *new_dir, //新文件所在的目录的struct dentry
const char *new_name); //新文件名
调用 debugfs_rename() 将一个已经存在的debugfs文件重命名为 new_name,也可以修改文件的目录。new_name不能是已经存在的文件的文件名;
创建软连接:
struct dentry *debugfs_create_symlink(const char *name, //软连接名
struct dentry *parent, //软连接所在的目录
const char *target); //目标文件的路径
代码中需要显式主动删除debugfs文件和目录。
删除一个文件或一个目录:
void debugfs_remove(struct dentry *dentry);
递归删除文件和目录:
void debugfs_remove_recursive(struct dentry *dentry); //dentry对应一个目录
示例代码
#if defined(CONFIG_DEBUG_FS)
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/sched.h>
struct lbk_debugfs_data {
struct dentry *dir;
struct dentry *dbg;
u32 command;
};
struct lbk_debugfs_data *dbg = NULL;
#endif
#if defined(CONFIG_DEBUG_FS)
static ssize_t lbk_dbg_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
int ret = 0;
char buf[24];
ssize_t len = 0;
if (dbg && (dbg->command != 0)) {
switch (dbg->command) {
case LBK_GET_KERNEL_VERSION:
len = snprintf(buf, sizeof(buf), "%s\n", LB_KERNEL_VERSION);
break;
default:
ret = -EINVAL;
break;
}
dbg->command = 0;
}
return ret ? ret : simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
static ssize_t lbk_dbg_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
char *pb;
char buf[24];
int ret = 0;
unsigned int val;
memset(buf, 0 , sizeof buf);
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
mutex_lock(&_lb_mutex);
if (count > 1) {
pb = buf + 1;
switch (buf[0]) {
case 'h':
lbwdt_info("Usage: [hkusplrfc] [t timeout] [j jiffies] [m msec]\n"
"\tj <jiffies>: set watchdog timeout jiffies\n\tm <msec>: set watchdog timeout msec\n\n");
break;
case 'k':
if (dbg) dbg->command = LBK_GET_KERNEL_VERSION;
lbwdt_info("kernel version: %s\n", LB_KERNEL_VERSION);
break;
case 'u':
if (dbg) dbg->command = LBK_GET_UUID;
break;
case 's':
...
default:
pr_err("%s: Invalid command!\n", __func__);
ret = -EINVAL;
break;
}
}
mutex_unlock(&_lb_mutex);
return ret ? ret : count;
}
static const struct file_operations lbk_dbg_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = lbk_dbg_write,
.read = lbk_dbg_read,
};
static int lbk_debugfs_init(void)
{
int ret = 0;
dbg = kzalloc(sizeof(struct lbk_debugfs_data), GFP_KERNEL);
if (!dbg) {
pr_err("Alloc debugfs data memory failed!\n");
return -ENOMEM;
}
dbg->dir = debugfs_create_dir(KBUILD_MODNAME, NULL); // the dir name is same with this file.
if (IS_ERR(dbg->dir)) {
ret = PTR_ERR(dbg->dir);
pr_err("failed to create debugfs dir! error: %d.\n", ret);
return ret;
} else {
dbg->dbg = debugfs_create_file("dbg", S_IRUGO | S_IWUSR, dbg->dir, NULL, &lbk_dbg_fops);
if (!dbg->dbg) {
ret = -ENOMEM;
goto err0;
}
}
return 0;
err0:
debugfs_remove_recursive(dbg->dir);
kfree(dbg);
dbg = NULL;
return ret;
}
static void lbk_debugfs_exit(void)
{
if (dbg) {
if (dbg->dir)
debugfs_remove_recursive(dbg->dir);
kfree(dbg);
dbg = NULL;
}
}
#endif
static int __init lbwdt_init(void)
{
#if defined(CONFIG_DEBUG_FS)
lbk_debugfs_init();
#endif
}
static void __exit lbwdt_exit(void)
{
#if defined(CONFIG_DEBUG_FS)
lbk_debugfs_exit();
#endif
return;
}
