[解決済み] C++で例外が発生した場所を見つけるにはどうしたらいいですか?
質問
どこかでキャッチできない例外をスローするプログラムを持っています。例外がスローされたというレポートだけが表示され、どこでスローされたかについての情報はありません。デバッグ シンボルを含むようにコンパイルされたプログラムが、私のコードのどこで例外が発生したかを私に通知しないのは、非論理的だと思われます。
gdb で 'catch throw' を設定し、スローされたすべての例外のバックトレースを呼び出す以外に、例外がどこから来るのかを知る方法はないのでしょうか?
どのように解決するのですか?
ここで、いくつかの情報は があります。 あなたの問題のデバッグに役立つかもしれない情報です。
例外がキャッチされない場合、特殊なライブラリ関数である
std::terminate()
が自動的に呼び出されます。 Terminateは実際には関数へのポインターで、デフォルト値はStandard Cライブラリ関数の
std::abort()
. 捕捉されない例外に対してクリーンアップが行われない場合
†
の場合、それは
かもしれない
は、デストラクタが呼び出されないので、この問題のデバッグに実際に役立つかもしれません。
の前にスタックが巻き戻されるかどうかは実装で定義されています。
std::terminate()
が呼ばれる前にスタックが巻き戻されるかどうかは実装で決まる。
への呼び出し
abort()
の呼び出しは、例外の原因を特定するために分析できるコアダンプを生成するのに役立つことがよくあります。 コアダンプを有効にするには
ulimit -c unlimited
を使用してコアダンプを有効にします (Linux)。
自分でインストールできる
terminate()
関数をインストールすることができます。
std::set_terminate()
. gdbでterminate関数にブレークポイントを設定することができるはずです。 あなたは
かもしれません
からスタックバックトレースを生成することができます。
terminate()
関数からスタックバックトレースを生成し、このバックトレース
は
は例外の発生場所を特定するのに役立ちます。
に関する簡単な議論があります。 キャッチできない例外 で ブルース・エッケルの「C++で考える」第2版 も参考になるかもしれません。
から
terminate()
コール
abort()
をデフォルトで呼び出します(これによって
SIGABRT
シグナルが発生します)、あなたは
かもしれません
を設定することができます。
SIGABRT
ハンドラを設定し
シグナルハンドラからスタックバックトレースを表示する。
. このバックトレースは
は
は、例外の発生場所を特定するのに役立ちます。
注意してください。
私は
かもしれない
というのは、C++はエラー処理と報告コードを通常のコードから分離するための言語構造を使用することで、非ローカルなエラー処理をサポートしているからです。 キャッチブロックは、投げるポイントとは異なる関数/メソッドに配置することができ、またしばしば配置されます。 また、コメントで指摘されたこともあります(Thanks
Dan
の前にスタックが巻き戻されるかどうかは実装で決まっているとのことです。
terminate()
が呼び出される前にスタックが巻き戻されるかどうかは、実装によって決まるということです。
更新しました。
バックトレースを生成するLinuxのテストプログラムというのを投げました。
terminate()
関数セットで
set_terminate()
のシグナルハンドラで、もうひとつは
SIGABRT
. 両方のバックトレースは、処理されない例外の場所を正しく示しています。
更新 2:
のブログ記事に感謝します。
terminateでキャッチできない例外をキャッチする
のブログ記事のおかげで、terminate ハンドラ内で捕捉できない例外を再スローすることなど、いくつかの新しいトリックを学ぶことができました。 ここで重要なのは、空の
throw
ステートメントは GCC で動作し、移植可能なソリューションではありません。
コードです。
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <execinfo.h>
#include <signal.h>
#include <string.h>
#include <iostream>
#include <cstdlib>
#include <stdexcept>
void my_terminate(void);
namespace {
// invoke set_terminate as part of global constant initialization
static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}
// This structure mirrors the one found in /usr/include/asm/ucontext.h
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;
// Get the address at the time the signal was raised from the EIP (x86)
void * caller_address = (void *) uc->uc_mcontext.eip;
std::cerr << "signal " << sig_num
<< " (" << strsignal(sig_num) << "), address is "
<< info->si_addr << " from "
<< caller_address << std::endl;
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
// overwrite sigaction with caller's address
array[1] = caller_address;
char ** messages = backtrace_symbols(array, size);
// skip first stack frame (points here)
for (int i = 1; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
exit(EXIT_FAILURE);
}
void my_terminate() {
static bool tried_throw = false;
try {
// try once to re-throw currently active exception
if (!tried_throw++) throw;
}
catch (const std::exception &e) {
std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
<< e.what() << std::endl;
}
catch (...) {
std::cerr << __FUNCTION__ << " caught unknown/unhandled exception."
<< std::endl;
}
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
char ** messages = backtrace_symbols(array, size);
for (int i = 0; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
abort();
}
int throw_exception() {
// throw an unhandled runtime error
throw std::runtime_error("RUNTIME ERROR!");
return 0;
}
int foo2() {
throw_exception();
return 0;
}
int foo1() {
foo2();
return 0;
}
int main(int argc, char ** argv) {
struct sigaction sigact;
sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGABRT, &sigact, (struct sigaction *)NULL) != 0) {
std::cerr << "error setting handler for signal " << SIGABRT
<< " (" << strsignal(SIGABRT) << ")\n";
exit(EXIT_FAILURE);
}
foo1();
exit(EXIT_SUCCESS);
}
出力します。
my_terminate は手つかずの例外をキャッチしました。RUNTIME ERROR! my_terminateのバックトレースは10フレームを返しました。 [bt]: (0) ./test(my_terminate__Fv+0x1a) [0x8048e52]. [bt]: (1) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa] [0x40045baa [bt]: (2) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5] [0x400468e5] [0x400468e5 [bt]です。(3) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf] [bt]です。(4) ./test(throw_exception__Fv+0x68) [0x8049008]. [bt]: (5) ./test(foo2__Fv+0xb) [0x8049043] です。 [bt]: (6) ./test(foo1__Fv+0xb) [0x8049057] です。 [bt]: (7) ./test(main+0xc1) [0x8049121] (7) ./test(main+0xc1) [0x8049122 [bt]: (8) ./test(__libc_start_main+0x95) [0x42017589] [0x42017589]を実行します。 [bt]: (9) ./test(__eh_alloc+0x3d) [0x8048b21] です。 シグナル 6 (中止)、アドレスは 0x42029331 から 0x1239 です。 crit_err_hdlrバックトレースは、13フレームを返しました。 [bt]です。(1) ./test(kill+0x11) [0x42029331]です。 [bt]: (2) ./test(abort+0x16e) [0x4202a8c2]. [bt]です。(3) ./test [0x8048f9f] [bt]です。(4) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa] [0x40045baa] [0x40045baa [bt]です。(5) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5] [0x400468e5] [0x400468e5 [bt]です。(6) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf] [bt]です。(7) ./test(throw_exception__Fv+0x68) [0x8049008]. [bt]: (8) ./test(foo2__Fv+0xb) [0x8049043] です。 [bt]: (9) ./test(foo1__Fv+0xb) [0x8049057] です。 [bt]: (10) ./test(main+0xc1) [0x8049121] (10) ./test(main+0xc1) [0x8049122 [bt]: (11) ./test(__libc_start_main+0x95) [0x42017589] [0x42017589]を実行します。 [bt]: (12) ./test(__eh_alloc+0x3d) [0x8048b21] のようになります。
関連
-
[解決済み】「The breakpoint will not currently be hit」を改善するには?このドキュメントにはシンボルが読み込まれていません。" という警告はどうすれば改善されますか?
-
[解決済み] 文字列の単語を反復処理するにはどうすればよいですか?
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] Linux上で動作するC++コードのプロファイリングを行うにはどうすればよいですか?
-
[解決済み] Node.jsアプリケーションをデバッグするにはどうすればよいですか?
-
[解決済み] Pythonの関数が例外を投げるかどうかをテストするにはどうすればよいですか?
-
[解決済み] Rubyで「例外 => e」を救済するのはなぜ悪いスタイルなのですか?
-
[解決済み] Node.jsの例外処理のベストプラクティス
-
[解決済み】WPFアプリケーションで例外をグローバルにキャッチする?
-
[解決済み] Visual Studioです。処理された例外でブレークする方法は?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】構造体のベクター初期化について
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】Visual Studio 2015で「非標準の構文。'&'を使用してメンバーへのポインターを作成します」エラー
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】'cout'は型名ではない
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み] プログラムがクラッシュしたときにスタックトレースを自動的に生成する方法