debugfs
Rev.3を表示中。最新版はこちら。
debugfsはprocfsのように、カーネル内部情報を参照するもので、本質機能の目的はprocfsと同じです。ただ実装の仕方がprocfsのような実装上の制約がなく、通常のファイルイメージでファイルを作成するように、ファイルを作成することができます。そしてファイル単位に、ファイルオペレーションコールバック関数を設定可能となります。カーネルは/procのようにマウントまで行っていません。マウントはユーザランドの操作となり、従ってシステム依存になります。tools/perf/util/debugfs.cにデフォルトで
static const char *debugfs_known_mountpoints[] = {
"/sys/kernel/debug/",
"/debug/",
0,
};とされていて、通常は/sys/kernel/debugにマウントされています。
サンプルです。
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
static struct dentry *hogehoge_root;
static char hoge_buff[128];
static ssize_t
debugfs_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
return simple_read_from_buffer(buf, len, ppos, hoge_buff, strlen(hoge_buff));
}
static ssize_t
debugfs_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
return simple_write_to_buffer(hoge_buff, sizeof(hoge_buff), ppos, buf, len);
}
static struct file_operations hoge_fops = {
.owner = THIS_MODULE,
.read = debugfs_read,
.write = debugfs_write,
};
static int debugfs_init_module(void)
{
hogehoge_root = debugfs_create_dir("a-hogehoge", NULL);
if (!hogehoge_root)
return -ENOMEM;
if (!debugfs_create_file("test", 0444,
hogehoge_root, NULL, &hoge_fops))
return -ENOMEM;
return 0;
}
static void debugfs_exit_module(void)
{
debugfs_remove_recursive(hogehoge_root);
}
module_init(debugfs_init_module);
module_exit(debugfs_exit_module);
実効結果[root@localhost lkm]# insmod debugfs.ko [root@localhost lkm]# ls /sys/kernel/debug/ a-hogehoge boot_params extfrag kprobes sched_features usb wakeup_sources acpi cxgb4 gpio mce suspend_stats virtio-ports x86 bdi dynamic_debug hid regmap tracing vmmemctl [root@localhost lkm]# ls /sys/kernel/debug/a-hogehoge/ test [root@localhost lkm]# echo "abcde" > /sys/kernel/debug/a-hogehoge/test [root@localhost lkm]# cat /sys/kernel/debug/a-hogehoge/test abcdedebugfs_create_dir()は、debugfsにディレクトリを作成します。parentは作成する親ディレクトリです。NULLの場合、debugfsのroot(/sys/kernel/debug)に作成されます。
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
{
return debugfs_create_file(name,
S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
parent, NULL, NULL);
}
debugfs_create_file()は、ディレクトリ/ファイル/リンク作成のすべてにおいて、modeを該当値に設定することでコールされます。simple_pin_fs()はdebugfsのmnt構造体の参照カウンターをインクリメントし、debugfs_create_by_name()で対応するinodeおよびdentryを取得します。
struct dentry *debugfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops)
{
struct dentry *dentry = NULL;
int error;
pr_debug("debugfs: creating file '%s'\n",name);
error = simple_pin_fs(&debug_fs_type, &debugfs_mount,
&debugfs_mount_count);
if (error)
goto exit;
error = debugfs_create_by_name(name, mode, parent, &dentry,
data, fops);
if (error) {
dentry = NULL;
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
goto exit;
}
exit:
return dentry;
}
debugfs_create_by_name()でmodeに対応するdentryを取得します。
static int debugfs_create_by_name(const char *name, umode_t mode,
struct dentry *parent,
struct dentry **dentry,
void *data,
const struct file_operations *fops)
{
int error = 0;
if (!parent)
parent = debugfs_mount->mnt_root;
*dentry = NULL;
mutex_lock(&parent->d_inode->i_mutex);
*dentry = lookup_one_len(name, parent, strlen(name));
if (!IS_ERR(*dentry)) {
switch (mode & S_IFMT) {
case S_IFDIR:
error = debugfs_mkdir(parent->d_inode, *dentry, mode,
data, fops);
break;
case S_IFLNK:
error = debugfs_link(parent->d_inode, *dentry, mode,
data, fops);
break;
default:
error = debugfs_create(parent->d_inode, *dentry, mode,
data, fops);
break;
}
dput(*dentry);
} else
error = PTR_ERR(*dentry);
mutex_unlock(&parent->d_inode->i_mutex);
return error;
}
debugfs_get_inode()は、debugfs_mkdir()/debugfs_link()/debugfs_create()でinodeを取得する時にコールされます。この時ファイルの場合、inode->i_fop = fops ? fops : &debugfs_file_operationsとしています。debugfs_file_operationsは、何もしない関数です。
static struct inode *debugfs_get_inode(struct super_block *sb, umode_t mode, dev_t dev,
void *data, const struct file_operations *fops)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = fops ? fops : &debugfs_file_operations;
inode->i_private = data;
break;
case S_IFLNK:
inode->i_op = &debugfs_link_operations;
inode->i_fop = fops;
inode->i_private = data;
break;
case S_IFDIR:
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = fops ? fops : &simple_dir_operations;
inode->i_private = data;
/* directory inodes start off with i_nlink == 2
* (for "." entry) */
inc_nlink(inode);
break;
}
}
return inode;
}
ファイル毎のファイルオペレーションを定義していない場合、以下のコールバックが設定されます。
const struct file_operations debugfs_file_operations = {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
.llseek = noop_llseek,
};
static ssize_t default_read_file(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t default_write_file(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return count;
}
static int default_open(struct inode *inode, struct file *file)
{
if (inode->i_private)
file->private_data = inode->i_private;
return 0;
}
補足
kprobeでのdebugfsの利用例です。kprobeのkprobeサンプルをinsmodした時の内容です。[root@localhost kprobes]# pwd /sys/kernel/debug/kprobes [root@localhost kprobes]# ls enabled list [root@localhost kprobes]# cat list c0436270 k do_fork+0x0
実装(kprobes)
debugfs_kprobe_init()は、モジュール初期化時にコールされ(late_initcallマクロで宣言)、最初にdebugfs_create_dir()で、/sys/kernel/debug下にkprobesが作成され、debugfs_create_fill()で、その下にlistとenableのファイルが作成されます。そのファイルの参照は、debugfs_kprobes_operations/fops_kpオペレーションに記述する事で、kprobeでの内部情報をユーザプロセスとやり取りする事が可能となります。
static const struct file_operations debugfs_kprobes_operations = {
.open = kprobes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int __kprobes debugfs_kprobe_init(void)
{
struct dentry *dir, *file;
unsigned int value = 1;
dir = debugfs_create_dir("kprobes", NULL);
if (!dir)
return -ENOMEM;
file = debugfs_create_file("list", 0444, dir, NULL,
&debugfs_kprobes_operations);
if (!file) {
debugfs_remove(dir);
return -ENOMEM;
}
file = debugfs_create_file("enabled", 0600, dir,
&value, &fops_kp);
if (!file) {
debugfs_remove(dir);
return -ENOMEM;
}
return 0;
}
late_initcall(debugfs_kprobe_init);
ちなみに、listの内容は以下の様に、kproobeのアドレス、kprobeタイプ(r:kretprobe j:kjprobe k:kprobe)、kprobeのシンボル+オフセット 設定モジュール)となっています。[root@localhost kprobes]# cat list c0436270 k do_fork+0x0
static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
const char *sym, int offset, char *modname, struct kprobe *pp)
{
char *kprobe_type;
if (p->pre_handler == pre_handler_kretprobe)
kprobe_type = "r";
else if (p->pre_handler == setjmp_pre_handler)
kprobe_type = "j";
else
kprobe_type = "k";
if (sym)
seq_printf(pi, "%p %s %s+0x%x %s ",
p->addr, kprobe_type, sym, offset,
(modname ? modname : " "));
else
seq_printf(pi, "%p %s %p ",
p->addr, kprobe_type, p->addr);
if (!pp)
pp = p;
seq_printf(pi, "%s%s%s\n",
(kprobe_gone(p) ? "[GONE]" : ""),
((kprobe_disabled(p) && !kprobe_gone(p)) ? "[DISABLED]" : ""),
(kprobe_optimized(pp) ? "[OPTIMIZED]" : ""));
}
module_init/late_initcallマクロは、関数を.initcallセクションに配置するものですが、module_initマクロは6、late_initcallマクロは7と言う具合に、そのセクション内で順位を有しています。従って、late_initcallマクロで宣言された関数は、module_initマクロで宣言された関数の後にコールされる事になります。
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn






