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 execute
returned 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 execute
returned 3はファイルIDとなる。


最終更新 2012/07/19 19:49:37 - north
(2012/07/19 19:44:42 作成)


検索

アクセス数
2787277
最近のコメント
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
Adsense
広告情報が設定されていません。