kretprobe
Rev.3を表示中。最新版はこちら。
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は、ネスティングによるインスタンスが足りなかった場合の、ヒットミス数となる。
entry_handlerでカーネルスレッドの場合、何故かスキップしている。自分自身を自分自身で測るようなもので、タイマ更新にかかるスレッドを考慮してか?
#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 [root@localhost lkm]# dmesg : [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となる。






