Средства тестирования приложений для разработчиков

       

Purfy


Начать описание возможностей продукта Rational Purify хочется перефразированием одного очень известного изречения: "с точностью до миллиБАЙТА". Данное сравнение не случайно, ведь именно этот продукт направлен на разрешение всех проблем, связанных с утечками памяти. Ни для кого не секрет, что многие программные продукты ведут себя "не слишком скромно", замыкая на себя во время работы все системные ресурсы без большой на то необходимости. Подобная ситуация может возникнуть вследствие нежелания программистов доводить созданный код "до ума", но чаще подобное происходит не из-за лени, а из-за невнимательности. Это понятно - современные темпы разработки ПО в условиях жесточайшего прессинга со стороны конкурентов не позволяют уделять слишком много времени оптимизации кода, ведь для этого необходимы и высокая квалификация, и наличие достаточного количества ресурсов проектного времени. Как мне видится, имея в своем распоряжении надежный инструмент, который бы сам в процессе работы над проектом указывал на все черные дыры в использовании памяти, разработчики начали бы его повсеместное внедрение, повысив надежность создаваемого ПО

В общих чертах работа программы сводится к детальному выводу статистики об использовании памяти приложением. Данных в выводимой статистике вполне достаточно для получения общей, а затем - и детальной информации обо всем, что имеет отношение к памяти: утечки, потерянные блоки, фиктивные ссылки.

Purify позволяет анализировать исполняемый модуль, содержащий отладочную информацию, либо работать на уровне исходников, но только в среде Visual C++.

Работа программы традиционно начинается со сбора информации о загружаемых библиотеках, любых внешних модулях. Так же, как и в Quantify, сначала идет детальный сбор информации и анализ загружаемых библиотек, затем анализ плавно перетекает на исполняемый модуль. В зависимости от производительности и количества оперативной памяти данный процесс может очень надолго затянуться:

Поскольку Purify тесно интегрируется с Microsoft Development Studio, мне хочется построить дальнейшее изложение материала в перспективе интеграции двух продуктов.

Интеграция компонентов от Rational выражается в появлении новых инструментальных панелей на поверхности рабочего стола в Development Studio.
Получив полный набор тестирующих и профилирующих средств, разработчик обращается к ним по мере необходимости, не покидая рабочего пространства, что позволяет сэкономить массу времени на различные вызовы сторонних программ.

Рисунок 8 Рисунок 8 показывает примерный вид инструментальных панелей в Visual Studio, появляющихся после инсталляции Purify, Quantify, PureCoverage. А учитывая давнюю дружбу Rational и Microsoft, становится понятно, почему поддерживаются в полной мере только среды разработчиков от MS. Собрав воедино всю перечисленную информацию, давайте попробуем на конкретном примере в Visual Studio, создать приложение, внести в него ряд намеренных ошибок после чего, используя Purify, отыскать их. Опустим за скобки то, каким образом создаются проекты в Visual Studio и из чего они состоят - это не самоцель данной статьи. Считаем, что читатель уже работал с данной средой, или хотя бы знаком с ней - это - во-первых. Во-вторых, попробуем внести некоторый зловредный код на С++ (в стандартный шаблон, генерируемый Visual Studio при использовании "мастера"), с преднамеренным вызовом функции выделения блока памяти. Для детального тестирования нам необходимо будет дважды исполнить одну и ту же программу: в одном случае со "зловредным" кодом, а другой раз без него (в качестве "зловредного" кода я подразумеваю безвозвратное выделение блока памяти), а в другом: все тоже самое, только с возвратом блока системе)
Рисунок 9 Итак, для дальнейшей демонстрации возможностей я подготовил проект при помощи "волшебника", создающего 32-разрядное приложение для Windows. Имя проекту дал "PROJECTOID". На рисунке 9 изображен скриншот окна Workspace после создания проекта. Для демонстрации преимуществ Purify не нужно заводить в примере тонны сложных классов, запутывая и себя, и программу, и статью: ограничимся лишь простыми вызовами на распределение памяти Для более наглядного способа отлова ошибок допишем пару строк в стандартный обработчик "OnAppAbout": void All::OnAppAbout() { char *alex; //Наша строка №1 alex=(char *)malloc(20000); //Наша строка №2 CAboutDlg aboutDlg; aboutDlg.DoModal(); } Добавление интеллекта к функции OnAppAbout сделано намеренно, поскольку во время работы можно воспользоваться данной функцией несколько раз подряд, активируя диалог "ABOUT" после игр с его вызовом.




Теперь завершим приложение, посмотрим статистику по памяти и под конец найдем "виновного" в полученной утечке памяти. Из фрагмента видно, что указатель "alex" смотрит в сторону блока длиной в 20Кб, который выделяется функцией MALLOC. Еще можно заметить, что:
  1. указатель нигде не используется;
  2. Блок памяти не освобождается.
Рисунок 10 Запускаем программу по F5, предварительно активировав Purify (увеличительные стекла на инструментальной панели Purify. См. рис. 8). В запущенном приложении трижды запускаем диалог ABOUT из верхнего меню и закрываем приложение. Во время работы подконтрольного приложения Purify собрала достаточно информации о нем и о его проблемах с памятью. Полученная статистика выведется на экран сразу по окончании работы приложения.
Рисунок 11 Рисунок 10 иллюстрирует вид окна со статистикой по памяти. При внимательном рассмотрении становится видна вся подноготная как нашего модуля, так и шапки, сгенерированной компилятором Microsoft в лице Visual C++. Purify насчитала 43 (!) предупреждения о неправильном (читай: неэффективном) использовании памяти, а из них только одно наше было преднамеренно введено в программу. Что же, теперь понятно, почему все продукты от MS так охочи до памяти!
Рисунок 12 Вновь обратимся к рисунку со статистикой, где в явном виде находится информация по ошибкам и по модулям, в которых эти ошибки были обнаружены. К приятной неожиданности можно отнести фразу "Memory leak of 60000", указывающую на то, сколько фактических байтов памяти программа не вернула системе по завершении своей работы. Эта черта разительно отличает подходы к тестированию программы Rational Purify от подобных продуктов конкурирующих компаний, которые высчитывают не фактические утечки (полученные в результате нескольких вызовов при реальной работе приложения, а количество невозвращенных блоков, то есть ограничиваются лишь анализом (на уровне исходных текстов) программы с выявлением вызовов функций аллокирования памяти без последующего освобождения.


Из этого и следует полученное число 60000 - фактически не освобожденных блоков (3 по 20000). После добавления функции free(alex) в конец обработчика OnAppAbout и перекомпиляции тестируемого приложения, Purify не обнаруживает никаких ошибок памяти, что и являлось нашей целью. Все вышеописанные возможности дают мощный инструментарий в руки разработчика, желающего знать, где в коде находится вызов на выделение памяти, сколько физически утеряно блоков в результате работы приложения и какая часть программы в этом виновата. Еще очень важным моментом работы Purify является возможность включения опции остановки на ошибке в подопытном приложении: так, если в приложении есть Runtime-ошибка, то повстречавшись с ней оно (приложение), не "вылетит" по дампу и не позовет "доктора Ватсона", а просто завершится с передачей управления Purify, который и сообщит программисту о том, где произошел сбой, по каким причинам и сколько при этом было использовано памяти. Описанные возможности продукта Rational Purify - основные, но не единственные. Просто рамки статьи не позволяют сконцентрироваться на всех мелочах и тонкостях, присущих данному продукту, что, впрочем, и неважно, так как для этого есть руководство пользователя, где по пунктам расписаны все супервозможности продукта. По сложившейся традиции, подведем итоги статьи описанием основных возможностей Purify:
  1. Отслеживание ошибок доступа к памяти
  2. Сбор и вывод статистики по использованию памяти
  3. Использование комплексного подхода к тщательному тестированию
  4. Технология OCI - Object Code Insertion позволяет детально отследить и выловить ошибку не только в контролируемом модуле, но и в модулях DLL сторонних разработчиков
  5. Тестирование ActiveX, COM/DCOM, ODBC, DLL
  6. Настраиваемый, двухуровневый способ тестирования (придирчивости) приложений
  7. Отслеживание API вызовов
  8. Интеграция с Visual Studio
| |  

Содержание раздела