debugfs
Rev.1を表示中。最新版はこちら。
debugfsはextのファイルシステムをデバックするコマンドのことでなく、debug用の特殊ファイルシステムについてです。debugfsはproc/sysファイルのように、ユーザプロセスとカーネル空間をやり取りするファイルシステムで、/sys/kernel/debug下のファイル群となるわけですが、sysfsの機能を使うのでなく、sysfsの/sys/kernel/debugに、debugfsをマウントする事で行っています。そうする事で、一連の処理を統一できる・・・故かと思います。ちなみに、SELinuxのselinuxfsも同じように/sysファイルシステム下にマウントする事で参照できるようになっています。
debugfsのモジュールがロードされると、初期化処理のdebugfs_ini()で、kobject_create_and_add()コールし、kernel_kobj下(/sys/kernel)に、debugホルダを作成、debug_fs_typeのdebugfsをファイルシステムとして登録します。なおこの時点では、まだdebugfsはマウントされていません。
なお、debugfsモジュールのdebugfs_initは、module_initマクロでなく、core_initcallマクロで定義しています。これは初期化起動の順序ずけとなります。
static struct file_system_type debug_fs_type = { .owner = THIS_MODULE, .name = "debugfs", .mount = debug_mount, .kill_sb = kill_litter_super, } static int __init debugfs_init(void) { int retval; debug_kobj = kobject_create_and_add("debug", kernel_kobj); if (!debug_kobj) return -EINVAL; retval = register_filesystem(&debug_fs_type); if (retval) kobject_put(debug_kobj); else debugfs_registered = true; return retval; } core_initcall(debugfs_init); #define core_initcall(fn) __define_initcall("1",fn,1)debugfs_create_file()は、各コンポーネントで、debugfsを利用するための関数です。まず、simple_pin_fs()でdebugfsをマウントします。もしすでにマウントされているなら、mnt->mnt_countをインクリメントした後、引数のnameのホルダを作成します。引数のparentがNULLなら、debugfsマウントのroot直下に作成されます。
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; }以下は、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