[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]  


スタックの検査

ユーザ・プログラムが停止したとき、 まず最初に、 どこで停止したのか、 そして、 どのようにしてそこに到達したのかを知る必要があるでしょう。

ユーザ・プログラムが関数呼び出しを行うたびに、 その呼び出しに関する情報が生成されます。 その情報には、 ユーザ・プログラム内においてその呼び出しが発生した場所、 関数呼び出しの引数、 呼び出された関数内部のローカル変数などが含まれます。 その情報は、 スタック・フレームと呼ばれるデータ・ブロックに保存されます。 スタック・フレームは、 呼び出しスタックと呼ばれるメモリ域に割り当てられます。

ユーザ・プログラムが停止すると、 スタックを検査するGDBコマンドを使用して、 この情報をすべて見ることができます。

GDBは1つのスタック・フレームを選択していて、 多くのGDBコマンドはこの選択されたフレームを暗黙のうちに参照します。 特に、 GDBに対してユーザ・プログラム内部の変数の値を問い合わせると、 GDBは選択されたフレームの内部においてその値を探そうとします。 関心のあるフレームを選択するための特別なGDBコマンドが提供されています。 フレームの選択 を参照してください。

ユーザ・プログラムが停止すると、 GDBはその時点において実行中のフレームを自動的に選択し、 frameコマンド (フレームに関する情報参照) のように、 そのフレームに関する情報を簡潔に表示します。

スタック・フレーム

呼び出しスタックは、 スタック・フレーム、 または短縮してフレームと呼ばれる、 連続した小部分に分割されます。 個々のフレームは、 ある関数に対する1回の呼び出しに関連するデータです。 フレームには、 関数への引数、 関数のローカル変数、 関数の実行アドレスなどの情報が含まれます。

ユーザ・プログラムが起動されたとき、 スタックにはmain関数のフレームが1つ存在するだけです。 これは、 初期フレームまたは「最上位のフレーム」と呼ばれます。 関数が呼び出されるたびに、 新たにフレームが作成されます。 関数が復帰すると、 その関数を呼び出したときに生成されたフレームが取り除かれます。 関数が再帰的に呼び出される場合、 1つの関数に対して多くのフレームが生成されるということもありえます。 実際に実行中の関数に対応するフレームは、 「最下位のフレーム」と呼ばれます。 これは、 存在するすべてのスタック・フレームの中で、 最も新しく作成されたものです。

ユーザ・プログラムの内部においては、 スタック・フレームはアドレスによって識別されます。 スタック・フレームは多くのバイトから構成され、 それぞれがそれ自身のアドレスを持っています。 どのような種類のコンピュータにおいても、 これらのバイトのうちの1つのバイトのアドレスをもって フレームのアドレスとする慣習的な方法が提供されています。 通常、 あるフレーム内部で実行中は、 そのフレームのアドレスがフレーム・ポインタ・レジスタと呼ばれるレジスタに格納されています。

GDBは、 既存のスタック・フレームのすべてに番号を割り当てます。 最下位のフレームは0で、 それを呼び出したフレームは1となります。 以下、 最下位のフレームを起点として、 順番に値を割り当てていきます。 これらの番号はユーザ・プログラム内部には実際には存在しません。 これらの番号は、 GDBコマンドでスタック・フレームを指定することができるように、 GDBによって割り当てられたものです。

コンパイラによっては、 スタック・フレームを使用せずに実行されるように関数をコンパイルする方法を提供しているものもあります (例えば、 gccのオプション

`-fomit-frame-pointer'

を指定すると、 フレームを持たない関数が生成されます)。 これは、 フレームをセットアップする時間を節約するために、 頻繁に利用されるライブラリ関数に対してしばしば適用されます。 これらの関数の呼び出しを処理するためにGDBが提供する機能は限られています。 最下位のフレームの関数呼び出しがスタック・フレームを持たない場合、 GDBは、 あたかもそれが通常どおりに番号0のフレームを持つものとみなして、 関数呼び出しの連鎖を追跡できるようにします。 しかしながら、 最下位以外のスタック位置に存在する、 フレームを持たない関数に対しては、 GDBは特別な処置を取りません。

frame args
frameコマンドによって、 あるスタック・フレームから別のスタック・フレームに移動し、 選択したスタック・フレームを表示させることができます。 argsは、 フレームのアドレスまたはスタック・フレーム番号です。 引数なしで実行すると、 frameコマンドはカレントなスタック・フレームを表示します。
select-frame
select-frameコマンドによって、 フレームを表示することなく、 あるスタック・フレームから別のスタック・フレームに移動することができます。 これは、 frameコマンドから、 表示処理を取り除いたものです。

バックトレース

バックトレースとは、 ユーザ・プログラムが現在いる箇所にどのようにして到達したかを示す要約情報です。 複数のフレームが存在する場合、 1フレームの情報を1行に表示します。 現在実行中のフレーム (番号0のフレーム) を先頭に、 それを呼び出したフレーム (番号1のフレーム) を次行に、 以降、 同様にスタックをさかのぼって情報を表示します。

backtrace
bt
全スタックのバックトレースを表示します。 スタック内のすべてのフレームが、 1行に1フレームずつ表示されます。 システムの割り込み文字 (通常は、Ctrlキーを押しながらCを押す) によって、 いつでもバックトレースを停止することができます。
backtrace n
bt n
引数のないbacktraceコマンドと似ていますが、 最下位のフレームから数えてn個のフレームだけが表示されます。
backtrace -n
bt -n
引数のないbacktraceコマンドと似ていますが、 最上位のフレームから数えてn個のフレームだけが表示されます。

backtraceの別名としては、 ほかにwhereinfo stack (省略形はinfo s) があります。

backtraceコマンドの出力結果の各行に、 フレーム番号と関数名が表示されます。 set print address offコマンドを実行していなければ、 プログラム・カウンタの値も表示されます。 backtraceコマンドの出力結果では、 関数への引数に加えて、 ソース・ファイル名や行番号も表示されます。 プログラム・カウンタが、 行番号で指定される行の最初のコードを指している場合、 その値は省略されます。

以下にbacktraceの例を示します。 これは、 `bt 3'の出力であり、 したがって最下位のフレームから3フレームが表示されています。

#0  m4_traceon (obs=0x24eb0, argc=1, argv=0x2b8c8)
    at builtin.c:993
#1  0x6e38 in expand_macro (sym=0x2b600) at macro.c:242
#2  0x6840 in expand_token (obs=0x0, t=177664, td=0xf7fffb08)
    at macro.c:71
(More stack frames follow...)

番号0のフレームを表示する行の先頭には、 プログラム・カウンタの値がありません。 これは、 builtin.c993行目の最初のコードにおいて ユーザ・プログラムが停止したことを表わしています。

フレームの選択

スタックやユーザ・プログラム内の他のデータを調べるためのほとんどのコマンドは、 それが実行された時点において選択されているスタック・フレーム上で動作します。 以下に、 スタック・フレームを選択するためのコマンドを列挙します。 どのコマンドも、 それによって選択されたスタック・フレームに関する簡単な説明を最後に表示します。

frame n
f n
番号nのフレームを選択します。 最下位の (現在実行中の) フレームが番号0のフレーム、 最下位のフレームを呼び出したフレームが番号1のフレーム、 以下同様となります。 最も大きい番号を持つフレームはmainのフレームです。
frame addr
f addr
アドレスaddrのフレームを選択します。 スタック・フレームの連鎖がバグのために破壊されてしまって、 GDBがすべてのフレームに正しく番号を割り当てられないような場合に、 この方法が役に立ちます。 さらに、 ユーザ・プログラムが複数のスタックを持ち、 スタックの切り替えを行うような場合にも有効です。 SPARCアーキテクチャでは、 フレームを任意に選択するには、 フレーム・ポインタ、 スタック・ポインタの2つのアドレスをframeに指定する必要があります。 MIPS、 Alphaの両アーキテクチャでは、 スタック・ポインタ、 プログラム・カウンタの2つのアドレスが必要です。 29kアーキテクチャでは、 レジスタ・スタック・ポインタ、 プログラム・カウンタ、 メモリ・スタック・ポインタの3つのアドレスが必要です。
up n
スタックをnフレームだけ上へ移動します。 nが正の値の場合、 最上位のフレームに向かって移動します。 これは、 より大きいフレーム番号を持ち、 より長く存在しているフレームへの移動です。 nのデフォルト値は1です。
down n
スタックをnフレームだけ下へ移動します。 nが正の値の場合、 最下位のフレームに向かって移動します。 これは、 より小さいフレーム番号を持ち、 より最近作成されたフレームへの移動です。 nのデフォルト値は1です。 downの省略形はdoです。

これらのコマンドはいずれも、 最後にフレームに関する情報を2行で表示します。 1行めには、 フレーム番号、 関数名、 引数、 ソース・ファイル名、 そのフレーム内において実行停止中の行番号が表示されます。 2行めには、 実行停止中のソース行が表示されます。

以下に、 例を示します。

(gdb) up
#1  0x22f0 in main (argc=1, argv=0xf7fffbf4, env=0xf7fffbfc)
    at env.c:10
10              read_input_file (argv[i]);

この情報が表示された後で、 listコマンドを引数なしで実行すると、 フレーム内で実行停止中の行を中心に10行のソース行が表示されます。 ソース行の表示 を参照してください。

up-silently n
down-silently n
これら2つのコマンドは、 それぞれ、 upコマンド、 downコマンドの変種です。 相違点は、 ここに挙げた2つのコマンドが、 新しいフレームに関する情報を表示することなく実行されるという点にあります。 これらは、 情報の出力が不必要で邪魔ですらある、 GDBのコマンド・スクリプトの中での使用を主に想定したものです。

フレームに関する情報

既に挙げたもの以外にも、 選択されたスタック・フレームに関する情報を表示するコマンドがいくつかあります。

frame
f
このコマンドは、 引数なしで実行されると、 別のフレームを選択するのではなく、 その時点において選択中のフレームに関する簡単な説明を表示します。 このコマンドの省略形はfです。 引数付きの場合、 このコマンドはスタック・フレームを選択するのに使用されます。 フレームの選択 を参照してください。
info frame
info f
このコマンドは、 選択されたスタック・フレームに関する詳細な情報を表示します。 表示される情報には、 以下のようなものがあります。 これらの詳細な情報は、 何か問題が発生して、 スタックの形式が通常の慣習に合致しなくなった場合に、 役に立ちます。
info frame addr
info f addr
アドレスaddrのフレームに関する詳細な情報を、 そのフレームを選択することなく表示します。 このコマンドによって、 その時点において選択されていたフレームとは異なるフレームが選択されてしまうことはありません。 このコマンドでは、 frameコマンドに指定するのと同様のアドレスを (アーキテクチャによっては複数) 指定する必要があります。 フレームの選択 を参照してください。
info args
選択中のフレームの引数を、 1行に1つずつ表示します。
info locals
選択中のフレームのローカル変数を、 1行に1つずつ表示します。 これらはすべて、 選択中のフレームの実行箇所においてアクセス可能な (静的変数または自動変数として宣言された) 変数です。
info catch
選択中のスタック・フレームの実行箇所においてアクティブな状態にある、 すべての例外ハンドラの一覧を表示します。 他の例外ハンドラを参照したい場合は、 関連するフレームに (upコマンド、 downコマンド、 frameコマンドを使用して) 移動してから、 info catchを実行します。 キャッチポイントの設定 を参照してください。


[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]