kretprobe
Rev.1を表示中。最新版はこちら。
kretprobeはkprobeで実装されており(kprobeの応用例と言った方が正しいか。)、名称からしてカーネル関数の終了時に呼ばれるが、実は関数の開始時にもコールされる。従って以下のようにftraceのような事ができる。kprobeのpre/postハンドラで記述すれば、簡単にできるのでは、と・・・。実はkretprobe、オーバヘッドを考慮して、preハンドラだけで、Cのプログラミング手法の一つトランポリンでの実装している。サンプルはカーネルソースにあった物を、エラー/コメント等を端折ったもので、関数の実行時間と関数の戻り値を取得する。ftraceみたいなもの。
ソースの概略は説明する程のものでなく、いたってシンプル。struct kretprobe my_kretprobeに、関数開始時/終了時のコールバックを設定っするだけ。必要ならコールバック関数で使うデータを.data_sizeとして設定する。.maxactiveはstruct my_dataのインスタンスの数(kmalocで.maxactive数確保)。ネスティング度合いみたいなものか。kretprobe_exitのmy_kretprobe.nmissedは、ネスティングによるインスタンスが足りなかった場合の、ヒットミス数となる。
#include <linux/kernel.h> #include <linux/module.h> #include <linux/kprobes.h> #include <linux/ktime.h> #include <linux/limits.h> #include <linux/sched.h> static char func_name[NAME_MAX] = "do_fork"; module_param_string(func, func_name, NAME_MAX, S_IRUGO); MODULE_PARM_DESC(func, "Function to kretprobe"); struct my_data { ktime_t entry_stamp; }; static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { struct my_data *data; if (!current->mm) return 1; /* Skip kernel threads */ data = (struct my_data *)ri->data; data->entry_stamp = ktime_get(); return 0; } static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { int retval = regs_return_value(regs); struct my_data *data = (struct my_data *)ri->data; s64 delta; ktime_t now; now = ktime_get(); delta = ktime_to_ns(ktime_sub(now, data->entry_stamp)); printk(KERN_INFO "%s returned %d and took %lld ns to execute\n", func_name, retval, (long long)delta); return 0; } static struct kretprobe my_kretprobe = { .handler = ret_handler, .entry_handler = entry_handler, .data_size = sizeof(struct my_data), .maxactive = 20, }; static int __init kretprobe_init(void) { int ret; my_kretprobe.kp.symbol_name = func_name; ret = register_kretprobe(&my_kretprobe); if (ret < 0) { return -1; } return 0; } static void __exit kretprobe_exit(void) { unregister_kretprobe(&my_kretprobe); printk(KERN_INFO "Missed probing %d instances of %s\n", my_kretprobe.nmissed, my_kretprobe.kp.symbol_name); } module_init(kretprobe_init) module_exit(kretprobe_exit) MODULE_LICENSE("GPL");
実行結果
[root@localhost lkm]# insmod kretprobe_test.ko : [87702.796993] do_fork returned 6539 and took 309105 ns to executereturned 6539はプロセスIDとなる。
[root@localhost lkm]# insmod kretprobe_test.ko func="do_sys_open" [root@localhost lkm]# dmesg : [87601.986395] do_sys_open returned 3 and took 19732 ns to executereturned 3はファイルIDとなる。