Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]
適用先 openSUSE Leap 15.6

5 カーネルプローブ Edit source

カーネルプローブは Linux カーネルのデバッグや性能に関する情報を収集するためのツール集です。開発者やシステム管理者は、これらを利用してカーネルのデバッグを行ったり、システムにおける性能のボトルネックを発見したりすることができます。また、収集したデータをシステムの性能改善のために使用することもできます。

カーネルプローブは任意のカーネルルーチン内に設定し、カーネル処理内の特定の箇所 (ブレークポイント) に到達した後に、指定したハンドラを実行することができるようになります。カーネルプローブの主な利点としては、カーネルの再構築が不要であることと、プローブの変更後にシステムの再起動が不要になっている点です。

カーネルプローブを使用するには、まずは必要なカーネルモジュールを作成する必要があります。このモジュール内には 初期化終了 の各関数を用意する必要があります。初期化の関数 (たとえば register_kprobe()) では 1 つ以上のプローブを設定し、終了の関数ではそれらの登録を解除します。登録処理ではカーネル内の どこ にプローブを挿入するかと、そのプローブに到達した際に どのようなハンドラ を実行するのかを定義します。複数のプローブを一括で登録もしくは登録解除するには、対応する register_<プローブの種類>probes()unregister_<プローブの種類>probes() を使用する必要があります。

デバッグや状態を表すメッセージは一般に、 printk カーネルルーチンを利用して行います。 printk はユーザスペースでの printf と等価なカーネルルーチンです。 printk に関する詳細については、 Logging kernel messages (英語) をお読みください。通常、このカーネルルーチンで出力されたメッセージは、 systemd ジャーナルで読むことができます (詳しくは 第11章 「journalctl : systemd ジャーナルへの問い合わせコマンド をお読みください) 。なお、ログファイルに関する詳細については、 第3章 「システムログファイル をお読みください。

5.1 対応するアーキテクチャ Edit source

カーネルプローブは、下記のアーキテクチャ向けに 完全 実装されています:

  • x86

  • AMD64/Intel 64

  • Arm

  • POWER

カーネルプローブは、下記のアーキテクチャ向けに 部分 実装されています:

  • IA64 (インストラクション slot1 に対するプローブには対応していません)

  • sparc64 (return probe が実装されていません)

5.2 カーネルプローブの種類 Edit source

カーネルプローブには 3 種類のものがあります。具体的には Kprobes , Jprobes , Kretprobes の 3 種類です。 Kretprobes は return probes (返りプローブ) とも呼ばれます。 3 種類のプローブの例については、 Linux カーネルのソースコードをお読みください。 /usr/src/linux/samples/kprobes/ ディレクトリ内 (kernel-source パッケージ内) にあります。

5.2.1 Kprobes Edit source

Kprobes は Linux カーネル内の任意のインストラクション (命令) に接続することができる仕組みです。 Kprobes を登録すると、対象となるインストラクションの最初のバイト位置にブレークポイントが設定されます。プロセッサがそのブレークポイントに到達すると、プロセッサレジスタを保存して、処理を Kprobes に渡します。その後 pre-handler (事前ハンドラ) を実行し、対象のインストラクションをステップ実行したのち、最後に post-handler (事後ハンドラ) を実行します。あとはプローブポイント後の位置に制御が移り、元の動作に戻ることになります。

5.2.2 Jprobes Edit source

Jprobes は Kprobes の仕組みを利用して実装されているものです。関数の開始位置に挿入され、プローブ対象の関数のパラメータに対して、直接アクセスできるようになります。ハンドラルーチンにはプローブ対象の関数と全く同じパラメータリストを指定しなければならず、かつ返り値も同じ型でなければなりません。これを終了するには、 jprobe_return() 関数を呼び出します。

JProbes が設定された関数を到達すると、プロセッサレジスタを保存したあと、インストラクションポインタは JProbes のハンドラルーチンに転送されます。その後、制御は ハンドラ内に移り、プローブ対象の関数と同じレジスタ状態になります。最後に、ハンドラは jprobe_return() を呼び出し、制御が元の関数に戻るようになります。

一般的には、 1 つの関数に対して複数のプローブを設定することができますが、 Jprobes については 1 つの関数に対して 1 つのインスタンスのみに制限されます。

5.2.3 Return Probe Edit source

Return Probe も Kprobes を介して実装されています。 register_kretprobe() を呼び出すと、 Kprobes はプローブ対象の関数の開始位置に設定されます。 プローブ位置に到達すると、カーネルはプローブ対象の関数の返りアドレスを保存して、ユーザが指定した返りハンドラを呼び出します。あとは制御が元の関数に戻ります。

register_kretprobe() を呼び出す場合は、 maxactive パラメータを指定する必要があります。これはこの関数を同時にいくつまでプローブするのかを指定するパラメータで、値が小さすぎるとプローブが失敗することがあります。

5.3 Kprobes API Edit source

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()

指定したプローブを一時的に有効化します。

5.4 debugfs インターフェイス Edit source

新しい Linux カーネルでは、 Kprobes はカーネルの debugfs インターフェイスを利用して制御することができます。ここでは登録済みのプローブの一覧や、全てのプローブの一括有効化/無効化を行うことができます。

5.4.1 登録済みのカーネルプローブの一覧表示 Edit source

現時点で登録されている全てのプローブの一覧は、 /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] と書かれます。

5.4.2 指定したプローブの全体的な有効化/無効化 Edit source

/sys/kernel/debug/kprobes/enabled ファイルは、登録済みの全てのカーネルプローブに対して、一括でかつ強制的に有効化および無効化を行うスイッチになっています。全てのカーネルプローブを無効化したい場合は、 root で下記のように入力して実行します:

# echo "0" > /sys/kernel/debug/kprobes/enabled

再度有効化したい場合は、 root で下記のように入力して実行します:

# echo "1" > /sys/kernel/debug/kprobes/enabled

なお、この方法ではプローブの状態変更を行うことはできません。特定のプローブが一時的に無効化されている場合、後者のコマンドを入力して実行しても、 [DISABLED] (無効) のままになります。

5.5 さらなる情報 Edit source

カーネルプローブに関する詳細については、下記の情報源をご覧ください:

  • カーネルプローブに関する技術寄りの情報については、 /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 (英語) 内に説明が書かれています。

このページを印刷