web-dev-qa-db-ja.com

gdbを使用してC ++で現在の例外の値とタイプを取得するにはどうすればよいですか?

gdbを使用すると、例外がスローされたとき、および例外がキャッチされたときに例外をキャッチできます。ただし、例外がスローされた行に記号がない場合や、例外処理中にブレークポイントがトリガーされる場合があります。現在の例外の値を検査するにはどうすればよいですか?

30
Matt Joiner

以前の回答は(2013年に)書かれたときは正しかったが、それ以来、gdbとlibstdc ++が変更された。

libstdc ++に、gdbが例外システムとより適切に相互作用できるようにするフックがいくつか追加されました。特に、ユーザーに$_exceptionコンビニエンス変数を提供するのに十分な情報がgdbに公開されるようになりました。この変数は、スローされる例外を保持します。例外がキャッチされている正確な場所でのみ有効です。 catch catchの使用を停止できます。

詳細については、 マニュアルのページ を参照してください。

7
Tom Tromey

更新


これがGDBマニュアルからの情報です

現在、gdbでのC++例外処理(キャッチスローとキャッチキャッチ)にはいくつかの制限があります。

関数をインタラクティブに呼び出すと、gdbは通常、関数の実行が終了したときに制御を返します。ただし、呼び出しで例外が発生した場合、呼び出しは制御を返すメカニズムをバイパスし、プログラムを中止するか、ブレークポイントに到達するか、gdbがリッスンしている信号をキャッチするか、終了するまで実行を継続する可能性があります。これは、例外のキャッチポイントを設定した場合でも当てはまります。例外のキャッチポイントは、対話型呼び出し内では無効になっています。インタラクティブに例外を発生させることはできません。例外ハンドラをインタラクティブにインストールすることはできません。例外処理をデバッグするのにcatchが最善の方法ではない場合があります。例外が発生する場所を正確に知る必要がある場合は、例外ハンドラーが呼び出される前に停止することをお勧めします。そうすれば、巻き戻しが行われる前にスタックを確認できます。代わりに、例外ハンドラーにブレークポイントを設定すると、例外が発生した場所を簡単に見つけることができない場合があります。

例外ハンドラーが呼び出される直前に停止するには、実装に関する知識が必要です。 gnu C++の場合、例外は、次のANSICインターフェイスを持つ__raise_exceptionという名前のライブラリ関数を呼び出すことによって発生します。

_     /* addr is where the exception identifier is stored.
        id is the exception identifier.  */
     void __raise_exception (void **addr, void *id); To make the debugger catch all exceptions before any stack unwinding takes place,
_

__raise_exceptionにブレークポイントを設定します(ブレークポイント、ウォッチポイント、および例外を参照)。


とはいえ

コードとスタックのどこにいるかによって異なります。次のように実際に例外をキャッチした場合:

_try { .... } catch (std::exception &e) {
   //do stuff
}
_

おそらくe.what()を出力するか、例外のメンバーを調べることができます。あなたがそれを(...)として捕まえただけなら、あなたが何を集めることができるかわかりません。

フロー全体を本当に追跡したい場合は、gdbで「throw」をキャッチし、「catch」もキャッチすることもできます。

_gdb> catch catch  
gdb> catch throw
_

このようにして、例外がスローされる直前とキャッチされた直後にブレークポイントを取得し、スタックをウォークして何が起こっているかについての詳細情報を取得できます。別のブレークポイントにいる場合でも、(上または下を使用して)スタックを上に移動して、例外が表示されているフレームを取得できるはずです。

8
UpAndAdam

簡単な答え:例外処理のほとんどの作業はプログラムの外部で行われるため、gdbの範囲外であるためできません。

説明された答え:

例外がスローされる行に記号がない場合があります

デバッグしているバイナリにデバッグシンボルがない場合、バイナリはおそらく削除されており、何かのタイプ/値についてはほとんど見つけることができません。

現在の例外の値を検査するにはどうすればよいですか?

ここでは、例外はgdbが検査できる言語機能であると想定していると思います。実際、C++の例外は、言語としてのC++の機能、libc ++とABIの機能の組み合わせです。また、アクティブな現在の例外が複数存在する場合もあります。

UpAndAdamが指摘しているように、型指定子を使用してcatchブロックにブレークポイントを設定し、その要素を検査できますが、問題は「catch(...)」が見つかった場合にあると思われます。そのような場合、例外処理の実装を掘り下げない限り、現在の例外について多くを学ぶことはできません。

非常に短く不完全な説明で、例外をスローすると言うことができます。

  1. プログラムはlibc ++を呼び出して例外を発生させます
  2. libc ++はglibcで「unwind」を呼び出して、スタックの巻き戻しを開始します
  3. unwindは、スタックフレームごとにlibc ++から「パーソナリティ関数」をコールバックします(基本的に、スタック内の各関数呼び出し)
  4. パーソナリティ関数は、現在のスタックフレームがこの例外を処理できるかどうかを何らかの方法で決定します
  5. 例外を処理できる場合は、catchブロックが実行されます

例外処理の多くはツールチェーン(コンパイラ、プラットフォーム、アーキテクチャ、libc ++など)に依存するため、詳細について話すのは難しいですが、ほとんどの場合、「キャッチ(...)」は元の例外を受け取りません。引数として。いずれにせよ、どういうわけかあなたの質問に答えるために:gnuのlibc ++を備えたgccでは、次のようなことを試すことができます:

  1. デバッグシンボルを含むlibc ++を取得する
  2. __gxx_personality_v0にブレークポイントを設定します(これはパーソナリティ関数と呼ばれます)。この関数は、スタックフレーム(基本的には関数呼び出し)に例外を処理するのに適したcatchブロックがあるかどうかを判断するために呼び出されます
  3. パーソナリティ関数では、実際の例外のラッパーである_Unwind_Exceptionへのポインターを見つけることができます。
  4. 次のように、例外のタイプ情報を取得します。__cxa_exception * exception_header =(__ cxa_exception *)(unwind_exception + 1)-1; std :: type_info * thrown_exception_type = exception_header-> exceptionType;
  5. 例外タイプを取得し、コード用に定義された残りのRTTIで検索できます。

いずれにせよ、プラットフォームで例外処理がどのように実装されているかを理解するには、おそらくかなりの時間を費やす必要があります。例外処理についてもう少し読みたい場合は、過去にトピックについて書いていました@ http://monoinfinito.wordpress.com/series/exception-handling-in-c/ 。これは公式の情報源ではありませんが、例外の処理に関係する各部分の仕様へのリンクがあります。

3
Nico Brailovsky