Как найти место, где было вызвано исключение в C++?


У меня есть программа, которая бросает неперехваченное исключение где-то. Все что я получаю это сообщение об исключении, и никакой информации о том, где он был брошен. Это кажется нелогичным для программы, скомпилированной, чтобы содержать отладочные символы, чтобы не уведомлять меня о том, где в моем коде было сгенерировано исключение.

есть ли способ сказать, откуда приходят мои исключения, кроме установки "catch throw" в gdb и вызова backtrace для каждого брошенного исключения?

7 77

7 ответов:

вот некоторые сведения, что мая быть полезным в отладке вашей проблемы

если исключение не перехвачено, то специальная библиотечная функция std::terminate() вызывается автоматически. Terminate - это фактически указатель на функцию, а значение по умолчанию-стандартная функция библиотеки C std::abort(). Если никакие очистки не происходят для неперехваченного исключения, это мая на самом деле быть полезным в отладке этой проблемы нет деструкторы вызываются.
†это реализация-определяется ли стек разматывается перед std::terminate() называется.


вызов abort() часто полезно при создании дампа ядра, который может быть проанализирован для определения причины исключения. Убедитесь, что вы включили основные дампы через ulimit -c unlimited (Linux).


вы можете установить свой собственный

Как вы говорите, мы можем использовать ' catch throw 'в gdb и вызывать' 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/

вы можете создать макрос типа:

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

...и это даст вам место, где выбрасывается исключение (по общему признанию, не трассировка стека). Это необходимо для вас, чтобы получить исключения из некоторого базового класса, который принимает выше конструктор.

вы не передали информацию о том, какую ОС / компилятор вы используете.

в Visual Studio C++ исключения могут быть инструментированы.

посмотреть " Инструментарий Обработки Исключений Visual C++" on ddj.com

мои статьи "Отладка Посмертных", также включено ddj.com включает код для использования структурированной обработки исключений Win32 (используемой инструментарием) для ведения журнала и т. д.

вы можете отметить основные труднодоступные места в своем коде как noexcept чтобы найти исключение, используйте libunwind (просто добавить -lunwind к параметрам компоновщика) (проверено с clang++ 3.6):

demagle.ГЭС:

#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);
}

след.ГЭС:

#pragma once
#include <ostream>

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

след.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.ГЭС:

#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
}

}

здесь хорошая статья о вопрос.

У меня есть код, чтобы сделать это в Windows/Visual Studio, дайте мне знать, если вы хотите наброски. Не знаю, как это сделать для кода dwarf2, хотя быстрый google предполагает, что в libgcc есть функция _Unwind_Backtrace, которая, вероятно, является частью того, что вам нужно.

проверьте этот поток, возможно, это помогает:

ловить все необработанные исключения C++?

Я сделал хороший опыт работы с этим программным обеспечением:

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

Он может распечатать трассировку стека в файл для любого необработанного исключения.