Структурированная обработка ошибок
Централизованная обработка исключений Беляев Алексей Когда приложение перестает работать в офисе у разработчика, найти ошибку и исправить ее не составляет труда. Когда же приложение отказывает у клиента, то трудно найти общий язык с огорченным пользователем, и понять что ты сделал не так... Введение Что такое ошибка? Отвечая на этот вопрос кратко, можно сказать, что ошибка – это отклонение от описанного поведения. Для разработчика это означает, что необходимо искать и исправлять причину этого отклонения. Для программиста контроля качества ПО это означает, что необходимо доработать тесты и включить их в базовый цикл тестирования приложения. Для руководства это означает увеличение времени и затрат на разработку продукта. Ошибки бывают разные, одни воспроизводятся легко, другие трудно. На поиск одних тратится немного времени, на поиск других уходят дни. Основная проблема поиска ошибки зачастую связана с недостатком информации по ее воспроизведению или состоянии приложения в момент возникновения ошибки. Если бы разработчик имел информацию о том, какая строчка программы содержит ошибку, ему не составило бы труда исправить ее. Единственный способ избежать ошибок в программах – это писать код без ошибок. Но человек не может не делать ошибок, поэтому в любой программе они есть. Единственное, что разработчик может постараться сделать – это минимизировать количество ошибок, а также облегчить их поиск и исправление. В этой статье рассматривается способ, помогающий ускорить поиск и исправление ошибок. Windows и необработанные исключения Когда в приложении, работающем под управлением ОС Windows (от 9х до ХР), возникает необработанное исключение, операционная система обрабатывает его, создает dump-файл и записывает в него информацию, анализируя которую можно восстановить состояние приложения и быстро найти ошибку. К информации, которую сохраняет операционная система, относится:
информация о потоках; информация о загруженных модулях; информация об исключении; информация о системе; информация о памяти. Всю собранную информацию операционная система предлагает послать в Microsoft для последующего анализа. Учитывая полезность этой информации и выгоду, которую предоставляет владение ей, необходимо на этапе проектирования приложения заложить возможность для сбора информации о приложении во время возникновения исключения для последующего ее анализа. Тогда разработчики будут иметь больше информации для поиска и исправления ошибок. Каким образом Windows XP определяет, что в приложении произошло необработанное исключение? Ответить на этот вопрос можно, если разобраться с механизмом структурированной обработки исключений (SEH). Все версии Windows, начиная с версии Windows 95 и Windows NT, поддерживают этот механизм обработки исключений, позволяющий операционной системе и приложению тесно взаимодействовать в случае возникновения исключительной ситуации. И если в каком-либо приложении возникает необработанное исключение, операционная система обрабатывает его и завершает приложение. Структурированная обработка ошибок Структурированная обработка исключений (SEH) – это предоставляемый системой сервис, вокруг которого библиотеки современных языков программирования реализуют свои собственные функции для работы с исключениями. C++-программисты наверняка знакомы с SEH в основном по использованию конструкций __try... __except. Встретив в теле функции конструкцию __try … __except, компилятор, поддерживающий SEH, генерирует код для регистрации обработчика исключения. Затем, после возникновения исключения, операционная система ищет подходящий обработчик. Если подходящий обработчик не найден, операционная система создает dump-файл и завершает работу приложения.
Таким образом, перед нами стоит задача – сделать так, чтобы после возникновения в приложении необработанного исключения вызывался наш обработчик. Для решения этой задачи необходимо выяснить, как операционная система ищет обработчик исключения. В поисках ответа на этот вопрос я углублялся в документацию по механизму структурированных исключений, анализировал системный ассемблерный код, смотрел, что генерирует компилятор, когда встречает конструкцию __try … __except, но подходящего решения не находилось. Ни в SDK, ни в DDK я не нашел ничего, что могло бы ответить на этот вопрос. Анализируя код, генерируемый компилятором для конструкции __try … __except, я увидел, что для каждого нового обработчика исключений в стеке создается запись, которая помещается в связанный список. Вот пример простой функции, который поможет понятнее объяснить это:
Код, который был сгенерирован компилятором VC 7.0:
Теперь давайте посмотрим, что делает этот ассемблерный код. В начале функции создается фрейм стека. В этом нет ничего необычного – такой код есть в большинстве функций, но вот несколько следующих инструкций показались мне очень интересными:
Вначале в стек кладется -1 (как оказалось впоследствии, просто резервируется место в стеке), а затем в стек записывается адрес статической переменной и адрес обработчика исключения. Если присмотреться к последним трем инструкциям, то можно увидеть, что из памяти по адресу fs:[0] считывается какое-то число, и кладется в стек, а на его место заносится текущий указатель стека. В принципе ничего подозрительного тут нет, но если расположить эти три инструкции последовательно несколько раз, то станет заметно, что они формируют связанный список, причем первый элемент этого списка всегда указывает на предыдущий элемент. На выходе из функции находится код, который восстанавливает предыдущее значение переменной по адресу fs:[0]:
Таким образом, если функция имеет в себе конструкцию __try … __except, то компилятор создает в стеке запись о новом обработчике исключений и помещает информацию о ней в список обработчиков. Придя к такому выводу, я начал искать хоть какую-то информацию об обработчиках исключений и нашел публикацию, написанную Matt Pietrek-ом 7 лет назад (A Crash Course on the Depths of Win32 Structured Exception Handling). В этой статье описана структура SEH, и подтверждаются выводы, сделанные путем анализа кода приведенной выше функции. Изучив эту статью и проверив написанное в ней, я обнаружил, что с тех пор в области обработки исключений практически ничего не изменилось. Из статьи следует, что по адресу fs:[0], находится начало связанного списка зарегистрированных обработчиков исключения, элементами которого являются структуры типа _EXCEPTION_REGISTRATION, расположенные в стеке.
В этой структуре handler является процедурой обработки исключения. Прототип этой функции приведен ниже:
Как видите, функция обработчика исключения принимает 4 параметра. Первый параметр имеет тип PEXCEPTION_RECORD – это указатель на структуру, содержащую информацию об исключении. Вы можете найти объявление этой структуры в заголовочном файле winnt.h:
Описание наиболее значимых полей этой структуры приведено ниже: ExceptionCode – тип исключения. ExceptionFlags – флаг исключения. ExceptionAddress – адрес участка кода, где возникло исключение. Второй параметр функции содержит в себе указатель на структуру PEXCEPTION_REGISTRATION. Ее описание и назначение было приведено выше. Третий параметр указывает на переменную типа PCONTEXT и несет информацию о состоянии регистров во время исключения. Таким образом, механизм обработки исключений становится более или менее ясным, т.е. когда в приложении возникает исключение, операционная система берет текущий указатель по адресу fs:[0] и просматривает список обработчиков в поисках нужного обработчика исключения. Если обработчик найден, она передает ему управление. В противном случае операционная система выполняет свой обработчик, который вызывает функцию UnhandledExceptionFilter. Значит, для получения управления в случае возникновения необработанного исключения, нужно зарегистрировать свой обработчик и расположить его в вершине этого списка. Но мир программирования не был бы таким интересным, если бы все было так просто! Давайте пройдем дальше и посмотрим, что происходит во время старта приложения, и какую роль в обработке исключений играет runtime-библиотека.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|