Kprobesの使い方

Kprobesは、レジスタ情報ダンプなど任意の処理を、アセンブラの命令単位で実行できる機能です。


利用するにはカーネルモジュールを作成する必要はありますが、
カーネルの再ビルド無しで、カーネル内のすべての命令をデバッグできます。




なお以降の説明は、Linux 3.14.51で確認したものです。
また、debugfsのマウントポイントをで略記しています。


◆ 参考: debugfsのマウント方法
(マウントポイント /sys/kernel/debug の場合)

sudo mount -t debugfs none /sys/kernel/debug

カーネルコンフィギュレーション

以下を有効化してください。

  • General setup
    • [*] Kprobes <- 有効化


また、カーネルソース内にKprobesのサンプルコードがあります。
これもカーネルビルド時にビルドされるよう、有効化します。

  • Kernel hacking
    • [*] Sample kernel code <- 有効化
        • Build kprobes examples -- loadable modules only <- 有効化

Kprobesの使い方

Kprobesを使用するために行うことは以下の2つです。
1. kprobe構造体の変数を定義して値を設定
2. 1.で定義した変数をregister_kprobe関数でKprobesの枠組みに登録


1. で以下の情報をkprobe構造体に登録することで、任意の命令で任意の処理を行わせることができます。

  • デバッグ対象の命令
    • 「シンボル名(+オフセット)」か、「アドレス」で指定
  • デバッグ対象の命令に到達した時、呼ばれるハンドラのアドレス
    • 「命令実行前」・「命令実行後」・「fault時」の3つを指定できる


上述したとおり、サンプルコードがありますので、
ここではサンプルコードを参照しながら説明します。


◆ サンプルコードの場所


まず、サンプルコードが何をするものかというと、
システムコール"fork"の処理を行うdo_fork関数の最初の命令をデバッグ対象として、
この命令の実行前・実行後・fault時に、レジスタ情報とアドレスをダンプさせています。


サンプルコードでkprobeの定義と登録を行っている箇所は以下のとおりです。

/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {
	.symbol_name	= "do_fork",
};

static int __init kprobe_init(void)
{
	int ret;
	kp.pre_handler = handler_pre;
	kp.post_handler = handler_post;
	kp.fault_handler = handler_fault;

	ret = register_kprobe(&kp);
	if (ret < 0) {
		printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
		return ret;
	}
	printk(KERN_INFO "Planted kprobe at %p\n", kp.addr);
	return 0;
}


まず、kprobe構造体のグローバル変数kpの定義で、
symbol_nameに初期値として"do_fork"を設定しており、
オフセット(offsetメンバー)には何も設定していないので、
do_fork関数の最初の命令がKprobeの対象となります。


そして、モジュールのinit処理(kprobe_init関数)で、

  • pre_handler : 命令実行前ハンドラ
  • post_handler : 命令実行後ハンドラ
  • fault_handler : faultハンドラ

に関数ポインタを設定しています。


"handler_pre"、"handler_post"、"handler_fault"は関数として
サンプルコード内で定義されています。


サンプルコードでのこれらのハンドラ内の処理は、
どれも「printk()でアドレスとレジスタ情報ダンプ」のみなので、
命令実行前ハンドラの"handler_pre"のみ紹介します。

/* kprobe pre_handler: called just before the probed instruction is executed */
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	printk(KERN_INFO "pre_handler: p->addr = 0x%p, ip = %lx,"
			" flags = 0x%lx\n",
		p->addr, regs->ip, regs->flags);

	/* A dump_stack() here will give a stack backtrace */
	return 0;
}

アーキテクチャ判別のifdefと、x86以外の処理は削除しました


ハンドラには

が引数で渡されます。
サンプルコードでは、これらの情報をダンプしています。


カーネルのビルド時にサンプルコードのカーネルモジュールは、
以下の場所に生成されます。


このカーネルモジュールをinsmodでカーネルにインストールすれば、
今回のKprobesを使用したサンプルの動作を確認できます。

$ sudo insmod kprobe_example.ko