12

MS VC++ で答えを探しています。

大規模な C++ アプリケーションをデバッグする場合、残念ながら C++ 例外が非常に広範囲に使用されます。実際に必要な時間よりも少し遅れて例外をキャッチすることがあります。

擬似コードの例:

FunctionB()
{
    ...
    throw e;
    ...
}

FunctionA()
{
    ...
    FunctionB()
    ...
}

try
{
    Function A()
}
catch(e)
{
    (<--- breakpoint)
    ...
}

デバッグ時にブレークポイントで例外をキャッチできます。FunctionA()しかし、例外がor FunctionB()、または他の関数で発生したかどうかを追跡することはできません。(広範な例外の使用と上記の例の巨大なバージョンを想定しています)。

私の問題に対する 1 つの解決策は、コール スタックを決定し、例外コンストラクターで(つまり、キャッチされる前に) 保存することです。しかし、これには、この基本例外クラスからすべての例外を派生させる必要があります。また、多くのコードが必要になり、プログラムの速度が低下する可能性があります。

作業が少なくて済む簡単な方法はありますか? 大規模なコード ベースを変更する必要はありませんか?

他の言語でこの問題に対するより良い解決策はありますか?

4

14 に答える 14

13

コード内のブレークポイントを指しています。デバッガーを使用しているため、例外クラスのコンストラクターにブレークポイントを設定するか、スローされたすべての例外で中断するように Visual Studio デバッガーを設定できます (Debug->Exceptions C++ 例外をクリックし、スローされたオプションとキャッチされていないオプションを選択します)。

于 2008-08-30T18:08:51.967 に答える
12

例外がどこから来たのかだけに興味がある場合は、次のような単純なマクロを書くことができます

#define throwException(message) \
    {                           \
        std::ostringstream oss; \
        oss << __FILE __ << " " << __LINE__ << " "  \
           << __FUNC__ << " " << message; \
        throw std::exception(oss.str().c_str()); \
    }

これにより、ファイル名、行番号、および関数名が例外テキストに追加されます (コンパイラがそれぞれのマクロを提供している場合)。

次に、を使用して例外をスローします

throwException("An unknown enum value has been passed!");
于 2008-09-12T23:15:34.693 に答える
7

John Robbinsによって書かれた、デバッグに関する多くの難しい質問に取り組む優れた本があります。この本は、Microsoft.NETおよびMicrosoftWindows用のアプリケーションのデバッグと呼ばれています。タイトルにもかかわらず、この本には、ネイティブC++アプリケーションのデバッグに関する多くの情報が含まれています。

この本には、スローされた例外の呼び出しスタックを取得する方法についての長いセクションがあります。私の記憶が正しければ、彼のアドバイスのいくつかは、C ++例外の代わりに(またはそれに加えて)構造化例外処理(SEH)を使用することを含みます。私は本当にその本を十分に推薦することはできません。

于 2008-08-30T17:00:19.590 に答える
5

例外オブジェクト コンストラクターにブレークポイントを配置します。例外がスローされる前にブレークポイントを取得します。

于 2008-10-24T16:44:20.230 に答える
4

例外がスローされたときにその情報を含めない限り、例外がキャッチされた後にそのソースを見つける方法はありません。例外をキャッチするまでに、スタックはすでに巻き戻されており、スタックの以前の状態を再構築する方法はありません。

コンストラクターにスタックトレースを含めるという提案が最善の策です。はい、構築には時間がかかりますが、これが問題になるほど頻繁に例外をスローするべきではありません。すべての例外を新しいベースから継承させることも、必要以上のことになる可能性があります。単純に、関連する例外を継承させ(ありがとう、多重継承)、それらを個別にキャッチすることができます。

StackTrace64関数を使用してトレースを作成できます(他の方法もあると思います)。コード例については、この記事を確認してください。

于 2008-08-30T16:37:53.433 に答える
2

GCC ライブラリを使用して C++ で行う方法は次のとおりです。

#include <execinfo.h> // Backtrace
#include <cxxabi.h> // Demangling

vector<Str> backtrace(size_t numskip) {
    vector<Str> result;
    std::vector<void*> bt(100);
    bt.resize(backtrace(&(*bt.begin()), bt.size()));
    char **btsyms = backtrace_symbols(&(*bt.begin()), bt.size());
    if (btsyms) {
        for (size_t i = numskip; i < bt.size(); i++) {
            Aiss in(btsyms[i]);
            int idx = 0; Astr nt, addr, mangled;
            in >> idx >> nt >> addr >> mangled;
            if (mangled == "start") break;
            int status = 0;
            char *demangled = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);

            Str frame = (status==0) ? Str(demangled, demangled+strlen(demangled)) : 
                                      Str(mangled.begin(), mangled.end());
            result.push_back(frame);

            free(demangled);
        }
        free(btsyms);
    }
    return result;
}

例外のコンストラクターは、この関数を呼び出すだけで、スタック トレースを保存できます。numskipスタック トレースから例外のコンストラクターを切り取るのが好きなので、paramを使用します。

于 2008-08-30T18:14:57.087 に答える
1

ネイティブコードでは、 Vectored Exceptionハンドラーをインストールすることで、コールスタックをウォークスルーすることができます。VC ++は、SEH例外に加えてC ++例外を実装し、ベクトル化された例外ハンドラーは、フレームベースのハンドラーの前に最初のショットが与えられます。ただし、実際には注意が必要です。ベクトル化された例外処理によって発生する問題は、診断が難しい場合があります。

また、Mike Stallには、コードを管理しているアプリでの使用に関する警告がいくつかあります。最後に、Matt Pietrekの記事を読み、これを試す前に、SEHとベクトル化された例外処理を理解していることを確認してください。(追加したコードに重大な問題を追跡することは、重大な問題を追跡するのに役立ちます。)

于 2008-09-01T22:07:04.123 に答える
1

MSDevを使用すると、例外がスローされたときにブレークポイントを設定できると思います。

または、例外オブジェクトのコンストラクターにブレークポイントを設定します。

于 2008-10-24T17:16:19.293 に答える
1

これを行うための標準的な方法はありません。

さらに、コールスタックは通常、例外がスローされたときに記録する必要があります。キャッチされるとスタックが展開されるため、スローされた時点で何が起こっていたかがわかりません。

Win32 /Win64のVC++では、コンパイラ組み込み関数_ReturnAddress()からの値を記録し、例外クラスコンストラクタが__declspec(noinline)であることを確認することで、十分な結果が得られる場合があります。デバッグシンボルライブラリと組み合わせて、SymGetLineFromAddr64を使用して、リターンアドレスに対応する関数名(および.pdbに含まれている場合は行番号)を取得できると思います。

于 2008-08-30T16:25:36.177 に答える
1

IDE からデバッグしている場合は、[デバッグ] -> [例外] に移動し、[C++ 例外のスロー] をクリックします。

于 2008-09-05T20:40:04.970 に答える
0

他の言語?ええと、Javaではe.printStackTrace();を呼び出します。それよりも簡単になることはありません。

于 2008-08-30T16:22:23.627 に答える
0

誰かが興味を持っている場合は、同僚がこの質問に電子メールで返信しました。

Artemは書いた:

MiniDumpWriteDump()には、すべてのグローバル変数などを含む完全なプログラム状態を確認できるクラッシュダンプを改善できるフラグがあります。呼び出しスタックに関しては、最適化のために改善できるとは思えません...多分いくつかの)最適化をオフにします。

また、インライン関数とプログラム全体の最適化を無効にすることは非常に役立つと思います。

実際、多くのダンプタイプがあります。おそらく、十分に小さいものを選択できますが、それでも詳細情報があります http://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx

ただし、これらのタイプはコールスタックには役立ちませんが、表示できる変数の量にのみ影響します。

これらのダンプタイプの一部は、使用しているdbghelp.dllバージョン5.1ではサポートされていないことに気付きました。最新の6.9バージョンに更新することもできますが、MSデバッグツールのEULAを確認しました。最新のdbghelp.dllは引き続き再配布できます。

于 2008-08-30T17:23:45.803 に答える
0

私は独自の例外を使用します。それらは非常に簡単に処理できます-また、テキストが含まれています。私はフォーマットを使用します:

throw Exception( "comms::serial::serial( )", "Something failed!" );

また、2 番目の例外形式があります。

throw Exception( "comms::serial::serial( )", ::GetLastError( ) );

これは、FormatMessage を使用して DWORD 値から実際のメッセージに変換されます。where/what 形式を使用すると、何がどの関数で発生したかがわかります。

于 2008-08-30T23:27:36.693 に答える
0

この質問が出されてから 11 年が経ちましたが、今日では、標準の C++11のみを使用して、つまりクロスプラットフォームを使用して、デバッガーや面倒なログを必要とせずに、この問題を解決できます。例外が発生したコール スタックをトレースできます

使用std::nested_exceptionしてstd::throw_with_nested

これはスタックの巻き戻しにはなりませんが、私の意見では次善の策です。StackOverflow hereおよびhereで説明されています。ネストされた例外を再スローする適切な例外ハンドラーを記述するだけで、デバッガーや面倒なログを必要とせずに、コード内の例外のバックトレースを取得する方法について説明されています。

ただし、トレースする関数に try/catch ステートメントを挿入する必要があります (つまり、これがない関数はトレースに表示されません)。マクロを使用してこれを自動化し、記述/変更する必要があるコードの量を減らすことができます。

任意の派生例外クラスでこれを行うことができるため、このようなバックトレースに多くの情報を追加できます! GitHubの私の MWE もご覧ください。バックトレースは次のようになります。

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
于 2019-12-24T15:39:36.487 に答える