Получение информации о том, где исключения С++ выбрасываются внутри блока 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__
, где произошел бросок.