gdb を使用すると、例外がスローされたときとキャッチされたときに例外をキャッチできます。ただし、例外がスローされる行にシンボルがない場合や、例外処理中にブレークポイントがトリガーされる場合があります。現在の例外の値を調べるにはどうすればよいですか?
3 に答える
以前の回答は (2013 年に) 書かれた時点では正しかったのですが、それ以降 gdb と libstdc++ が変更されました。
libstdc++ には、gdb が例外システムとより適切に対話できるようにするいくつかのフックが追加されました。特に、gdb が$_exception
ユーザーに便利な変数を提供するのに十分な情報が公開されるようになりました。この変数は、スローされる例外を保持します。例外がキャッチされている正確な場所でのみ有効です。の使用をやめることができますcatch catch
。
詳しくはマニュアルのページをご覧ください。
更新しました
ここにGDBマニュアルからの情報があります
現在、gdb での C++ 例外処理 (catch throw および catch catch) にはいくつかの制限があります。
関数を対話的に呼び出した場合、通常、関数の実行が終了すると、gdb は制御を返します。ただし、呼び出しが例外を発生させた場合、呼び出しは制御をユーザーに返すメカニズムをバイパスし、プログラムを中止するか、ブレークポイントに到達するか、gdb がリッスンしているシグナルをキャッチするか、終了するまで単に実行を継続する可能性があります。これは、例外のキャッチポイントを設定した場合でも当てはまります。例外のキャッチポイントは、対話型呼び出し内では無効になっています。対話的に例外を発生させることはできません。例外ハンドラを対話的にインストールすることはできません。場合によっては、catch が例外処理のデバッグに最適な方法ではないこともあります。例外が発生した場所を正確に知る必要がある場合は、例外ハンドラーが呼び出される前に停止することをお勧めします。そうすれば、巻き戻しが行われる前にスタックを見ることができます。代わりに例外ハンドラーにブレークポイントを設定すると、例外が発生した場所を見つけるのが簡単ではない場合があります。
例外ハンドラが呼び出される直前に停止するには、実装に関するある程度の知識が必要です。gnu C++ の場合、次の ANSI C インターフェイスを持つ __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()
を試すか、例外のメンバーを確認できます。(...) として捉えただけでは、何を集めることができるかわかりません。
あなたができるもう1つの処理は、gdbで「スロー」をキャッチし、フロー全体を本当にたどりたい場合は「キャッチ」もキャッチすることです。
gdb> catch catch
gdb> catch throw
このようにして、例外がスローされる直前と例外がキャッチされる直前にブレークポイントを取得し、スタックをたどって、何が起こっているかについてより多くの情報を得ることができます。別のブレーク ポイントにいる場合でも、(up または down を使用して) スタックを上に移動して、例外が表示されているフレームを取得できるはずです。
簡単な答え: 例外処理のほとんどの作業はプログラムの外で行われ、したがって gdb の範囲外であるため、できません。
説明された答え:
例外がスローされた行にシンボルがない場合があります
デバッグしているバイナリにデバッグ シンボルがない場合、そのバイナリはおそらく削除されており、型や値についてはまったく見つけることができません。
現在の例外の値を調べるにはどうすればよいですか?
ここで、例外は gdb が検査できる言語機能であると想定していると思います。実際、C++ の例外は、言語としての C++ の機能、libc++ と ABI の組み合わせです。また、複数のアクティブな現在の例外が存在する場合もあります。
UpAndAdam が指摘するように、型指定子を使用して catch ブロックにブレークポイントを設定し、その要素を検査できますが、「catch (...)」が見つかった場合に問題があると思われます。そのような場合、例外処理の実装を掘り下げない限り、現在の例外について多くを学ぶことはできません。
非常に短く不完全な説明で、例外をスローすると言えます。
- プログラムは libc++ を呼び出して例外を発生させます
- libc++ は、glibc で「unwind」を呼び出して、スタックの巻き戻しを開始します。
- unwind は、スタック フレームごとに libc++ から「パーソナリティ関数」を呼び出します (基本的に、スタック内の各関数呼び出し)。
- パーソナリティ関数は、現在のスタック フレームがこの例外を処理できるかできないかを何らかの方法で決定します。
- 例外を処理できる場合、catch ブロックが実行されます
多くの例外処理はツールチェーン (コンパイラ、プラットフォーム、アーキテクチャ、libc++ など) に依存するため、詳細について話すことは困難ですが、ほとんどの場合、「catch (...)」は元の例外を受け取ることさえありません。引数として。いずれにせよ、どういうわけかあなたの質問に答えるために: gnu の libc++ を使用した gcc では、次のようなことを試すことができます:
- デバッグ シンボルを含む libc++ を入手する
- __gxx_personality_v0 にブレークポイントを設定します (パーソナリティ関数と呼ばれます)。この関数は、スタック フレーム (基本的には関数呼び出し) に例外を処理するための適切な catch ブロックがあるかどうかを判断するために呼び出されます。
- パーソナリティ関数では、実際の例外へのラッパーである _Unwind_Exception へのポインターを見つけることができます。
- 次のように例外の型情報を取得します。 __cxa_exception *exception_header = (__cxa_exception*)(unwind_exception+1)-1; std::type_info *thrown_exception_type = exception_header->exceptionType;
- コードに定義された残りの RTTI で検索できる例外タイプを取得します。
いずれにせよ、例外処理がプラットフォームにどのように実装されているかを理解するには、おそらくかなりの時間を費やす必要があります。例外処理についてもう少し詳しく知りたい場合は、過去に @ http://monoinfinito.wordpress.com/series/exception-handling-in-c/というトピックについて書いていました。公式のソースではありませんが、例外の処理に関連する各部分の仕様へのリンクがあります。