EN JA
GDB(4)
GDB(4) FreeBSD Kernel Interfaces Manual GDB(4)

名称

gdb外部カーネルデバッガ

書式

makeoptions DEBUG=-g
options DDB

解説

gdb カーネルデバッガは、 FreeBSD カーネル環境のいくつかの側面を理解している gdb(1) の変形物です。それは、多くの方法で使用することができます:
  • 実行されるプロセッサのメモリを調べるために使用することができます。
  • パニックの後でプロセッサダンプを解析するために使用することができます。
  • シリアルかファイヤワイヤリンクを通してインタラクティブに他のシステムをデバッグするために使用することができます。訳注: ファイヤワイヤは、アップルが開発した高速シリアルデータ転送技術。このモードで、プロセッサを停止したりシングルステップを行うことができます。
  • firewire リンクと共に、そのシステムの参加しないでリモートシステムのメモリを調べるためにそれを使用することができます。このモードでは、プロセッサは、停止もシングルステップもできませんが、リモートシステムがクラッシュして、もう反応していないときでも、使用することができます。

リモートデバッグに使用されるとき、 gdb は、 ddb(4) カーネルデバッガの存在を要求します。コマンドは、 gdbddb(4) を切り換えるために存在しています。

デバッグの準備

カーネルをデバッグするとき、デバッグシンボル ( makeoptions DEBUG=-g) でカーネルを構築することが実際に不可欠です。カーネル構築ディレクトリ、デフォルトで /usr/obj/usr/src/sys/GENERIC、から操作を実行することは、最も簡単です。

最初に、ディレクトリのデバッグマクロを確実にコピーしてください:

make gdbinit

このコマンドは、それらをローカル環境に適合させるために /usr/src/tools/debugscripts にインストールされたマクロでいくつかの変換を実行します。

ローカルマシンの環境を点検

システムのメモリの内容を見て変更するためには、次のように実行します。

gdb -k -wcore kernel.debug /dev/mem

“ダンプファイル” /dev/mem がカーネルデータファイルであることを gdb(1) に示すために -k フラグを必要とします。利用者は、ライブデータを見ることができます、そして、 -wcore オプションを含められるなら、危険を覚悟でそれを変更することができます。システムは、止められないので (明らかに)、多くのものは、動作しないでしょう。ブレークポイントを設定することができますが、“続けて”実行できないので、それらは、動作しないでしょう。

クラッシュダンプをデバッグ

デフォルトで、クラッシュダンプは、ディレクトリ /var/crash に格納されます。次のようにカーネル構築ディレクトリからそれらを調査してください。

gdb -k kernel.debug /var/crash/vmcore.29

このモードでは、システムは、明らかに止められるので、利用者は、単にそれを見ることができます。

リモートリンクで動いているシステムをデバッグ

次の議論では、“ローカルシステム”という用語は、デバッガを実行するシステムのことです、そして、“リモートシステム”は、デバッグされているライブシステムのことです。

リモートリンクでライブシステムをデバッグするためには、オプション options DDB でカーネルをコンパイルしなければなりません。オプション options BREAK_TO_DEBUGGER は、デバッグするマシンを停止することを可能とし、いったん接続されたデバッグされるマシンは、‘ ^C’を押すことによって確立されます。

リモートシリアルリンクで動いているシステムをデバッグ

i386 プラットフォームでリモートリンクにシリアルポートを使用するとき、指定されたインタフェースにフラグビット 0x80 を設定することによって、シリアルポートを識別しなければなりません。また、一般的に、このポートがシリアルコンソール (フラグビット 0x10) として使用されるので、 /boot/device.hints のエントリは、次のようになるべきです:

hint.sio.0.flags="0x90"

リモートファイヤワイヤリンクで動いているシステムをデバッグ

シリアルデバッグと同様に、firewire リンクでライブシステムをデバッグするためには、オプション options DDB でカーネルをコンパイルしなければなりません。

firewire リンクをセットアップするためには、多くのステップを実行しなければなりません:

  • 両方のシステムには、 firewire(4) サポートがあり、リモートシステムのカーネルは、 dcons(4)dcons_crom(4) ドライバを確実に含めてください。それらがカーネル中にコンパイルされていないなら、KLD (カーネルローダブルモジュール) をロードしてください:

    kldload firewire

    リモートシステムだけに関して:

    kldload dcons 
    kldload dcons_crom

    リモートシステムの dmesg(8) 出力において次ように見るべきです:

    fwohci0: BUS reset 
    fwohci0: node_id=0x8800ffc0, gen=2, non CYCLEMASTER mode 
    firewire0: 2 nodes, maxhop <= 1, cable IRM = 1 
    firewire0: bus manager 1 
    firewire0: New S400 device ID:00c04f3226e88061 
    dcons_crom0: <dcons configuration ROM> on firewire0 
    dcons_crom0: bus_addr 0x22a000

    /boot/loader.conf の次のエントリでブート時にこれらのモジュールをロードするのは、良い考えです:

    dcons_crom_enable="YES"

    これは、すべての 3 つのモジュールがロードされることを確実にします。ローカルシステムで dcons(4)dcons_crom(4) のローディングで問題は、ありませんが、 firewire(4) モジュールだけをロードしたいなら /boot/loader.conf に次を含めてください:

    firewire_enable="YES"
  • 次に、リモートマシンに対応している firewire ノードを見つけるためには、 fwcontrol(8) を使用します。ローカルマシン上では、次のように見るかもしれません:

    # fwcontrol 
    2 devices (info_len=2) 
    node        EUI64        status 
       1  0x00c04f3226e88061      0 
       0  0x000199000003622b      1

    最初のノードが常にローカルシステムであるので、この場合、ノード 0 は、リモートシステムです。 2 つ以上のシステムがあれば、リモートシステムに対応するノードを見つけるためにもう一方の終りからチェックします。リモートマシンでは、次のように見えます:

    # fwcontrol 
    2 devices (info_len=2) 
    node        EUI64        status 
       0  0x000199000003622b      0 
       1  0x00c04f3226e88061      1
  • 次に、firewire と dconschat(8) との接続を確立します:

    dconschat -br -G 5556 -t 0x000199000003622b

    0x000199000003622b は、上記の fwcontrol(8) の出力から決定するようなリモートノードの EUI64 アドレスです。このように始められるとき、 dconschat(8) は、ローカルトンネルとリモートデバッガへのポート localhost:5556 からの接続を確立します。また、コンソールポートと dconschat(8) と同じ呼び出しへの -C オプションで接続をに確立することができます。さらに詳しい詳細については、 dconschat(8) マニュアルページを参照してください。

    dconschat(8) ユーティリティは、制御をユーザに返しません。それは、リモートシステムのためのエラーメッセージとコンソール出力を表示するので、それ自身のウィンドウでそれを始めるのは良い考えです。

  • 最終的に、接続を確立します:

    # gdb kernel.debug 
    GNU gdb 5.2.1 (FreeBSD) 
    (political statements omitted) 
    Ready to go.  Enter 'tr' to connect to the remote target 
    with /dev/cuau0, 'tr /dev/cuau1' to connect to a different port 
    or 'trf portno' to connect to the remote target with the firewire 
    interface.  portno defaults to 5556. 
     
    Type 'getsyms' after connection to load kld symbols. 
     
    If you are debugging a local system, you can use 'kldsyms' instead 
    to load the kld symbols.  That is a less obnoxious interface. 
    (gdb) trf 
    0xc21bd378 in ?? ()

    trf マクロは、ポート 5556 で接続を仮定します。異なったポート (上記の dconschat(8) の呼び出しを変更することによって) を使用したいなら、代わりに tr マクロを使用します。例えば、ポート 4711 を使用したなら、次にように dconschat(8) を実行します:

    dconschat -br -G 4711 -t 0x000199000003622b

    そして、次のように接続を確立します:

    (gdb) tr localhost:4711 
    0xc21bd378 in ?? ()

リモートファイヤワイヤリンクで動いているシステムを非協調デバッグ

前のセクションで説明された firewire による従来のデバッグに加えて、初期の接続がいったん確立されると、連携なしでリモートシステムをデバッグすることは可能です。これは、 /dev/mem を使用するローカルマシンのデバッグと対応しています。システムがクラッシュし、デバッガは、もう応じないなら、非常に役に立つ場合があります。そして、このメソッドを使用するためには、リモートシステムの EUI64 ID を上位と下位に半分にするために、それぞれ sysctl(8) 変数 hw.firewire.fwmem.eui64_hihw.firewire.fwmem.eui64_lo 設定します。前の例から、リモートマシンは、次のように示します:

# fwcontrol 
2 devices (info_len=2) 
node        EUI64        status 
   0  0x000199000003622b      0 
   1  0x00c04f3226e88061      1

入力:

# sysctl -w hw.firewire.fwmem.eui64_hi=0x00019900 
hw.firewire.fwmem.eui64_hi: 0 -> 104704 
# sysctl -w hw.firewire.fwmem.eui64_lo=0x0003622b 
hw.firewire.fwmem.eui64_lo: 0 -> 221739

変数は、16 進数で明示的に提示しなければならないことに注意してください。この後、次の入力でリモートマシンの状態を調べることができます:

# gdb -k kernel.debug /dev/fwmem0.0 
GNU gdb 5.2.1 (FreeBSD) 
(messages omitted) 
Reading symbols from /boot/kernel/dcons.ko...done. 
Loaded symbols for /boot/kernel/dcons.ko 
Reading symbols from /boot/kernel/dcons_crom.ko...done. 
Loaded symbols for /boot/kernel/dcons_crom.ko 
#0  sched_switch (td=0xc0922fe0) at /usr/src/sys/kern/sched_4bsd.c:621 
0xc21bd378 in ?? ()

この場合、明示的にシンボルをロードする必要はありません。リモートシステムは、実行し続けます。

コマンド

gdb へのユーザインタフェースは、 gdb(1) を通してであるので、 gdb(1) コマンドも動きます。このセクションは、カーネル構築ディレクトリにインストールされて得られる、カーネルデバッグのための拡張についてのみ論じます。

デバッグ環境

次のマクロは、デバッグ環境を操作します:
ddb
ddb(4) に切り替えます。このコマンドは、リモートデバッグを実行するときだけ重要です。
getsyms
ターゲットマシンのための kldstat 情報を表示して、それを復活して貼るユーザを招待します。 gdb がデータをシェルスクリプトに渡すことを許さないので、これが必要です。それは、リモートデバッグとフラッシュダンプに必要です。ローカルメモリのデバッグのために、代わりに kldsyms を使用します。
kldsyms
デバッグマシンのためにシンボルテーブルを読み込みます。これは、リモートデバッグとフラッシュダンプのために動作しません。代わりに getsyms を使用します。
tr interface
指定されたシリアルまたは firewire インタフェースを通してリモートシステムをデバッグします。
tr0
シリアルインタフェース /dev/cuau0 を通してリモートシステムをデバッグします。
tr1
シリアルインタフェース /dev/cuau1 を通してリモートシステムをデバッグします。
trf
デフォルトポート 5556 の firewire インタフェースを通してリモートシステムをデバッグします。

コマンド tr0, tr1, trf は、 tr を呼び出す便利コマンドです。

現在のプロセス環境

次のマクロは、標準の gdb(1) コマンドより簡単にすることを意図した便利関数です。
f0
スタックフレーム 0 を選択し、アセンブラレベルの詳細を表示します。
f1
スタックフレーム 1 を選択し、アセンブラレベルの詳細を表示します。
f2
スタックフレーム 2 を選択し、アセンブラレベルの詳細を表示します。
f3
スタックフレーム 3 を選択し、アセンブラレベルの詳細を表示します。
f4
スタックフレーム 4 を選択し、アセンブラレベルの詳細を表示します。
f5
スタックフレーム 5 を選択し、アセンブラレベルの詳細を表示します。
xb
現在の ebp 値で始まって、16 進数で 12 ワードを表示します。
xi
現在の eip 値から次の 10 命令をリストします。
xp
レジスタの内容と現在のスタックフレームの最初の 4 つのパラメータを表示します。
xp0
様々な形式で現在のスタックフレームの最初のパラメータを表示します。
xp1
様々な形式で現在のスタックフレームの 2 番目のパラメータを表示します。
xp2
様々な形式で現在のスタックフレームの 3 番目のパラメータを表示します。
xp3
様々な形式で現在のスタックフレームの 4 番目のパラメータを表示します。
xp4
様々な形式で現在のスタックフレームの 5 番目のパラメータを表示します。
xs
スタックの最後の 12 ワードを 16 進数で表示します。
xxp
レジスタの内容と最初の 10 のパラメータを表示します。
z
シングルステップで 1 命令を実行して (呼び出しを越えて) 次の命令を表示します。
zs
シングルステップで 1 命令を実行して (呼び出しを通して) 次の命令を表示します。

他のプロセスを調査

次のマクロは、他のプロセスにアクセスします。 gdb デバッガは、複数のプロセスの概念を理解していないので、それらは、全体の gdb 環境を事実上迂回させます。
btp pid
プロセス pid. のためにバックトレースを表示します。
btpa
システムのすべてのプロセスのためのバックトレースを表示します。
btpp
以前に defproc で選択されたプロセスのためのバックトレースを表示します。
btr ebp
指定された ebp アドレスからのバックトレースを表示します。
defproc pid
このセクションのある他のコマンドのためのプロセスの PID を指定します。
fr frame
以前に defproc で選択されたプロセスのスタックのフレーム frame を表示します。
pcb proc
プロセス proc の何らかの PCB の内容を表示します。

データ構造を調査

利用者は、高々データ構造を見る標準の gdb(1) コマンドを使用することができます。このセクションのマクロは、より読み込み可能な形式でデータを通常表示するか、または構造のそれほど興味のない一部分を省略する便利な関数です。
bp
現在のフレームで変数 bp によって指されたバッファヘッダに関する情報を表示します。
bpd
現在のフレームの bp->data の内容 ( char *) を表示します。
bpl
ローカル変数 bp によって指されたバッファヘッダ ( struct bp) に関する詳細な情報を表示します。
bpp bp
パラメータ bp によって指されたバッファヘッダ ( struct bp) に関する概要情報を表示します。
bx
現在の環境でポインタ bp によって指されたバッファヘッダから多くのフィールドを印刷 (表示) します。
vdev
ローカル変数 vp によって指された vnode のいくつかの情報を表示します。

その他のマクロ

checkmem
変更がないかどうか割り付けられていないメモリをチェックします。これは、カーネルが options DIAGNOSTIC でコンパイルされたことを仮定します。これは、 0xdeadc0de にフリーメモリの内容を設定します。
dmesg
システムメッセージバッファを印刷 (表示) します。これは、 dmesg(8) ユーティリティに対応しています。このマクロは、以前は、 msgbuf と呼ばれていました。それは、シリアルラインで非常に長い時間かかるかもしれません、そして、 gdb で非能率のために firewire かローカルメモリを通してさらに遅くなります。クラッシュダンプまたは firewire を越えてデバッグするとき、メッセージバッファにアクセスするために gdb を始動する必要はありません: 代わりに、次のような適切な変化を使用してください。

dmesg -M /var/crash/vmcore.0 -N kernel.debug 
dmesg -M /dev/fwmem0.0 -N kernel.debug
kldstat
オプションのない kldstat(8) ユーティリティと同等です。
pname
現在のプロセスのコマンド名を印刷 (表示) します。
ps
プロセスの状態を表示します。これは、 ps(1) ユーティリティに概念で対応していますが、外観で対応していません。クラッシュダンプまたは firewire を越えてデバッグするとき、 ps(1) 出力を表示するために gdb を始動する必要はありません: 代わりに、次のような適切な変化を使用してください。

ps -M /var/crash/vmcore.0 -N kernel.debug 
ps -M /dev/fwmem0.0 -N kernel.debug
y
マクロを書くためにクラッジします。訳注: クラッジ (kludge) は、便宜的にトリックを使って問題を一時的に解決すること。マクロを書くとき、それらを gdb のウィンドウに貼って戻すのは、便利です。残念ながら、マクロが既に定義されているなら、 gdb は、次のように問い合わせを要求します。

Redefine foo?

利用者が‘ y’と答えるまであきらめません。このコマンドは、その答えです。もう一度それを取り除くように利用者に指摘する警告メッセージを印刷することを除いて他に何もしません。

作者

このマニュアルページは、 Greg Lehey <grog@FreeBSD.org>によって書かれました。

バグ

gdb(1) デバッガは、カーネルをデバッグするように決して設計されませんでした、そして、それは、非常に良く適合しません。多くの問題が存在しています。

gdb 実装は、非常に効率が悪く、多くの操作が遅いです。

シリアルデバッグは、さらに遅く、競合条件で、9600 bps 以上のリンクで動かすのは、難しくなる場合があります。 firewire 接続には、この問題がありません。

デバッグマクロは、“ただ、増大”しました。一般的に、それらを書いた人は、特定の問題を探している間にそうしただけなので、それらは、十分に一般的でないかもしれません、そして、それらが意図されなかった方法で使用されるとき、それらのの意味を理解しても、それらは、ひどく振る舞うかもしれません。

これらのコマンドの多くは、ia32 アーキテクチャでのみ動作します。

February 8, 2005 FreeBSD