| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Системное программирование и WinAPI > SEH и STL исключения |
| Автор: BearFear 1.9.2012, 18:06 | ||||||||||||||||||
Всем приуэт. Возникла следующая дилемма. Для весьма простенькой программы надо предоставить возможность вывода информации об ошибках (замучала меня эта тема блин). После прочтения статей и бла-бла-бла... в общем родился вот такой велосипед с тремя колесами и одной педалью.
В данном случае, создается объект для данного потока, устанавливающий ExceptionHandler для системы и принимающий указатель на функцию shelled_main которая заменяет стандартный мэйн. Далее, вызывается shelled_main и в случае необходимости кидается throw который и обрабатывается указанным try-catch. В случае возникновения исключения stl соответственно обрабатывается условия под исключения stl. В случае если throw без аргумента (класса исключения) то соответственная ветка catch(...). (поправка: пустой throw вызовет unexpected)
Объект shell предназначен для установки обработчиков как для main, так и для потоков, которые создаются отдельным объектом, внутри прокси-функции так же производится подобная установка try-catch и вызов пользовательской функции, которая должна выполняться в контексте потока.
Фактически, такой код приведет к генерации от 1го до 2х исключений подряд. В случае систем исключения, вызывается exception_handler класса shell для данного потока. В случае stl исключения, сначала вызывается исключение уровня try-catch где производится формирование инфы под одну гребенку и вызов системного исключения с уникальным (отличным от системных кодов) кодом исключения. Фактически, цель этого велосипеда, в случае какой либо ошибки, собрать инфу в лог. Повторюсь, ПО позволяет слетать в случае внезапного чиха, так как задача ПО очень тривиальна, хоть и работает с сетью. В общем должны отлавливаться все ошибки и формирование лога проихводится почти одинаково для всех видов ошибок\исключений, будь то рантайм или логические.
Для примера, объект принимает в конструкторе имя модуля и номер строки где возникла логическая ошибка. Это дело можно будет потом допустим поменять на что то иное, например код ошибки, который внутри объекта будет использоваться как индекс описания ошибки из файла, и в таком случае на выходе получим уже описание ошибки как для stl::[some_error].
Собстно код, тут только один комментарий. Данный код пока что не эффективен в силу недоделанности системы. Но отделяет от итогового варианта лишь два момента, которые пока что просто не написал. 1) нет статического глобального объекта строки, в которую будут сваливаться имя модуля и код 2) нет реализации вывода в лог из exception_handler и вызовов RaiseException в конструкциях try-catch
Ну тут все ясно, это прототип shelled_main функции главного потока процесса. В итоге, как бы, программист начинает кодирование отсюда. Все остальное является как бы не то что бы закрытым... как бы шаблоном проекта, каркасом последующих приложений.
Для примера, дропается throw который будет обработан catch(shell_exception&) и будет выведена инфа о факте исключения, с подробностями.
Класс-поток, который по сути ничем не отличается внутри от главного потока (в отношении к велосипеду).
Вот собственно и весь код. Хотелось бы узнать, братцы, является ли такой велосипед целесообразным в данных условиях: 1 - ПО не предназначено для отлова и решения ошибок, так как расчитано на других юзверей. Система должна быть простой настолько, что бы ошибки сводились к отчету об ошибке и отправке на сервер в случае если ошибка не логическая, а stl или системная. О логической ошибке пользователь должен будет прочитать лог и уже сделать вывод, что у него случилось 2 - ПО не обладает важными данными которые надо сохранять или оперировать локально, это графическое ПО которое всего лишь может отсылать ошибки на сервер (для статистики о багах и собственно что бы знать, что исправлять). 3 - Формат лога об ошибках один для всех ситуаций и выводится в xml формат для его более простого просмотра с помощью другой утилиты, а так же что бы сервер смог например прочитать лог и сохранить нужную для разработов инфу на баг тракере, откуда уже будут разбирать задания все кому не лень. |
| Автор: BearFear 1.9.2012, 18:25 |
| Ай, чуть не забыл. Компилятор MinGW. |
| Автор: BearFear 1.9.2012, 20:22 |
| У меня есть только одно сомнение (может о других я не знаю), на счет вызова системного экцепшена из другого экцепшена. Не знаю чем это может быть черевато, так как не до конца еще понимаю всю подноготную односвязного списка обработчиков. |
| Автор: BearFear 1.9.2012, 22:01 |
| Жалко с Касперски не дружу, он бы точно, фыркнув, щелчком пальца решил такой вопрос, ухоха. |
| Автор: BearFear 1.9.2012, 22:58 | ||
| В excpt.h нашел строчки, которые определяют макросы __try1 и __except1, в теле которых выполняется асм инлайн по установке обработчиков ошибок. Завтра займусь проверкой, если это то что я думаю, то всю мою конструкцию можно будет переписать одной функцией и обработчика с указанием ее хэндла в __try1(exception_handler). Это существенно сократит код. А ваще, в манере захватчика-террориста, лучше даже будет сделать свой макрос с красивым именем
|
| Автор: BearFear 1.9.2012, 23:30 | ||||||||||||
Прикольно
Используя __cyg_profile_func_[enter|exit] можно довести до автоматизма некоторые вещи, например автоматическая установка обработчиков для определенных функций. Живые есть? Самому с собой не прикольно болтать |
| Автор: BearFear 2.9.2012, 19:38 |
| Обнаруживается плавающая ошибка: - код исключения не всегда верный - аргументы не всегда владеют верными указателями происходит все в блоке вызова RaiseException внутри catch. Почему такое может происходить? |
| Автор: BearFear 2.9.2012, 21:20 |
| Ну что, никто не сможет помочь? Никто не знает как в низах работают STL исключения? |
| Автор: GremlinProg 3.9.2012, 07:15 | ||||||
ну и зачем этот, к тому же "вложенный" велосипед? stl и так перехватывает свой собственный SEH (__try-__except), генерируя c++ исключение (try-catch) со своим классом, какой смысл после этого переводить все обратно в SEH? ну поставь просто свой собственный SEH в майн'е и фантазируй дальше как душе угодно
опять сомнительный велосипед
как-то условия не очень вяжутся с определением если не хочется трогать ПО, в плане диагностики, как ты эту диагностику проведешь-то? разворачивая стек? а смысл тогда в "номер строки где возникла логическая ошибка" и т.п.? ты же не имеешь эту информацию в произвольном участке кода, где может возникнуть исключение ну и смотри тогда простой дамп исключения, которое и так винда тебе генерирует и предлагает отослать (баг-репорт), там уже это все развернуто и городить огород не нужно или просто поставь Unhandled-фильтр и генерируй свой репорт с версией ПО, адресом и кодом исключения Добавлено через 7 минут и 22 секунды кстати, это скорее C++-исключения, а не STL |
| Автор: BearFear 3.9.2012, 10:50 |
| Да, придется отказаться от вложения исключения. Я толком не смог разобраться почему такое происходит, но после С++ отката аргументы для RaiseException перезаписываются, разумеется в обработчик исключений попадает черт пойми что. В принципе, для исправления ситуации надо пару трок поменять. Убрать RaisedException из catch блоков и добавить туда код логирования. Опять таки, это только для main, скажем так, на крайний случай. Далее я определил макрос critical_error который переопределяет вызов throw с указанием модуля и строки + еще добавил комментарий. Выглядит так critical_error("comment"); На выходе уже идет "source.cpp:45 comment". Массив char использовал что бы при отладке не ковыряться в куче подвызовов string объекта отказываться от try-catch я не собираюсь, опять таки, это только самый низкий уровень который выше системного __try __catch. В коде shelled_main и выше будут простые try-catch с возможностью отката неправильных объектов (без перегруженных операторов new) и прочих прелестей. Просто выгоднее и удобнее как для обработки так и для генерации, использовать один алгоритм логирования, в котором будет указано время, адрес, код. аргументы исключения. Стек вызовов в той же Olly отображается, генерить его локально в лог смысла нет. Достаточно знать адрес исключения (начало инструкции до исключения) что бы поставить брейкпоинт там и соблюдая все условия слета, смоделировать ситуацию. Вот. Спасибо ребят, очень важно было мнение со стороны, хоть какое нибудь |
| Автор: BearFear 3.9.2012, 21:26 |
| Да, в общем так и сделал. Сначала долго проверял, может быть созданный в try секции shell_exception объект затирается до того как его используют коды в catch по ссылке... но все не то. В общем, теперь добавился объект report, который работает с файлом лога (что бы не писать повторяющийся код вывода в лог). Собстно он весьма гуманно реагирует на запросы о выводе строк, достаточно всего лишь вызвать соответствующий из трех методов для вывода: А) контекста Б) текстового описания ошибки для stl или пользовательского throw В) событие (дружелюбное описание события с выводом циферок и прочего в блок комментариев в логе. Позволяет более ясно видеть картину в соотношении со стеком вызовов). Всем спасибо, жалко что не удалось разобраться с обобщением логирования, но и то хорошо. Как нибудь все таки это дело расковыряю, как с асмом подружусь окончательно. Всему свое время. |