カーネルプローブは Linux カーネルのデバッグや性能に関する情報を収集するためのツール集です。開発者やシステム管理者は、これらを利用してカーネルのデバッグを行ったり、システムにおける性能のボトルネックを発見したりすることができます。また、収集したデータをシステムの性能改善のために使用することもできます。
カーネルプローブは任意のカーネルルーチン内に設定し、カーネル処理内の特定の箇所 (ブレークポイント) に到達した後に、指定したハンドラを実行することができるようになります。カーネルプローブの主な利点としては、カーネルの再構築が不要であることと、プローブの変更後にシステムの再起動が不要になっている点です。
カーネルプローブを使用するには、まずは必要なカーネルモジュールを作成する必要があります。このモジュール内には 初期化 と 終了 の各関数を用意する必要があります。初期化の関数 (たとえば register_kprobe()
) では 1 つ以上のプローブを設定し、終了の関数ではそれらの登録を解除します。登録処理ではカーネル内の どこ にプローブを挿入するかと、そのプローブに到達した際に どのようなハンドラ を実行するのかを定義します。複数のプローブを一括で登録もしくは登録解除するには、対応する register_<プローブの種類>probes()
や unregister_<プローブの種類>probes()
を使用する必要があります。
デバッグや状態を表すメッセージは一般に、 printk
カーネルルーチンを利用して行います。 printk
はユーザスペースでの printf
と等価なカーネルルーチンです。 printk
に関する詳細については、 Logging kernel messages (英語) をお読みください。通常、このカーネルルーチンで出力されたメッセージは、 systemd
ジャーナルで読むことができます (詳しくは 第11章 「journalctl
: systemd
ジャーナルへの問い合わせコマンド」 をお読みください) 。なお、ログファイルに関する詳細については、 第3章 「システムログファイル」 をお読みください。
カーネルプローブは、下記のアーキテクチャ向けに 完全 実装されています:
x86
AMD64/Intel 64
Arm
POWER
カーネルプローブは、下記のアーキテクチャ向けに 部分 実装されています:
IA64 (インストラクション slot1
に対するプローブには対応していません)
sparc64 (return probe が実装されていません)
カーネルプローブには 3 種類のものがあります。具体的には Kprobes , Jprobes , Kretprobes の 3 種類です。 Kretprobes は return probes (返りプローブ) とも呼ばれます。 3 種類のプローブの例については、 Linux カーネルのソースコードをお読みください。 /usr/src/linux/samples/kprobes/
ディレクトリ内 (kernel-source
パッケージ内) にあります。
Kprobes は Linux カーネル内の任意のインストラクション (命令) に接続することができる仕組みです。 Kprobes を登録すると、対象となるインストラクションの最初のバイト位置にブレークポイントが設定されます。プロセッサがそのブレークポイントに到達すると、プロセッサレジスタを保存して、処理を Kprobes に渡します。その後 pre-handler (事前ハンドラ) を実行し、対象のインストラクションをステップ実行したのち、最後に post-handler (事後ハンドラ) を実行します。あとはプローブポイント後の位置に制御が移り、元の動作に戻ることになります。
Jprobes は Kprobes の仕組みを利用して実装されているものです。関数の開始位置に挿入され、プローブ対象の関数のパラメータに対して、直接アクセスできるようになります。ハンドラルーチンにはプローブ対象の関数と全く同じパラメータリストを指定しなければならず、かつ返り値も同じ型でなければなりません。これを終了するには、 jprobe_return()
関数を呼び出します。
JProbes が設定された関数を到達すると、プロセッサレジスタを保存したあと、インストラクションポインタは JProbes のハンドラルーチンに転送されます。その後、制御は ハンドラ内に移り、プローブ対象の関数と同じレジスタ状態になります。最後に、ハンドラは jprobe_return()
を呼び出し、制御が元の関数に戻るようになります。
一般的には、 1 つの関数に対して複数のプローブを設定することができますが、 Jprobes については 1 つの関数に対して 1 つのインスタンスのみに制限されます。
Return Probe も Kprobes を介して実装されています。 register_kretprobe()
を呼び出すと、 Kprobes はプローブ対象の関数の開始位置に設定されます。 プローブ位置に到達すると、カーネルはプローブ対象の関数の返りアドレスを保存して、ユーザが指定した返りハンドラを呼び出します。あとは制御が元の関数に戻ります。
register_kretprobe()
を呼び出す場合は、 maxactive
パラメータを指定する必要があります。これはこの関数を同時にいくつまでプローブするのかを指定するパラメータで、値が小さすぎるとプローブが失敗することがあります。
Kprobes のプログラミングインターフェイスは、使用するカーネルプローブと対応するプローブハンドラの登録と登録解除に使用する関数から構成されています。これらの関数とパラメータに対する詳しい説明については、 5.5項 「さらなる情報」 にある情報源を参照してください。
register_kprobe()
指定したアドレスにブレークポイントを挿入します。ブレークポイントに到達すると、 pre_handler
(事前ハンドラ) と post_handler
(事後ハンドラ) がそれぞれ呼び出されます。
register_jprobe()
指定したアドレスにブレークポイントを挿入します。アドレスは、プローブ対象となる関数内で最初のインストラクションのアドレスである必要があります。ブレークポイントに到達すると、指定したハンドラを実行します。ハンドラはプローブ対象の関数と同じパラメータリストでなければならず、かつ返り値も同じデータ型でなければなりません。
register_kretprobe()
指定した関数に return probe を挿入します。プローブ対象の関数が終了して値を返す際、指定したハンドラが呼び出されます。この関数は 0 を返すと成功の意味に、負の数のエラー番号である場合は失敗の意味になります。
unregister_kprobe()
, unregister_jprobe()
, unregister_kretprobe()
指定したプローブを削除します。これらの関数は、プローブの登録後であれば任意のタイミングで使用することができます。
register_kprobes()
, register_jprobes()
, register_kretprobes()
指定した配列内にあるそれぞれのプローブを挿入します。
unregister_kprobes()
, unregister_jprobes()
, unregister_kretprobes()
指定した配列内にあるそれぞれのプローブを削除します。
disable_kprobe()
, disable_jprobe()
, disable_kretprobe()
指定したプローブを一時的に無効化します。
enable_kprobe()
, enable_jprobe()
, enable_kretprobe()
指定したプローブを一時的に有効化します。
debugfs
インターフェイス #Edit source新しい Linux カーネルでは、 Kprobes はカーネルの debugfs
インターフェイスを利用して制御することができます。ここでは登録済みのプローブの一覧や、全てのプローブの一括有効化/無効化を行うことができます。
現時点で登録されている全てのプローブの一覧は、 /sys/kernel/debug/kprobes/list
内に書かれています。
saturn.example.com:~ # cat /sys/kernel/debug/kprobes/list c015d71a k vfs_read+0x0 [DISABLED] c011a316 j do_fork+0x0 c03dedc5 r tcp_v4_rcv+0x0
左から最初の列には、プローブを挿入したカーネル内のアドレスが書かれています。 2 番目の列にはプローブの種類が書かれています (k
(kprobe), j
(jprobe), r
(return probe)) 。 3 番目の列はシンボルとオフセット、そしてプローブのモジュール名 (もしあれば) が書かれています。また、それ以降の列は必要に応じて現れる箇所で、プローブの状態が書かれています。挿入済みのプローブが無効なアドレスを指している場合は [GONE]
、対象のプローブが一時的に無効化されて居る場合は [DISABLED]
と書かれます。
/sys/kernel/debug/kprobes/enabled
ファイルは、登録済みの全てのカーネルプローブに対して、一括でかつ強制的に有効化および無効化を行うスイッチになっています。全てのカーネルプローブを無効化したい場合は、 root
で下記のように入力して実行します:
#
echo "0" > /sys/kernel/debug/kprobes/enabled
再度有効化したい場合は、 root
で下記のように入力して実行します:
#
echo "1" > /sys/kernel/debug/kprobes/enabled
なお、この方法ではプローブの状態変更を行うことはできません。特定のプローブが一時的に無効化されている場合、後者のコマンドを入力して実行しても、 [DISABLED]
(無効) のままになります。
カーネルプローブに関する詳細については、下記の情報源をご覧ください:
カーネルプローブに関する技術寄りの情報については、 /usr/src/linux/Documentation/trace/kprobes.txt
ファイル (kernel-source
パッケージ内に含まれています) をお読みください。
3 種類のプローブに関する例 (および対応する Makefile
) については、 /usr/src/linux/samples/kprobes/
ディレクトリ内 ( kernel-source
パッケージ内に含まれています) をお読みください。
Linux のカーネルモジュールや printk
カーネルルーチンに関する詳細については、 The Linux Kernel Module Programming Guide (英語) 内に説明が書かれています。