Debugfs
DebugFS as the name suggests, is a virtual file system used by kernel developers to make debug information available to user space. General approach of printing debug messages using printk() calls is sufficient for most cases, but, often that is not the best way to go.
The debugging information may only be useful occasionally, but the printed output clogs up the logs all the time. Using printk() also does not help if the developer wishes to be able to change values from user space. Debug information exported through logical file interface can be highly useful for a developer who is trying to figure out subtle issues, like memory leaks, function tracing, interrupt tracing etc..
Debug information can be made available through virtual file interfaces provided by Procfs or Sysfs. This approach works, but there is little enthusiasm among kernel developers for creating debug information files in /proc or /sys file-system, as it would be a deviation to use them as debug file interfaces. Procfs was intented for serving as an interface to reflect process information, and show status of various kernel data structures to user space through files in /proc. Sysfs was intended to serve as an interface for Linux device model. files in /sys present hardware information useful for administering the system. Rules for sysfs require that each file contain a single value. The result is that sysfs is relatively consistent, but it is unwieldy for a developer who wishes to dump out a complicated data structure.
As a way of making life easier for developers, Greg Kroah-Hartman has created debugfs, a virtual filesystem devoted to debugging information. Debugfs is intended to be a relatively easy and lightweight subsystem which gracefully disappears when configured out of the kernel. unlike sysfs, debugfs has no rules, Developers can put any information they want there, there are no stability constraints placed on files exported there.
By default, debugfs will be mounted under the directory /sys/kernel/debug if your release (distribution specific kernel release) does not automatically mount, can be done manually with the following command:
mount -t debugfs none /sys/kernel/debug
Creating Directories and Files
let us start by creating a new directory in debugfs root directory(/sys/kernel/debug).
static struct dentry *my_debugfs_root;
my_debugfs_root = debugfs_create_dir("mydir", NULL);
if (!my_debugfs_root)
return -ENOENT;
The first parameter is the name of the directory(mydir), the second parameter is used to specify parent directory, if it is NULL, that is placed in the root directory of the debugfs.
lets create a sub-directory under mydir
struct dentry *sub_dir;
sub_dir = debugfs_create_dir("subdir", my_debugfs_root);
if (!sub_dir)
goto Fail;
Debugfs provides three separate interfaces for creating files.
- Normal data files that are bound to assigned file operations, these files interfaces can be either read-only or read-write.
- Files to export a Single integer value that can be read-only or read-write access modes. values supported are for type u8, u16, u32, u64, bool in decimal . and x8, x16, x32, x64 in hexadecimal.
- Read-only data blob file interface that allows exporting block of arbitrary data , that can be binary or text.
Following is complete module source that creates one file of each type.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <asm/uaccess.h>
static struct dentry *my_debugfs_root;
static u8 a = 0;
static char mystr[32] = "Techveda.org";
static struct debugfs_blob_wrapper b;
static int dbg_open(struct inode *inode, struct file *filp)
{
filp->private_data = inode->i_private;
return 0;
}
static ssize_t dbg_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, mystr + *ppos, count))
return -EFAULT;
*ppos += count;
return count;
}
static ssize_t dbg_write(struct file *filp, const char __user * buffer,
size_t count, loff_t * ppos)
{
if (*ppos >= 32)
return 0;
if (*ppos + count > 32)
count = 32 - *ppos;
if (copy_from_user(mystr + *ppos, buffer, count))
return -EFAULT;
*ppos += count;
return count;
}
static struct file_operations data_fops = {
.owner = THIS_MODULE,
.open = dbg_open,
.read = dbg_read,
.write = dbg_write,
};
static int __init mydbgfs_init(void)
{
struct dentry *sub_dir, *r_a, *r_b, *s_c;
printk(KERN_INFO "mydbgfs_initn");
my_debugfs_root = debugfs_create_dir("mydir", NULL);
if (!my_debugfs_root)
return -ENOENT;
r_a = debugfs_create_u8("u8file", 0644, my_debugfs_root, &a);
if (!r_a)
goto Fail;
b.data = (void *)mystr;
b.size = strlen(mystr) + 1;
r_b = debugfs_create_blob("blobfile", 0644, my_debugfs_root, &b);
if (!r_b)
goto Fail;
sub_dir = debugfs_create_dir("subdir", my_debugfs_root);
if (!sub_dir)
goto Fail;
s_c = debugfs_create_file("datafile", 0644, sub_dir, NULL, &data_fops);
if (!s_c)
goto Fail;
return 0;
Fail:
debugfs_remove_recursive(my_debugfs_root);
my_debugfs_root = NULL;
return -ENOENT;
}
static void __exit mydbgfs_exit(void)
{
printk(KERN_INFO "mydbgfs_exitn");
debugfs_remove_recursive(my_debugfs_root);
}
module_init(mydbgfs_init);
module_exit(mydbgfs_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.techveda.org");
MODULE_DESCRIPTION("debugfs file enumeration module");
makefile to build module object
obj-m := mydbgfs.o
default:
make -C /lib/modules/$(shell uname -r)/build/ SUBDIRS=$(shell pwd) modules
clean:
rm -rf $(wildcard *.o *.ko *.mod.* .c* .t* Module.symvers *.order *.markers)
lets insert module and run tests
root@ubuntu:/home/raghu/debugfs# make make -C /lib/modules/3.0.0-12-generic/build/ SUBDIRS=/home/raghu/debugfs modules make[1]: Entering directory `/usr/src/linux-headers-3.0.0-12-generic' CC [M] /home/raghu/debugfs/mydbgfs.o Building modules, stage 2. MODPOST 1 modules CC /home/raghu/debugfs/mydbgfs.mod.o LD [M] /home/raghu/debugfs/mydbgfs.ko make[1]: Leaving directory `/usr/src/linux-headers-3.0.0-12-generic' root@ubuntu:/home/raghu/debugfs# insmod mydbgfs.ko root@ubuntu:/home/raghu/debugfs# ls /sys/kernel/debug/ acpi bdi bluetooth extfrag gpio hid kprobes mce mydir regulator sched_features tracing usb vmmemctl wakeup_sources x86 root@ubuntu:/home/raghu/debugfs# tree /sys/kernel/debug/mydir/ /sys/kernel/debug/mydir/ ├── blobfile ├── subdir │ └── datafile └── u8file 1 directory, 3 files root@ubuntu:/home/raghu/debugfs#
we can see directory and all files have been created , lets access u8 file
root@ubuntu:/home/raghu/debugfs# cd /sys/kernel/debug/mydir/ root@ubuntu:/sys/kernel/debug/mydir# ls blobfile subdir u8file root@ubuntu:/sys/kernel/debug/mydir# cat u8file 0 root@ubuntu:/sys/kernel/debug/mydir# echo 10 > u8file root@ubuntu:/sys/kernel/debug/mydir# cat u8file 10 root@ubuntu:/sys/kernel/debug/mydir#
let’s access blob file
root@ubuntu:/sys/kernel/debug/mydir# cat blobfile Techveda.org root@ubuntu:/sys/kernel/debug/mydir# echo www.techveda.org > blobfile bash: echo: write error: Invalid argument root@ubuntu:/sys/kernel/debug/mydir#
write access has rightly failed , since blob files are read only, let’s get into subdirectory and access datafile
root@ubuntu:/sys/kernel/debug/mydir# cat subdir/datafile Techveda.org root@ubuntu:/sys/kernel/debug/mydir# echo www.techveda.org > subdir/datafile root@ubuntu:/sys/kernel/debug/mydir# cat subdir/datafile www.techveda.org root@ubuntu:/sys/kernel/debug/mydir#
Both read and write access are allowed since we assigned read/write file ops.
Directory and files would be removed when module is unloaded
root@ubuntu:/sys/kernel/debug# rmmod /home/raghu/debugfs/mydbgfs root@ubuntu:/sys/kernel/debug# ls acpi bluetooth gpio kprobes regulator tracing vmmemctl x86 bdi extfrag hid mce sched_features usb wakeup_sources root@ubuntu:/sys/kernel/debug#
Following is a sample code form kernel source that shows how kmemleak uses debugfs interface.
static const struct seq_operations kmemleak_seq_ops = {
.start = kmemleak_seq_start,
.next = kmemleak_seq_next,
.stop = kmemleak_seq_stop,
.show = kmemleak_seq_show,
};
static int kmemleak_open(struct inode *inode, struct file *file)
{
return seq_open(file, &kmemleak_seq_ops);
}
static int kmemleak_release(struct inode *inode, struct file *file)
{
return seq_release(inode, file);
}
static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
size_t size, loff_t *ppos)
{...}
static const struct file_operations kmemleak_fops = {
.owner = THIS_MODULE,
.open = kmemleak_open,
.read = seq_read,
.write = kmemleak_write,
.llseek = seq_lseek,
.release = kmemleak_release,
};
dentry = debugfs_create_file("kmemleak", S_IRUGO, NULL, NULL,
&kmemleak_fops);
kmemleak is using seqfile operations to be able to print bulk data, look here for more information on sequence file interface .
Appendix: Complete interface
Creating and removing of directories and regular files
struct dentry *debugfs_create_dir(const char *name,
struct dentry *parent);
struct dentry *debugfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
struct file_operations *fops);
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);
Creating integer files
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);
Note that there is no debugfs_create_x64().
Creating Blob files
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);
Others
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);