debugfs

  • In this program, you are going to learn

  • How to debug the kernel code ?

  • Here is the explanation of the source code.

  • Add the list of headers to refer the APIs used in the source code.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/err.h>
  • Add the module macros which contains the information about macros such as author, description and license.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Simple Linux Device Driver - debugfs");
  • Initialize the functions and the variables which are used in the source code.

u64 intvalue, hexvalue;

struct dentry *dirret, *fileret, *u64int, *u64hex;
char ker_buf[len];
int filevalue;
  • Add the module init function to execute once when the module is loaded.

static int __init init_debug(void)
{
    /* create a directory by the name dell in /sys/kernel/debugfs */
    dirret = debugfs_create_dir("dell", NULL);

    /* create a file in the above directory. This requires read and write file operations */
    fileret = debugfs_create_file("text", 0004, dirret, &filevalue, &fops_debug);

    /* create a file which takes in a int(64) value */
    u64int = debugfs_create_u64("number", 0004, dirret, &intvalue);
    if (!u64int) {
        pr_err("error creating int file");
        return -ENODEV;
    }
    /* takes a hex decimal value */
    u64hex = debugfs_create_x64("hexnum", 0004, dirret, &hexvalue);
    if (!u64hex) {
        pr_err("error creating hex file");
        return -ENODEV;
    }
    pr_info("Device Driver Loaded..\n");
    return 0;
}
  • To be able to use debugfs, we start by creating a directory within /sys/kernel/debug, which is an ideal way to start. The rest of the files can be placed within this directory.

dirret = debugfs_create_dir("dell", NULL);
  • If you need to create a single file within debugfs, you can the debugfs_create_file.

  • To create a file in the above directory. This requires read and write file operation

fileret = debugfs_create_file("text", 0004, dirret, &filevalue, &fops_debug);
  • To create a file which takes in a int(64) value

u64int = debugfs_create_u64("number", 0004, dirret, &intvalue)
  • It takes a hex decimal value

u64hex = debugfs_create_x64("hexnum", 0004, dirret, &hexvalue
  • Add the module exit function which is executed when the module is unloaded from the kernel.

static void __exit exit_debug(void)
{
    debugfs_remove_recursive(dirret);
    pr_info("Device Driver Unloaded..\n");
}
  • debugfs_remove_recursive used to cleanup all the files and directories created.

debugfs_remove_recursive(dirret);
  • Add debugfs APIs

static ssize_t myreader(struct file *fp, char __user *user_buffer, size_t count, loff_t *position)
{
    return simple_read_from_buffer(user_buffer, count, position, ker_buf, len);
}

/* write file operation */
static ssize_t mywriter(struct file *fp, const char __user *user_buffer, size_t count, loff_t *position)
{
    if (count > len)
        return -EINVAL;

    return simple_write_to_buffer(ker_buf, len, position, user_buffer, count);
}

static const struct file_operations fops_debug = {
    .read = myreader,
    .write = mywriter,
};
  • Add the mention init and exit function which is executed the module is loaded and unloaded.

module_init(init_debug);
module_exit(exit_debug);
 1#include <linux/init.h>
 2#include <linux/module.h>
 3#include <linux/debugfs.h> /*this is for DebugFS libraries*/
 4#include <linux/fs.h>
 5
 6MODULE_LICENSE("GPL");
 7MODULE_AUTHOR("Linux_usr");
 8MODULE_DESCRIPTION("sample Device Driver - debugfs");
 9
10#define len 200
11
12u64 intvalue, hexvalue;
13
14struct dentry *dirret, *fileret, *u64int, *u64hex;
15char ker_buf[len];
16int filevalue;
17
18/* read file operation */
19static ssize_t myreader(struct file *fp, char __user *user_buffer, size_t count, loff_t *position)
20{
21	return simple_read_from_buffer(user_buffer, count, position, ker_buf, len);
22}
23
24/* write file operation */
25static ssize_t mywriter(struct file *fp, const char __user *user_buffer, size_t count, loff_t *position)
26{
27	if (count > len)
28		return -EINVAL;
29
30	return simple_write_to_buffer(ker_buf, len, position, user_buffer, count);
31}
32
33static const struct file_operations fops_debug = {
34	.read = myreader,
35	.write = mywriter,
36};
37
38static int __init init_debug(void)
39{
40	/* create a directory by the name dell in /sys/kernel/debugfs */
41	dirret = debugfs_create_dir("dell", NULL);
42
43	/* create a file in the above directory. This requires read and write file operations */
44	fileret = debugfs_create_file("text", 0004, dirret, &filevalue, &fops_debug);
45
46	/* create a file which takes in a int(64) value */
47	u64int = debugfs_create_u64("number", 0004, dirret, &intvalue);
48	if (!u64int) {
49		pr_err("error creating int file");
50		return -ENODEV;
51	}
52	/* takes a hex decimal value */
53	u64hex = debugfs_create_x64("hexnum", 0004, dirret, &hexvalue);
54	if (!u64hex) {
55		pr_err("error creating hex file");
56		return -ENODEV;
57	}
58	pr_info("Device Driver Loaded..\n");
59	return 0;
60}
61
62static void __exit exit_debug(void)
63{
64	debugfs_remove_recursive(dirret);
65	pr_info("Device Driver Unloaded..\n");
66}
67
68module_init(init_debug);
69module_exit(exit_debug);
1obj-m += driver_debugfs.o
2all:
3	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run Make to compile the module.

$make
make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_driver_debugfs modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
  CC [M]  $HOME/kernel_driver_debugfs/driver_debugfs.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  $HOME/kernel_driver_debugfs/driver_debugfs.mod.o
  LD [M]  $HOME/kernel_driver_debugfs/driver_debugfs.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
  • Run ls to check if driver_debugfs.ko is generated or not.

$ ls -l
total 36
-rw-rw-r-- 1 test test  154 Feb 26 13:18 Makefile
-rw-rw-r-- 1 test test   47 Feb 26 13:18 modules.order
-rw-rw-r-- 1 test test    0 Feb 26 13:18 Module.symvers
-rw-rw-r-- 1 test test  820 Feb 26 13:18 driver_debugfs.c
-rw-rw-r-- 1 test test 5880 Feb 26 13:18 driver_debugfs.ko
-rw-rw-r-- 1 test test   47 Feb 26 13:18 driver_debugfs.mod
-rw-rw-r-- 1 test test  919 Feb 26 13:18 driver_debugfs.mod.c
-rw-rw-r-- 1 test test 3448 Feb 26 13:18 driver_debugfs.mod.o
-rw-rw-r-- 1 test test 3320 Feb 26 13:18 driver_debugfs.o
  • Run insmod to load the module.

$ sudo insmod ./driver_debugfs.ko
  • Once the module is loaded into kernel check for the files created.

$ ls -l /sys/kernel/debug/dell/
total 0
-rw-r--r-- 1 root root 0 Jul 14 20:08 hexnum
-rw-r--r-- 1 root root 0 Jul 14 20:08 number
-rw-r--r-- 1 root root 0 Jul 14 20:08 text
  • Run rmmod to unload the module.

$ sudo rmmod driver_debugfs
  • Check dmesg and we can clearly see the read and write operations are executed.

[ 9523.186611] Device Driver Loaded..
[ 9534.912912] Device Driver Unloaded..

API

Learning

debugfs_create_dir

To create a directory within /sys/kernel/debug

debugfs_create_file

To create a file in the above directory

debugfs_create_u64

To create a file which takes in a int(64) value

debugfs_remove_recursive

To create a file which takes hex decimal value

See Also