DebugFS, as the name implies, is a virtual file system for kernel debugging, in which kernel developers exchange data through DebugFS and user space. Similar virtual file systems, such as PROCFS and SYSFS, are not actually stored on the hard disk, but are built after the Linux kernel is running.
Typically, the most commonly used kernel debugging means is PRINTK. But PRINTK not all things are good, such as printing data may be too much, we really care about the data in a lot of output is not so obvious, or we may need to modify some kernel variables in the debugging, in this case PRINTK can not do anything, And if the kernel is recompiled or the driver is too inefficient in order to modify a value, a temporary filesystem is needed to map the data we need to care to the user space. In the past, PROCFS can achieve this, in the 2.6 era, the newly introduced SYSFS can also be achieved, but whether PROCFS or SYSFS, using them to achieve some of the requirements of debug, seems to deviate from the original intention of their creation. PROCFS, for example, is designed to reflect the state of the process, while SYSFS is used primarily for Linux device models. The interfaces of either PROCFS or SYSFS should remain relatively stable because the user-state program is likely to rely on them. Of course, if we are only temporarily borrowing PROCFS or SYSFS for debug purposes, deleting the relevant debug code before the code is released is not an issue. But if the relevant debugging excuses are to be present in the kernel for a long time, it is not appropriate to put them in Procfs and Sysfs. Therefore, Debugfs was born.
By default, Debugfs will be mounted under directory/sys/kernel/debug, if you do not have an automatic mount in your distribution, you can do it manually using the following command:
# mount -t debugfs none /your/debugfs/dir |
The Linux kernel provides a very concise API for DEBUGFS, which will be introduced in a real example, and sample code can be downloaded from here.
This implementation will create the following directory structure in Debugfs:
Where a corresponds to a U8 type of variable in the module, B and subdir the following C is a character array in the corresponding module, but they are implemented differently.
In Module_init, we first set up the root directory mydebug:
1 |
my_debugfs_root = debugfs_create_dir( "mydebug" , NULL); |
The first parameter is the name of the directory, and the second parameter specifies the parent directory of the directory, and if it is null, it is placed in the root directory of the Debugfs.
Subdirectories are also implemented with Debugfs_create_dir:
1 |
sub_dir = debugfs_create_dir( "subdir" , my_debugfs_root); |
The code for building file A is very simple:
1 |
debugfs_create_u8( "a" , 0644, my_debugfs_root, &a); |
This means that the file name is "a", the file attribute is 0644, and the parent directory is the "Mydebug" created above, and the corresponding variable is a in the module.
The Linux kernel also provides additional APIs for creating Debugfs files, see the appendix to this article.
b is an array of 32-bytes characters, in Debugfs, arrays can be implemented with BLOB wrapper.
123456 |
char hello[32] = "Hello world!\n" ; struct debugfs_blob_wrapper b; b.data = ( void *)hello; b.size = strlen (hello) + 1; debugfs_create_blob( "b" , 0644, my_debugfs_root, &b); |
It is important to note that the data defined by the Blob wrapper can only be read-only. In this example, although we set the permission of file B to 0644, the actual file is still read-only, and if you attempt to overwrite the file, the system will prompt an error.
If you need to write to the kernel array, blob wrapper will not be able to meet the requirements, we can only do this through our own definition of file operations. In this practice, you can refer to the implementation of file C. C and B correspond to the same block of characters in the module, except that B is read-only and C is read and written at the same time by a custom file operation.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 |
static
int
c_open(
struct
inode *inode,
struct
file *filp)
{
filp->private_data = inode->i_private;
return
0;
}
static
ssize_t c_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, hello + *ppos, count))
return
-EFAULT;
*ppos += count;
return
count;
}
static
ssize_t c_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(hello + *ppos, buffer, count))
return
-EFAULT;
*ppos += count;
return
count;
}
struct
file_operations c_fops = {
.owner = THIS_MODULE,
.open = c_open,
.read = c_read,
.write = c_write,
};
debugfs_create_file(
"c"
, 0644, sub_dir, NULL, &c_fops);
|
Note: In the code, C_open does not actually have any use, because C_read and c_write directly refer to the global variable hello. Here, we can also change the wording, in the Read/write function with Filp->private_data to refer to the character array hello.
Here, three files and subdirectories have been created. In Module_exit, we have to remember to release the created data.
1 |
debugfs_remove_recursive(my_debugfs_root); |
Debugfs_remove_recursive can help us to gradually remove each assigned dentry, and if you want a manual removal, you can also call Debugfs_remove directly.
Appendix:
Create and revoke directories and files
123456 |
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,
const
struct
file_operations *fops);
void
debugfs_remove(
struct
dentry *dentry);
void
debugfs_remove_recursive(
struct dentry *dentry);
|
Create a single-value file
1234567891011121314151617181920 |
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);
struct
dentry *debugfs_create_size_t(
const
char
*name, mode_t mode,
struct dentry *parent,
size_t
*value);
struct
dentry *debugfs_create_bool(
const
char
*name, mode_t mode,
struct
dentry *parent, u32 *value);
|
Where the three functions suffix x8, x16, and x32 refer to the data in Debugfs in hexadecimal notation.
Create Blob file
1234567 |
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);
|
Other
12345 |
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);
|
Debugfs in the Linux kernel