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 |
charhello[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 |
staticintc_open(structinode *inode, structfile *filp){ filp->private_data = inode->i_private; return0;}staticssize_t c_read(structfile *filp, char__user *buffer, size_tcount, loff_t *ppos){ if(*ppos >= 32) return0; if(*ppos + count > 32) count = 32 - *ppos; if (copy_to_user(buffer, hello + *ppos, count)) return-EFAULT; *ppos += count; returncount;}staticssize_t c_write(struct file *filp, constchar__user *buffer, size_tcount, loff_t *ppos){ if(*ppos >= 32) return0; if(*ppos + count > 32) count = 32 - *ppos; if(copy_from_user(hello + *ppos, buffer, count)) return-EFAULT; *ppos += count; returncount;}structfile_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(constchar*name, structdentry *parent);structdentry *debugfs_create_file(constchar*name, mode_t mode, struct dentry *parent, void*data, conststructfile_operations *fops);voiddebugfs_remove(structdentry *dentry);voiddebugfs_remove_recursive(struct dentry *dentry); |
Create a single-value file
| 1234567891011121314151617181920 |
structdentry *debugfs_create_u8(constchar*name, mode_t mode, structdentry *parent, u8 *value);structdentry *debugfs_create_u16(const char*name, mode_t mode, structdentry *parent, u16 *value);structdentry *debugfs_create_u32(constchar*name, mode_t mode, structdentry *parent, u32 *value);structdentry *debugfs_create_u64(constchar*name, mode_t mode, structdentry *parent, u64 *value);structdentry *debugfs_create_x8(constchar*name, mode_t mode, structdentry *parent, u8 *value);structdentry *debugfs_create_x16(constchar*name, mode_t mode, structdentry *parent, u16 *value);structdentry *debugfs_create_x32(constchar*name, mode_t mode, structdentry *parent, u32 *value);structdentry *debugfs_create_size_t(constchar*name, mode_t mode, struct dentry *parent, size_t*value);structdentry *debugfs_create_bool(constchar*name, mode_t mode, structdentry *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 |
structdebugfs_blob_wrapper { void*data; unsigned longsize;};structdentry *debugfs_create_blob(constchar *name, mode_t mode, structdentry *parent, structdebugfs_blob_wrapper *blob); |
Other
| 12345 |
structdentry *debugfs_rename(structdentry *old_dir, structdentry *old_dentry, structdentry *new_dir, constchar*new_name);structdentry *debugfs_create_symlink(constchar*name, struct dentry *parent, constchar*target); |
Debugfs in the Linux kernel