第6章 カーネル探査

目次

6.1. サポートされるアーキテクチャ
6.2. カーネル探査の種類
6.3. カーネルの探査 API
6.4. Debugfs インターフェイス
6.5. さらなる情報

カーネルの探査とは、 Linux カーネルのデバッグや性能に関する情報を集めるための、 一連のツール集のことを指します。開発者やシステム管理者は通常、これらを使用して カーネルのデバッグを行なったり、システム性能のボトルネックを発見しようとしたり します。ここから報告されたデータは、システムの性能を改善する目的で使用します。

探査はカーネルのルーチン (一連の動作) の中に組み込むことができ、特定のポイントに 到達した時に実行されるハンドラを設定することができます。カーネル探査の主な利点と しては、カーネルの再構築を行なったりする必要がなくなるほか、探査項目に変更を加える 際でも再起動を必要としない点が挙げられます。

カーネル探査を使用するには、通常は特定のカーネルモジュールを作成したり取得したり する必要があります。このようなモジュールには init (初期化) と exit (終了) の各関数が含まれています。 init 関数 (register_kprobe() など) では 1 つまたは複数の探査を 登録し、 exit 関数ではこれらの登録を解除します。登録関数では、探査を挿入する 場所 を指定するほか、探査に該当した場合に、 どのハンドラ を呼び出すのかを指定します。一括で複数の探査を 登録または登録解除するには、 register_<探査の種類>probes()unregister_<探査の種類>probes() を使用することができます。

デバッグメッセージと状態を表わすメッセージは、一般に printk カーネルルーチンで実施します。 printk はユーザスペースで 言うところの printf と同じルーチンです。 printk について、詳しくは Logging kernel messages (英語) をお読みください。通常であれば、これらのメッセージは /var/log/messages/var/log/syslog を参照することで、確認することができます。詳しくは 第4章 システムログファイルの分析と管理 をお読みください。

6.1. サポートされるアーキテクチャ

カーネル探査は下記のアーキテクチャで 完全に 実装されています:

  • i386

  • x86_64 (AMD-64, EM64T)

  • ppc64

  • arm

  • ppc

カーネル探査は下記のアーキテクチャで 部分的に 実装されています:

  • ia64 (slot1 命令に対する探査には対応 していません)

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

6.2. カーネル探査の種類

カーネル探査には 3 つの種類があります: Kprobes, Jprobes, Kretprobes 。 kretprobes とは return probe とも呼ばれます。 これら 3 種類の探査のソースコード例は、 /usr/src/linux/samples/kprobes/ 内 (kernel-source パッケージ) に存在しています。

6.2.1. Kprobe

Kprobe は Linux カーネル内の任意の命令に対して設定することができるもの です。 Kprobe を特定の命令に対して設定すると、その命令が書かれている 最初の場所に対してブレイクポイントが設定されます。プロセッサが ブレイクポイントに到達すると、プロセッサのレジスタ情報が保存されたあと 処理が Kprobe に渡されます。最初に プリハンドラ (事前のハンドラ) が実行され、そのあと対象の命令が一つずつ実行され、 最後に ポストハンドラ (事後のハンドラ) が実行されます。 これら一連の流れが終わると、制御は Kprobe で保存しておいたアドレスに戻ります。

6.2.2. Jprobe

Jprobe は Kprobe の仕組みを通して実装されているものです。これは関数の 開始点 (エントリポイント) に対して設定されるもので、対象の関数に対する パラメータについて、直接的なアクセスを行なうことができるようになっています。 このハンドラルーチンは、関数と同じパラメータ一覧および返り値を設定しなければ なりません。また、 jprobe_return() を呼び出すことで 終了しなければなりません。

Jprobe での探査に引っかかると、プロセッサのレジスタ情報が保存された あと、命令のポインタが Jprobe のハンドラルーチン側に向けられます。その後、 制御は関数が呼び出されたときと同じレジスタ状態で、ハンドラを実行します。 最後にハンドラは jprobe_return() 関数を呼び出し、 元の関数に戻ります。

一般的には 1 つの関数に複数の探査を挿入することが可能ですが、 Jprobe では 1 つの関数あたり 1 つのインスタンスしか設定できません。

6.2.3. Return probe

Return probeについても同様に、 KProbe の仕組みを通して実装されています。 register_kretprobe() 関数が呼び出されると、 KProbe が指定の関数の開始点に設定されます。 探査に引っかかると、カーネルの探査機構が対象の関数の戻りアドレスを保存し、 あらかじめ設定しておいたハンドラを呼び出します。これら一連の流れが終わると、 制御は Return probe で保存しておいたアドレスに戻ります。

なお register_kretprobe() を呼び出す前に、 maxactive パラメータを設定する必要があります。 これは同時にどれだけのインスタンス数を探査するかを指定します。 この値が小さすぎると、探査を見逃してしまう場合があります。

6.3. カーネルの探査 API

Kprobe のプログラミングインターフェイスは複数の関数から構成されていて、これらは 使用する全てのカーネル探査に対して、登録や登録解除を行なうことができるほか、 ハンドラを関連づけたりすることができます。これらの関数について、詳しい説明や それらのパラメータについては、 6.5項 「さらなる情報」 に示す情報を参照してください。

register_kprobe()

指定したアドレスにブレイクポイントを設定します。ブレイクポイントに到達すると、 pre_handlerpost_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()

一時的に無効化されていた探査を有効にします。

6.4. Debugfs インターフェイス

最近の Linux カーネルであれば、カーネル探査の命令はカーネル内にある debugfs インターフェイスを使用します。これにより、全ての登録済み探査を一覧表示できる ほか、全ての探査に対して有効や無効を切り替えることができます。

6.4.1. 登録済みのカーネル探査を一覧表示する方法

現在登録されている全ての Kprobe を表示するには、 /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] というマークが併記されます。

6.4.2. 全てのカーネル探査を有効または無効にする方法

/sys/kernel/debug/kprobes/enabled ファイルには、 登録済みの全てのカーネル探査について、有効または無効に切り替えることのできる 機能が用意されています。全てのカーネル探査を無効にするには、 root で 下記のように実行します:

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

全てのカーネル探査を有効に戻すには、同様に root で下記のように実行します:

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

なお、この方法では探査の状態を変更することはできません。特定の探査が一時的に 無効化されている場合、上記のコマンドを実行しても [DISABLED] の状態のままになります。

6.5. さらなる情報

カーネル探査についてより詳しく知るには、下記の情報源をご利用ください:

  • カーネル探査に関して、完全かつ技術的な情報を参照したい場合は、 /usr/src/linux/Documentation/kprobes.txt ファイル (kenrel-source パッケージ内) をお読みください。

  • 3 種類の探査についてのサンプル (および関連する Makefile) をお読みになりたい場合は、 /usr/src/linux/samples/kprobes/ ディレクトリ (kenrel-source パッケージ内) をご覧ください。

  • Linux カーネルモジュールや printk カーネルルーチンに ついて詳しい情報を得るには、 The Linux Kernel Module Programming Guide (英語) をお読みください。

  • カーネル探査を実際に使用するにあたって、現実的ではありますが少し古い情報を得たい 場合は、 Kernel debugging with Kprobes (英語) をお読みください。


openSUSE システム分析とチューニングガイド 13.1