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.

  1. Normal data files that are bound to  assigned file operations, these files interfaces can be either read-only or read-write.
  2. 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.
  3. 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);

Leave a Reply

Your email address will not be published. Required fields are marked *