DebugFS

Copyright © 2009 Jonathan Corbet <corbet@lwn.net>

Debugfs 存在,是内核开发者向用户空间提供信息的一种简单方式。 与 /proc(仅用于关于进程的信息)或 sysfs(具有严格的每个文件一个值的规则)不同,debugfs 没有任何规则。 开发者可以将他们想要的任何信息放在那里。 debugfs 文件系统也不打算用作用户空间的稳定 ABI; 理论上,对导出到那里的文件没有稳定性约束。 然而,现实世界并不总是那么简单 [1]; 即使是 debugfs 接口,最好也以需要永久维护的思路来设计。

Debugfs 通常通过如下命令挂载

mount -t debugfs none /sys/kernel/debug

(或者等效的 /etc/fstab 行)。 默认情况下,debugfs 根目录只能由 root 用户访问。 要更改对树的访问权限,可以使用 “uid”、“gid” 和 “mode” 挂载选项。

请注意,debugfs API 仅以 GPL 方式导出到模块。

使用 debugfs 的代码应包含 <linux/debugfs.h>。 然后,首先要做的是创建至少一个目录来保存一组 debugfs 文件

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

如果调用成功,此调用将在指定的父目录下创建一个名为 name 的目录。 如果 parent 为 NULL,则将在 debugfs 根目录中创建该目录。 成功后,返回值是一个 struct dentry 指针,可用于在该目录中创建文件(并在最后清理它)。 ERR_PTR(-ERROR) 返回值表示出现问题。 如果返回 ERR_PTR(-ENODEV),则表明内核在构建时没有 debugfs 支持,并且下面描述的任何函数都将不起作用。

在 debugfs 目录中创建文件的最通用方法是使用

struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                   struct dentry *parent, void *data,
                                   const struct file_operations *fops);

在这里,name 是要创建的文件的名称,mode 描述了文件应具有的访问权限,parent 指示应保存该文件的目录,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);

这些文件支持读取和写入给定的值; 如果不应写入特定文件,只需相应地设置模式位即可。 这些文件中的值采用十进制; 如果十六进制更合适,则可以使用以下函数代替

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);

只要开发人员知道要导出的值的大小,这些函数就很有用。 但是,某些类型在不同的体系结构上可能具有不同的宽度,从而使情况变得复杂。 有一些函数旨在帮助处理此类特殊情况

void debugfs_create_size_t(const char *name, umode_t mode,
                           struct dentry *parent, size_t *value);

可以预料的是,此函数将创建一个 debugfs 文件来表示 size_t 类型的变量。

类似地,有用于 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 中

void debugfs_create_bool(const char *name, umode_t mode,
                         struct dentry *parent, bool *value);

读取结果文件将产生 Y(对于非零值)或 N,后跟一个换行符。 如果写入该文件,它将接受大写或小写的值,或 1 或 0。 任何其他输入都将被静默忽略。

此外,atomic_t 值可以通过以下方式放置在 debugfs 中

void debugfs_create_atomic_t(const char *name, umode_t mode,
                             struct dentry *parent, atomic_t *value)

读取此文件将获取 atomic_t 值,写入此文件将设置 atomic_t 值。

另一个选项是导出任意二进制数据块,使用此结构和函数

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 结构指向的数据。 一些驱动程序使用 “blobs” 作为返回几行(静态)格式化文本输出的简单方法。 此函数可用于导出二进制信息,但似乎没有代码在主线中这样做。 请注意,使用 debugfs_create_blob() 创建的所有文件都是只读的。

如果要转储寄存器块(在开发过程中经常发生,即使很少有此类代码到达主线),debugfs 提供了两个函数:一个用于创建仅寄存器文件,另一个用于将寄存器块插入另一个顺序文件的中间

struct debugfs_reg32 {
    char *name;
    unsigned long offset;
};

struct debugfs_regset32 {
    const struct debugfs_reg32 *regs;
    int nregs;
    void __iomem *base;
    struct device *dev;     /* Optional device for Runtime PM */
};

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);

“base” 参数可以为 0,但您可能希望使用 __stringify 构建 reg32 数组,并且许多寄存器名称(宏)实际上是寄存器块的基址上的字节偏移量。

如果要将 u32 数组转储到 debugfs 中,可以使用以下命令创建一个文件

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” 参数包装指向数组数据的指针及其元素的数量。 注意:一旦创建了数组,就无法更改其大小。

有一个辅助函数可以创建与设备相关的 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” 参数是与此 debugfs 文件相关的设备,“read_fn” 是一个函数指针,用于调用以打印 seq_file 内容。

还有几个面向目录的辅助函数

struct dentry *debugfs_change_name(struct dentry *dentry,
                                      const char *fmt, ...);

struct dentry *debugfs_create_symlink(const char *name,
                                      struct dentry *parent,
                                      const char *target);

调用 debugfs_change_name() 将为现有 debugfs 文件提供一个新名称,始终在同一个目录中。 在调用之前,new_name 必须不存在; 成功时返回值为 0,失败时返回 -E...。 可以使用 debugfs_create_symlink() 创建符号链接。

所有 debugfs 用户都必须考虑一个重要事项:debugfs 中创建的任何目录都不会自动清理。 如果一个模块在没有显式删除 debugfs 条目的情况下卸载,结果将是大量的过时指针,并且会导致大量反社会行为。 因此,所有 debugfs 用户 - 至少那些可以构建为模块的用户 - 必须准备好删除他们在其中创建的所有文件和目录。 可以使用以下命令删除文件或目录

void debugfs_remove(struct dentry *dentry);

dentry 值可以为 NULL 或错误值,在这种情况下,将不会删除任何内容。 请注意,此函数将递归删除其下的所有文件和目录。 以前,debugfs_remove_recursive() 用于执行该任务,但此函数现在只是 debugfs_remove() 的别名。 debugfs_remove_recursive() 应被视为已弃用。