Получение информации о том, где исключения С++ выбрасываются внутри блока catch?

У меня есть приложение С++, которое обертывает большие части кода в блоках try. Когда я поймаю исключения, я могу вернуть пользователя в стабильное состояние, что приятно. Но я больше не получаю аварийные свалки. Я действительно хотел бы выяснить, где в коде происходит исключение, поэтому я могу его зарегистрировать и исправить.

Возможность получить дамп без остановки приложения будет идеальной, но я не уверен, что это возможно.

Есть ли какой-то способ выяснить, откуда было исключено исключение из блока catch? Если это полезно, я использую собственный msvС++ для windows xp и выше. Мой план состоит в том, чтобы просто регистрировать сбои в файле на компьютерах разных пользователей, а затем загружать краш-журналы, когда они достигают определенного размера.

Ответ 1

Это возможно при использовании SEH (структурированная обработка исключений). Дело в том, что MSVC реализует исключения С++ через SEH. С другой стороны, чистый SEH намного более мощный и гибкий.

Что ты должен делать. Вместо использования чистых блоков С++ try/catch, таких как:

try
{
    DoSomething();
} catch(MyExc& exc)
{
    // process the exception
}

Вы должны обернуть блок внутреннего кода DoSomething блоком SEH:

void DoSomething()
{
    __try {
        DoSomethingInner();
    }
    __except (DumpExc(GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) {
        // never get there
    }
}

void DumpEx(EXCEPTION_POINTERS* pExc)
{
    // Call MiniDumpWriteDump to produce the needed dump file
}

То есть внутри блока try/catch С++ мы размещаем еще один необработанный блок SEH, который только сбрасывает все исключения, не вылавливая их.

См. здесь для примера использования MiniDumpWriteDump.

Ответ 2

Возможно создание ваших исключений для включения имен исходных файлов и номеров строк. Для этого вам нужно создать класс, полученный из std::exception, чтобы содержать информацию. В приведенном ниже примере у меня есть библиотека исключений для моего приложения, включая my_exception. У меня также есть traced_error, который является классом исключения шаблона, полученным из моих исключений на уровне приложения. Исключение traced_error содержит информацию о имени файла и номере строки и вызывает метод класса what() класса исключения на уровне приложения для получения подробной информации об ошибках.

#include <cstdlib>
#include <string>
#include <stdexcept>
#include <iostream>
using namespace std;


template<class EX>
class traced_error : virtual public std::exception, virtual public EX
{
public:
    traced_error(const std::string& file, int line, const EX& ex)
    :   EX(ex),
        line_(line),
        file_(file)
    {       
    }

    const char* what() const
    {
        std::stringstream ss;
        static std::string msg;
        ss << "File: " << file_ << " Line: " << line_ << " Error: " << EX::what();
        msg = ss.str().c_str();
        return msg.c_str();
    }

    int line_;
    std::string file_;
};

template<class EX> traced_error<EX> make_traced_error(const std::string& file, int line, const EX& ex)
{
    return traced_error<EX>(file, line, ex);
}


class my_exception : virtual public std::exception
{
public:
    my_exception() {};

    const char* what() const
    {
        return "my_exception what";
    }
};

#define throwx(EX) (throw make_traced_error(__FILE__,__LINE__,EX))


int main()
{
    try
    {
        throwx(my_exception());
    }
    catch( const std::exception& ex )
    {
        cout << ex.what();
    }
    return 0;
}

Выход этой программы:

Файл:.\main.cpp Строка: 57 Ошибка: my_exception что

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

Ответ 3

Вам нужно проанализировать стек, чтобы выяснить, откуда взялось исключение. Для msvc существует lib, называемый dbghelp.dll, который может помочь вам исключить исключения. В общем, что я делаю, это выйти из файла minaump и использовать это, чтобы воспроизвести проблему, используя правую программную базу данных (файл pdb). Это работает на клиентских системах, которые не поставляются с исходным кодом или кому вы не хотите давать pdbs.

Ответ 4

Вы можете написать дампы, используя функцию MiniDumpWriteDump.

Если вы используете исключения С++, вы можете просто включить информацию о файле/строке/функции (так что вы увидите ее как текст, если вы вызываете std:: exception.what()) в любом месте, где вы бросаете исключение (с использованием макросов ____FUNCTION____, ____FILE____ и ____LINE____).

Если вы пытаетесь поймать исключения ОС, то сбой приложения, вероятно, будет лучшим выбором.

Ответ 5

Один трюк, независимый от компилятора, заключается в том, чтобы обернуть оператор throw в функцию. Функция может выполнять другие обязанности перед тем, как выбросить исключение, например запись в файл журнала. Он также делает удобное место для установки точки останова. Если вы создаете макрос для вызова функции, вы можете автоматически включать __FILE__ и __LINE__, где произошел бросок.