У меня есть приложение C++, которое переносит значительные части кода в блоках попытки. Когда я ловлю исключения, я могу возвратить пользователя устойчивому состоянию, которое хорошо. Но я дольше не получаю дампы катастрофического отказа. Я действительно хотел бы выяснить, где в коде исключение происходит, таким образом, я могу зарегистрировать его и зафиксировать его.
Способность получить дамп, не останавливая приложение была бы идеальна, но я не уверен, что это возможно.
Есть ли некоторый способ, которым я могу выяснить, где исключение было выдано из блока выгоды? Если это полезно, я использую собственный msvc ++ на Windows XP и выше. Мой план состоит в том, чтобы просто зарегистрировать катастрофические отказы в файл на машинах различных пользователей и затем загрузить crashlogs, после того как они добираются до определенного размера.
Это возможно с использованием SEH (структурированная обработка исключений). Дело в том, что MSVC реализует исключения C ++ через SEH.С другой стороны, чистый SEH намного более мощный и гибкий.
Вот что тебе следует делать. Вместо использования чистых блоков try / catch C ++, подобных этому:
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 C ++ мы помещаем еще один необработанный блок SEH, который только сбрасывает все исключения, не перехватывая их.
См. здесь для примера использования MiniDumpWriteDump.
Вы можете настроить исключения так, чтобы они включали имена исходных файлов и номера строк. Для этого вам необходимо создать класс, производный от 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's 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's what
Вы также можете перепроектировать это так, чтобы исключения уровня приложения были производными от traced_error
, а не наоборот, на случай, если вы предпочтете перехватить определенные исключения на уровне приложения. В вашей ловушке
вы можете записать ошибку в файл журнала и создать файл дампа с помощью MiniDumpWriteDump () .
Что вам нужно, так это проанализировать стек, чтобы выяснить, откуда пришло исключение. Для msvc существует библиотека под названием dbghelp.dll , которая может помочь вам выйти из системы исключений. Как правило, я выхожу из файла минидампа и использую его для воспроизведения проблемы рядом с использованием правильной базы данных программы (файл pdb). Это работает в клиентских системах, которые не поставляются с исходным кодом или которым вы не хотите передавать pdbs.
Один прием, не зависящий от компилятора, заключается в том, чтобы обернуть оператор throw в функцию. Функция может выполнять другие функции, прежде чем выбросить исключение, например, записывать в файл журнала. Это также удобное место для установки точки останова. Если вы создадите макрос для вызова функции, вы можете автоматически включить __FILE__
и __LINE__
, где произошел выброс.
Вы можете записывать дампы с помощью функции MiniDumpWriteDump .
Если вы используете исключения C ++, вы можете просто включить информацию о файле / строке / функции (чтобы вы видели ее как текст, если вы вызываете std :: exception.what ()) в любом месте, где вы бросаете это исключение (с использованием макросов ____FUNCTION____, ____FILE____ и ____LINE____).
Если вы пытаетесь перехватить исключения ОС, то, вероятно, лучшим выбором будет завершение работы приложения.