web-dev-qa-db-ja.com

C ++で例外がスローされた場所を見つけるにはどうすればよいですか?

どこかにキャッチされない例外をスローするプログラムがあります。私が受け取るのは、スローされている例外のレポートであり、例外がスローされた場所に関する情報はありません。デバッグシンボルを含むようにコンパイルされたプログラムが、コードのどこで例外が生成されたかを通知しないのは非論理的なようです。

Gdbで 'catch throw'を設定し、スローされたすべての例外に対してバックトレースを呼び出すことで、例外がどこから来たのかを知る方法はありますか

84
Alex

おっしゃるように、gdbで「catch throw」を使用し、スローされる例外ごとに「backtrace」を呼び出すことができます。これは通常、手作業では面倒ですが、gdbを使用するとプロセスを自動化できます。これにより、最後にキャッチされなかった例外を含む、スローされたすべての例外のバックトレースを確認できます。

gdb>

set pagination off
catch throw
commands
backtrace
continue
end
run

さらに手動で介入しなければ、これにより、最後のキャッチされなかった例外を含む多くのバックトレースが生成されます。

Catchpoint 1 (exception thrown), 0x00a30 in __cxa_throw () from libstdc++.so.6
#0  0x0da30 in __cxa_throw () from /usr/.../libstdc++.so.6
#1  0x021f2 in std::__throw_bad_weak_ptr () at .../shared_ptr_base.h:76
[...]
terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
Program received signal SIGABRT, Aborted.

これをまとめた素晴らしいブログ記事があります: http://741mhz.com/throw-stacktrace [on archive.org]

45
TimJ

次のようなマクロを作成できます。

#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )

...そして、例外がスローされる場所を提供します(確かにスタックトレースではありません)。上記のコンストラクターを使用する基本クラスから例外を派生させる必要があります。

16
Erik Hermansen

コード内の主なタイトな場所をnoexceptとしてマークして例外を特定し、libunwind(ちょうど_-lunwind_を追加するだけです)リンカパラメータへ)(_clang++ 3.6_でテスト済み):

demagle.hpp:

_#pragma once

char const *
get_demangled_name(char const * const symbol) noexcept;
_

demangle.cpp:

_#include "demangle.hpp"

#include <memory>

#include <cstdlib>

#include <cxxabi.h>

namespace
{

#pragma clang diagnostic Push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< char, decltype(std::free) & > demangled_name{nullptr, std::free};
#pragma clang diagnostic pop

}

char const *
get_demangled_name(char const * const symbol) noexcept
{
    if (!symbol) {
        return "<null>";
    }
    int status = -4;
    demangled_name.reset(abi::__cxa_demangle(symbol, demangled_name.release(), nullptr, &status));
    return ((status == 0) ? demangled_name.get() : symbol);
}
_

backtrace.hpp:

_#pragma once
#include <ostream>

void
backtrace(std::ostream & _out) noexcept;
_

backtrace.cpp:

_#include "backtrace.hpp"

#include <iostream>
#include <iomanip>
#include <limits>
#include <ostream>

#include <cstdint>

#define UNW_LOCAL_ONLY
#include <libunwind.h>

namespace
{

void
print_reg(std::ostream & _out, unw_Word_t reg) noexcept
{
    constexpr std::size_t address_width = std::numeric_limits< std::uintptr_t >::digits / 4;
    _out << "0x" << std::setfill('0') << std::setw(address_width) << reg;
}

char symbol[1024];

}

void
backtrace(std::ostream & _out) noexcept
{
    unw_cursor_t cursor;
    unw_context_t context;
    unw_getcontext(&context);
    unw_init_local(&cursor, &context);
    _out << std::hex << std::uppercase;
    while (0 < unw_step(&cursor)) {
        unw_Word_t ip = 0;
        unw_get_reg(&cursor, UNW_REG_IP, &ip);
        if (ip == 0) {
            break;
        }
        unw_Word_t sp = 0;
        unw_get_reg(&cursor, UNW_REG_SP, &sp);
        print_reg(_out, ip);
        _out << ": (SP:";
        print_reg(_out, sp);
        _out << ") ";
        unw_Word_t offset = 0;
        if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
            _out << "(" << get_demangled_name(symbol) << " + 0x" << offset << ")\n\n";
        } else {
            _out << "-- error: unable to obtain symbol name for this frame\n\n";
        }
    }
    _out << std::flush;
}
_

backtrace_on_terminate.hpp:

_#include "demangle.hpp"
#include "backtrace.hpp"

#include <iostream>
#include <type_traits>
#include <exception>
#include <memory>
#include <typeinfo>

#include <cstdlib>

#include <cxxabi.h>

namespace
{

[[noreturn]]
void
backtrace_on_terminate() noexcept;

static_assert(std::is_same< std::terminate_handler, decltype(&backtrace_on_terminate) >{});

#pragma clang diagnostic Push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< std::remove_pointer_t< std::terminate_handler >, decltype(std::set_terminate) & > terminate_handler{std::set_terminate(backtrace_on_terminate), std::set_terminate};
#pragma clang diagnostic pop

[[noreturn]]
void
backtrace_on_terminate() noexcept
{
    std::set_terminate(terminate_handler.release()); // to avoid infinite looping if any
    backtrace(std::clog);
    if (std::exception_ptr ep = std::current_exception()) {
        try {
            std::rethrow_exception(ep);
        } catch (std::exception const & e) {
            std::clog << "backtrace: unhandled exception std::exception:what(): " << e.what() << std::endl;
        } catch (...) {
            if (std::type_info * et = abi::__cxa_current_exception_type()) {
                std::clog << "backtrace: unhandled exception type: " << get_demangled_name(et->name()) << std::endl;
            } else {
                std::clog << "backtrace: unhandled unknown exception" << std::endl;
            }
        }
    }
    std::_Exit(EXIT_FAILURE); // change to desired return code
}

}
_

問題に関して 良い記事 があります。

5
Orient

使用しているOS /コンパイラに関する情報を渡しませんでした。

Visual Studio C++では、例外を計測できます。

Ddj.comの "Visual C++例外処理インストルメンテーション" を参照してください。

私の記事 "Postmortem Debugging" 、ddj.comにも、ロギングなどにWin32構造化例外処理(インストルメンテーションで使用)を使用するコードが含まれています。

5
RED SOFT ADAIR

Windows/Visual Studioでこれを行うコードがあります。アウトラインが必要な場合はお知らせください。しかし、dwarf2コードに対してそれを行う方法がわからないので、簡単なグーグルは、おそらく必要なものの一部であるlibgccに_Unwind_Backtrace関数があることを示唆しています。

1
Ben Voigt

このスレッドをチェックしてください。おそらく役立つでしょう:

未処理のC++例外をすべてキャッチしますか?

私はそのソフトウェアで良い経験をしました:

http://www.codeproject.com/KB/applications/blackbox.aspx

未処理の例外については、スタックトレースをファイルに出力できます。

1
nabulke